7

Ho ricevuto un programma da riga di comando Cocoa nel quale provo a eseguire il programma NSTask (tshark per monitorare la rete) e ottenere i dati da esso in tempo reale. Così faccio un NSFileHandle , chiama waitForDataInBackgroundAndNotify per inviare notifiche e poi registro la mia classe di aiuto al Centro di notifica per elaborare i dati, ma non una singola notifica viene inviata alla mia classe di aiuto.Ottenere dati da NSTask in tempo reale usando le notifiche non funziona

Qualcuno ha un'idea di cosa potrebbe essere sbagliato?

Grazie in anticipo

Ecco il mio codice:

#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: Quando ho Rimuovere il commento commentato sezione di codice e cancellare tutta quella roba la notifica, tutti i dati desiderati vengono estratti dai file handle dopo fine compito.

Mi chiedevo anche, se il problema non può essere in realtà, che il mio programma è 'strumento di riga di comando', quindi non sono sicuro se ha eseguito il ciclo - come dice la documentazione di Apple è necessario (nel messaggio WaitForDataInBackgroundAndNotify di NSFileHandle):

È necessario chiamare questo metodo da un thread che ha un ciclo di esecuzione attivo.

+0

Si dovrebbe davvero fare che il messaggio "addObserver: selector: nome: oggetto:' più specifico. Attualmente stai registrando per * qualsiasi * notifica, non solo la notifica 'NSFileHandleReadCompletionNotification'. –

+0

Il ciclo di esecuzione viene creato in modo implicito quando necessario, quindi è possibile ipotizzare "ha [un] ciclo di esecuzione", ma è necessario eseguirlo. Un'applicazione eseguirà il ciclo di esecuzione per te, quindi tutto ciò che devi fare è tornare, ma in uno strumento da riga di comando, devi eseguire il ciclo di esecuzione da solo. Raccomando di usare NSFileHandle per leggere anche dall'input standard e fare ciò che ho detto nella mia risposta per eseguire il ciclo di esecuzione fino all'uscita dall'attività. –

+0

@PeterHosey Buona idea - grazie.ho provato a farlo ma non ha funzionato. Così ho fatto un passo indietro verso il semplice, omesso arresto e ho trovato [questo] (http://pastebin.com/qnhaUAjp) (ho anche notato che i dati trasmessi da 'NSFileHandle' sono in qualche modo bufferizzati - non so come sbarazzarsi di esso) Dopo l'avvio, attende un po 'e poi scrive' Packet caught 'una volta - quindi niente. Se ho decommentato 1, allora attende e scrive' Packet caught 'volte illimitate. E infine quando provo ad estrarre i dati e il commento _2 -5_ aspetta un po ', scrive le righe _2 e 3_ e poi si blocca. Nessun incidente, nessun errore, solo non estrarre i dati. – user1023979

risposta

1

Il programma termina immediatamente dopo l'avvio dell'attività. È necessario indicare l'attività a wait until exit. Ciò eseguirà il ciclo di esecuzione, in attesa che il processo figlio esca e per coincidenza consenta all'handle del file di monitorare la pipe. Quando l'attività si chiude, il metodo ritorna e puoi procedere fino alla fine di main.

+0

Dovrei postare tutto il codice. Ho modificato la mia risposta: ora puoi vedere che sto dando all'utente la possibilità di interrompere manualmente l'attività dal mio programma. Penso che se chiamassi 'waitUntilExit' non sarebbe possibile. Ho anche provato ad estrarre i dati dal gestore di file dopo aver fermato l'attività (sezione commentata) e ha funzionato perfettamente. – user1023979

+0

E quando dico 'ho modificato la mia risposta' intendo 'ho modificato la mia domanda' - scusa – user1023979

0

Dai uno sguardo allo asynctask.m, codice di esempio che mostra come implementare lo stdin asincrono, stdout stderr per lo sviluppo dei dati con NSTask (ci possono essere comunque margini di miglioramento).

8

C'è una nuova API dal 10.7, quindi è possibile evitare l'uso di 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]; 
}]; 

Probabilmente si desidera ripetere la stessa sopra per task.standardError.

IMPORTANTE:

Quando il vostro compito termina, è necessario impostare blocco readabilityHandler a zero; altrimenti, incontrerai un elevato utilizzo della CPU, poiché la lettura non si fermerà mai.

[task setTerminationHandler:^(NSTask *task) { 

    // do your stuff on completion 

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

Questo è tutto asincrono (e si dovrebbe farlo asincrona), in modo che il metodo deve avere un blocco di completamento ^.