7

Mam program wiersza poleceń Cocoa, w którym próbuję uruchomić program NSTask (tshark, aby monitorować sieć) i pobierać z niego dane w czasie rzeczywistym. Zrobiłem więc NSFileHandle , zadzwonić pod numer waitForDataInBackgroundAndNotify, aby wysłać powiadomienia, a następnie zarejestrować moją klasę pomocy w centrum powiadomień, aby przetworzyć dane, ale żadne powiadomienie nie zostanie wysłane do mojej klasy pomocy.Pobieranie danych z NSTask w czasie rzeczywistym za pomocą powiadomień nie działa

Czy ktoś ma pojęcie, co może być nie tak?

góry dzięki

Oto mój kod:

#import <Foundation/Foundation.h> 
#import <string> 
#import <iostream> 

@interface toff : NSObject {} 
-(void) process:(NSNotification*)notification; 
@end 

@implementation toff 
-(void) process:(NSNotification*)notification{ 
    printf("Packet caught!\n"); 
} 
@end 

int main (int argc, const char * argv[]){ 
    @autoreleasepool { 
     NSTask* tshark = [[NSTask alloc] init]; 
     NSPipe* p = [NSPipe pipe]; 
     NSFileHandle* read = [p fileHandleForReading]; 
     toff* t1 = [[toff alloc] init]; 
     NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; 

     [read waitForDataInBackgroundAndNotify]; 
     [nc addObserver:t1 selector:@selector(process:) name:nil object:nil]; 

     printf("Type 'stop' to stop monitoring network traffic.\n"); 
     [tshark setLaunchPath:@"/usr/local/bin/tshark"]; 
     [tshark setStandardOutput:p]; 
     [tshark launch]; 

     while(1){ 
      std::string buffer; 
      getline(std::cin, buffer); 
      if(buffer.empty()) continue; 
      else if(buffer.compare("stop") == 0){ 
       [tshark interrupt]; 
       break; 
      } 
     } 

     //NSData* dataRead = [read readDataToEndOfFile]; 
     //NSLog(@"Data: %@", dataRead); 
     //NSString* stringRead = [[NSString alloc] initWithData:dataRead encoding:NSUTF8StringEncoding]; 
     //NSLog(@"Output: %@", stringRead); 

    } 
    return 0; 
} 

EDIT: Kiedy Odkomentuj skomentował fragment kodu i usunąć wszystkie tego powiadomienia rzeczy, wszystkie żądane dane są pobierane z uchwytu pliku po zakończenie zadania.

Zastanawiam się również, czy problem nie może być w rzeczywistości, że mój program jest "Narzędzie wiersza poleceń", więc nie jestem pewien, czy uruchomiono pętlę - jak mówi dokumentacja Apple jest potrzebna (w waitForDataInBackgroundAndNotify wiadomość NSFileHandle):

Musisz wywołać tę metodę z wątku, który ma aktywną pętlę uruchamiania.

+0

Naprawdę powinieneś zrobić bardziej konkretny komunikat 'addObserver: selektor: name: object:'. Aktualnie logujesz się do * dowolnego * powiadomienia, a nie tylko do powiadomienia "NSFileHandleReadCompletionNotification". –

+0

Pętla uruchamiania jest niejawnie tworzona w razie potrzeby, więc można bezpiecznie założyć, że "ma [a] uruchamianą pętlę", ale trzeba ją uruchomić. Aplikacja uruchomiłaby pętlę uruchamiania, więc wszystko, co musisz zrobić, to zwrócić, ale w narzędziu wiersza poleceń musisz uruchomić pętlę uruchamiania samodzielnie. Zalecam używanie NSFileHandle do odczytu ze standardowego wejścia, i robienie tego, co powiedziałem w mojej odpowiedzi, aby uruchomić pętlę uruchamiania, dopóki zadanie nie zostanie zakończone. –

+0

@PeterHosey Dobry pomysł - dziękuję. Próbowałem to zrobić, ale nie działało. Więc cofnąłem się do prostego, pominiętego zatrzymania i wpadłem na to (http://pastebin.com/qnhaUAjp) (zauważyłem również, że dane przekazywane przez 'NSFileHandle' są w jakiś sposób buforowane - nie wiem jak się pozbyć) Po uruchomieniu czeka chwilę, a następnie zapisuje "Pakiety złapane" raz - potem nic. Jeśli odkomentuję 1, to czeka, a następnie napisz "Pakiet złapany" nieograniczoną ilość razy.I wreszcie, gdy próbuję wyodrębnić dane i odkomentować _2 -5_ czeka chwilę, pisze linie _2 i 3_, a następnie zawiesza się. Nie ma awarii, nie ma błędu, po prostu nie wyodrębniaj danych. – user1023979

Odpowiedz

1

Twój program kończy się natychmiast po uruchomieniu zadania. Musisz powiedzieć zadanie: wait until exit. To uruchomi pętlę uruchamiania, czekając na zakończenie procesu potomnego i przypadkowe włączenie uchwytu pliku do monitorowania potoku. Kiedy zadanie zostanie zakończone, metoda powróci i możesz przejść do końca main.

+0

Powinienem opublikować cały kod. Edytowałem swoją odpowiedź - teraz widzisz, że daję użytkownikowi możliwość ręcznego zatrzymania zadania z mojego programu. Myślę, że gdybym zadzwonił "waitUntilExit", nie byłoby to możliwe. Próbowałem również wyodrębnić dane z obsługi plików po zatrzymaniu zadania (sekcja komentarzy) i działało idealnie. – user1023979

+0

Kiedy mówię "edytowałem swoją odpowiedź" mam na myśli "zredagowałem moje pytanie" - przepraszam – user1023979

0

Zapoznaj się z asynctask.m, przykładowy kod, który pokazuje, jak zaimplementować asynchroniczne stdin, stdout & strumienie stderr do przetwarzania danych z NSTask (może być jednak miejsce na poprawę).

8

Istnieje nowy interfejs API od 10,7, więc można uniknąć używania NSNotifications.

task.standardOutput = [NSPipe pipe]; 
[[task.standardOutput fileHandleForReading] setReadabilityHandler:^(NSFileHandle *file) { 
    NSData *data = [file availableData]; // this will read to EOF, so call only once 
    NSLog(@"Task output! %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); 

    // if you're collecting the whole output of a task, you may store it on a property 
    [self.taskOutput appendData:data]; 
}]; 

Prawdopodobnie chcesz powtórzyć to samo powyżej dla task.standardError.

WAŻNE:

Gdy zadanie kończy, trzeba ustawić readabilityHandler blok do zera; w przeciwnym razie wystąpi duże obciążenie procesora, ponieważ odczyt nigdy się nie zatrzyma.

[task setTerminationHandler:^(NSTask *task) { 

    // do your stuff on completion 

    [task.standardOutput fileHandleForReading].readabilityHandler = nil; 
    [task.standardError fileHandleForReading].readabilityHandler = nil; 
}]; 

To wszystko jest asynchroniczne (i powinieneś zrobić to asynchronicznie), więc twoja metoda powinna mieć blok zakończenia ^.