Cocoa 使用通知从NSTask实时获取数据不会';行不通

Cocoa 使用通知从NSTask实时获取数据不会';行不通,cocoa,real-time,nsnotifications,nsnotificationcenter,nstask,Cocoa,Real Time,Nsnotifications,Nsnotificationcenter,Nstask,我得到了一个Cocoa命令行程序,在该程序中我尝试运行NSTask程序(tshark以监视网络),并从中实时获取数据。所以我做了一个 NSFileHandle ,调用waitfordatainbackground和notify发送通知,然后将我的帮助类注册到通知中心以处理数据,但没有一个通知发送到我的帮助类 有人知道什么地方可能出错吗 提前谢谢 这是我的密码: #import <Foundation/Foundation.h> #import <string> #impo

我得到了一个Cocoa命令行程序,在该程序中我尝试运行
NSTask
程序(
tshark
以监视网络),并从中实时获取数据。所以我做了一个 NSFileHandle ,调用
waitfordatainbackground和notify
发送通知,然后将我的帮助类注册到通知中心以处理数据,但没有一个通知发送到我的帮助类

有人知道什么地方可能出错吗

提前谢谢

这是我的密码:

#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;
}
#导入
#进口
#进口
@接口toff:NSObject{}
-(无效)流程:(NSNotification*)通知;
@结束
@实现toff
-(无效)流程:(NSNotification*)通知{
printf(“包被捕获!\n”);
}
@结束
int main(int argc,const char*argv[]{
@自动释放池{
NSTask*tshark=[[NSTask alloc]init];
NSPipe*p=[NSPipe pipe];
NSFileHandle*read=[p fileHandleForReading];
toff*t1=[[toff alloc]init];
NSNotificationCenter*nc=[NSNotificationCenter defaultCenter];
[阅读WaitForDataInBackground和Notify];
[nc addObserver:t1选择器:@selector(进程:)名称:nil对象:nil];
printf(“键入'stop'停止监视网络流量。\n”);
[tshark setLaunchPath:@/usr/local/bin/tshark];
[t共享设置标准输出:p];
[沙克发射];
而(1){
字符串缓冲区;
getline(标准::cin,缓冲区);
如果(buffer.empty())继续;
else if(buffer.compare(“stop”)==0){
[tshark中断];
打破
}
}
//NSData*dataRead=[read readDataToEndOfFile];
//NSLog(@“数据:%@”,数据读取);
//NSString*stringRead=[[NSString alloc]initWithData:dataRead编码:NSUTF8StringEncoding];
//NSLog(@“输出:%@”,stringRead);
}
返回0;
}
编辑:当我取消注释代码的注释部分并删除所有通知内容时,所有需要的数据都会在任务完成后从文件句柄中提取出来

我还想知道,如果问题不可能是真的,我的程序是“命令行工具”,所以我不确定它是否运行了loop——正如苹果文档所说,这是必需的(在WaitFordAtainBackground和NSFileHandle的Notify消息中):

必须从具有活动运行循环的线程调用此方法


您的程序在启动任务后立即完成。你需要告诉他这个任务。这将运行run循环,等待子进程退出,同时启用文件句柄来监视管道。当任务确实退出时,该方法将返回,您可以继续到
main

的末尾查看示例代码,该代码显示了如何使用NSTask实现异步stdin、stdout和stderr流来处理数据(尽管可能还有改进的余地)。

从10.7开始有一个新的API,因此,您可以避免使用NSN通知

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];
}];
对于
任务.standardError
,可能需要重复上述操作

重要:

当任务终止时,必须将readabilityHandler块设置为nil;否则,您将遇到高CPU使用率,因为读取将永远不会停止

[task setTerminationHandler:^(NSTask *task) {

    // do your stuff on completion

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

这都是异步的(您应该异步执行),因此您的方法应该有一个^completion块。

我应该发布整个代码。我编辑了我的答案-现在你可以看到,我给了用户从我的程序手动停止任务的可能性。我想如果我打电话给waitUntilExit,那是不可能的。在停止任务(注释部分)后,我还尝试从文件处理程序中提取数据,效果非常好。当我说“我编辑了我的答案”时,我的意思是“我编辑了我的问题”-对不起,你真的应该让
addObserver:selector:name:object:
消息更具体一些。您当前正在注册任何通知,而不仅仅是
NSFileHandleReadCompletionNotification
通知。运行循环是在需要时隐式创建的,因此您可以安全地假设“它有[a]个运行循环”,但您确实需要运行它。应用程序将为您运行运行循环,因此您需要做的只是返回,但在命令行工具中,您必须自己运行运行循环。我建议也使用NSFileHandle读取标准输入,并按照我在回答中所说的那样运行运行循环,直到任务退出。@PeterHosey好主意-谢谢。我尝试过这样做,但没有成功。因此,我回到了简单的、省略了停止的状态,并在启动后提出了(我还注意到,通过
NSFileHandle
传递的数据以某种方式被缓冲了-不知道如何摆脱它),它等待一段时间,然后写一次“包捕获”,然后什么都不写。如果我取消注释1,那么它等待,然后无限次地写“包捕获”。最后,当我尝试提取数据并取消注释2-5时,它等待一段时间,写第2行和第3行,然后冻结。没有崩溃,没有错误,不要提取数据。@user1023979您找到解决方法了吗?我也有同样的问题。没有发送任何通知。@user76859403我的Mac不再工作,因此我无法尝试mneorr建议的解决方案。也许它有效-你自己试试,让我们知道:-)