简体   繁体   English

使用通知实时从NSTask获取数据不起作用

[英]Getting data from NSTask in real-time using notifications doesn't work

I got a Cocoa command-line program in which I try to run NSTask program ( tshark to monitor network) and get data from it in real-time. 我有一个Cocoa命令行程序,我尝试运行NSTask程序( tshark来监控网络)并从中实时获取数据。 So I make a NSFileHandle , call waitForDataInBackgroundAndNotify to send notifications and then register my help class to Notification center to process the data, but not a single notification is sent to my help class. 所以我创建了一个NSFileHandle,调用waitForDataInBackgroundAndNotify发送通知,然后将我的帮助类注册到Notification Center来处理数据,但没有一个通知发送到我的帮助类。

Does anybody have an idea of what could be wrong? 有人知道可能出现什么问题吗?

Thanks in advance 提前致谢

Here is my code: 这是我的代码:

#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: When I uncomment commented section of code and delete all that notification stuff, all desired data are extracted from file handle after task finish. 编辑:当我取消注释已注释的代码段并删除所有通知内容时,在任务完成后从文件句柄中提取所有所需数据。

I was also wondering, if problem can't be in fact, that my program is 'Command line tool' so I am not sure if it has run loop - as Apple documentation says is needed (in waitForDataInBackgroundAndNotify message of NSFileHandle): 我也想知道,如果问题不能实际上,我的程序是'命令行工具'所以我不确定它是否已经运行循环 - 正如Apple文档所说的那样(在NSFileHandle的waitForDataInBackgroundAndNotify消息中):

You must call this method from a thread that has an active run loop. 您必须从具有活动运行循环的线程调用此方法。

There's a new API since 10.7, so you can avoid using NSNotifications. 自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];
}];

Probably you want to repeat the same above for task.standardError . 可能你想为task.standardError重复上面的相同task.standardError

IMPORTANT: 重要:

When your task terminates, you have to set readabilityHandler block to nil; 当您的任务终止时,您必须将readabilityHandler块设置为nil; otherwise, you'll encounter high CPU usage, as the reading will never stop. 否则,您将遇到高CPU使用率,因为读数永远不会停止。

[task setTerminationHandler:^(NSTask *task) {

    // do your stuff on completion

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

This is all asynchronous (and you should do it async), so your method should have a ^completion block. 这都是异步的(你应该做异步),所以你的方法应该有一个^完成块。

Your program finishes immediately after launching the task. 启动任务后,程序立即完成。 You need to tell the task to wait until exit . 你需要告诉任务要等到退出 That will run the run loop, waiting for the child process to exit and coincidentally enabling the file handle to monitor the pipe. 这将运行run循环,等待子进程退出并巧合地启用文件句柄来监视管道。 When the task does exit, the method will return and you can proceed to the end of main . 当任务退出时,该方法将返回,您可以继续执行main的结尾。

看一下asynctask.m ,示例代码,演示如何使用NSTask处理数据的异步stdin,stdout和stderr流(尽管可能还有改进的余地)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM