7

Recibí un programa de línea de comandos Cocoa en el que trato de ejecutar el programa NSTask (tshark para monitorear la red) y obtengo datos en tiempo real. Así que hago un NSFileHandle , llamo al waitForDataInBackgroundAndNotify para enviar notificaciones y luego registrar mi clase de ayuda al centro de notificaciones para procesar los datos, pero no se envía una sola notificación a mi clase de ayuda.Obtener datos de NSTask en tiempo real mediante notificaciones no funciona

¿Alguien tiene una idea de lo que podría estar mal?

Gracias de antemano

Aquí está mi código:

#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: Cuando Descomentar comento sección de código y borrar todas esas cosas notificación, todos los datos deseados se extraen de identificador de archivo después de tarea finalizada

También me preguntaba, si el problema no puede ser de hecho, que mi programa es 'Herramienta de línea de comandos', así que no estoy seguro de si se ha ejecutado, como dice la documentación de Apple (en waitForDataInBackgroundAndNotify mensaje de NSFileHandle):

Debe llamar a este método desde una secuencia que tenga un ciclo de ejecución activo.

+0

Debería hacer ese mensaje 'addObserver: selector: name: object:' más específico. Actualmente estás registrando la notificación * any *, no solo la notificación 'NSFileHandleReadCompletionNotification'. –

+0

El ciclo de ejecución se crea implícitamente cuando es necesario, por lo que puede suponer con seguridad que "tiene [un] ciclo de ejecución", pero necesita ejecutarlo. Una aplicación ejecutaría el ciclo de ejecución por usted, por lo que todo lo que tendría que hacer es devolver, pero en una herramienta de línea de comandos, debe ejecutar el ciclo de ejecución usted mismo. Recomiendo usar NSFileHandle para leer desde la entrada estándar también, y hacer lo que dije en mi respuesta para ejecutar el ciclo de ejecución hasta que la tarea finalice. –

+0

@PeterHosey Buena idea, gracias. Intenté hacer esto pero no funcionó. Así que volví a la parada simple, omitida, y se me ocurrió [esto] (http://pastebin.com/qnhaUAjp) (también noté que los datos pasados ​​por 'NSFileHandle' son amortiguados de alguna manera - no sé cómo deshacerse de ella) Después de comenzar, espera un rato y luego escribe 'Packet caught' una vez, luego nada. Si descomiento 1, espera y luego escribe 'Packet caught' ilimitado. Y finalmente cuando intento extraer datos y descomentar _2 -5_ espera un tiempo, escribe las líneas _2 y 3_ y luego se congela. Sin bloqueo, sin error, simplemente no extraiga los datos. – user1023979

Respuesta

1

Su programa finaliza inmediatamente después de iniciar la tarea. Debe indicar la tarea al wait until exit. Eso ejecutará el ciclo de ejecución, esperando que el proceso hijo salga y coincidentemente habilite el manejador de archivo para monitorear el conducto. Cuando la tarea sale, el método volverá y puede continuar hasta el final de main.

+0

Debería publicar todo el código. Edité mi respuesta, ahora puede ver que le doy la posibilidad al usuario de detener manualmente la tarea desde mi programa. Creo que si llamara 'waitUntilExit' no sería posible. También traté de extraer datos del manejador de archivos después de detener la tarea (sección comentada) y funcionó perfectamente. – user1023979

+0

Y cuando digo "he editado mi respuesta" me refiero a "he editado mi pregunta", lo siento – user1023979

0

Eche un vistazo a asynctask.m, muestra el código que muestra cómo implementar Stdin asíncrono, stdout & corrientes stderr para procesar datos con NSTask (aunque puede haber margen de mejora).

8

Hay una nueva API desde 10.7, por lo que puede evitar el uso de 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]; 
}]; 

Probablemente desea repetir la misma por encima de task.standardError.

IMPORTANTE:

Cuando su tarea termina, usted tiene que fijar el bloque readabilityHandler a cero; de lo contrario, encontrará un uso elevado de la CPU, ya que la lectura nunca se detendrá.

[task setTerminationHandler:^(NSTask *task) { 

    // do your stuff on completion 

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

Esto es todo asíncrono (y debe hacerlo asíncrono), por lo que su método debe tener un bloque finalización ^.

Cuestiones relacionadas