Получение данных из NSTask в режиме реального времени с использованием уведомлений не работает - PullRequest
9 голосов
/ 20 января 2012

Я получил программу командной строки Cocoa, в которой я пытаюсь запустить программу NSTask (tshark для мониторинга сети) и получать данные из нее в режиме реального времени. Поэтому я делаю NSFileHandle позвоните по номеру waitForDataInBackgroundAndNotify, чтобы отправить уведомления, а затем зарегистрируйте мой класс справки в Центре уведомлений для обработки данных, но в мой класс справки не отправлено ни одного уведомления.

Кто-нибудь имеет представление о том, что может быть не так?

Заранее спасибо

Вот мой код:

#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;
}

РЕДАКТИРОВАТЬ: Когда я раскомментирую закомментированный раздел кода и удаляю все эти уведомления, все нужные данные извлекаются из дескриптора файла после завершения задачи.

Мне также было интересно, если проблема не может быть на самом деле, что моя программа является «инструментом командной строки», поэтому я не уверен, что она имеет цикл выполнения - как говорится в документации Apple (в сообщении waitForDataInBackgroundAndNotify NSFileHandle) :

Вы должны вызывать этот метод из потока с активным циклом выполнения.

Ответы [ 3 ]

10 голосов
/ 29 апреля 2013

С 10.7 появился новый API, поэтому вы можете избежать использования 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];
}];

Возможно, вы хотите повторить то же самое выше для task.standardError.

ВАЖНО:

Когда ваша задача завершается, вы должны установить для блока readabilityHandler значение nil;в противном случае вы столкнетесь с высокой загрузкой ЦП, поскольку чтение никогда не прекратится.

[task setTerminationHandler:^(NSTask *task) {

    // do your stuff on completion

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

Это все асинхронно (и вы должны делать это асинхронно), поэтому у вашего метода должен быть блок завершения ^.1012 *

1 голос
/ 21 января 2012

Ваша программа завершает работу сразу после запуска задачи. Вы должны указать заданию дождаться выхода . Это запустит цикл выполнения, ожидая завершения дочернего процесса и по совпадению позволяя дескриптору файла отслеживать канал. Когда задача завершится, метод вернется, и вы можете перейти к концу main.

0 голосов
/ 04 февраля 2012

Взгляните на asynctask.m , пример кода, который показывает, как реализовать асинхронные потоки stdin, stdout и stderr для обработки данных с помощью NSTask (хотя возможны улучшения).

...