Objective C : 如何持续监听 stdin 上的传入数据?

Objective C : How to keep listening to incoming data on stdin?

我正在尝试使用 native messaging 从浏览器扩展中调用用 Objective C 编写的 MacOS 应用程序。

到目前为止,我已经想出了这段代码来处理使用 chrome.runtime.sendNativeMessage 发出的请求。

int main(int argc, char * argv[]) {
  @autoreleasepool {
    NSFileHandle *stdIn = [NSFileHandle fileHandleWithStandardInput];
    NSError * stdinError = nil;
    NSData * rawReqLen = [stdIn readDataUpToLength:4 error:&stdinError];
    if(rawReqLen == nil || stdinError != nil) return 1;
    NSUInteger reqLen;
    [rawReqLen getBytes:&reqLen length:4];

    NSData * req = [stdIn readDataUpToLength:reqLen error:&stdinError];
    if(stdinError != nil) return 1;

    handleRequest(req); // this does something depending on the received request

    return 0;
  }
}

这是有效的。

我需要帮助弄清楚如何使用 chrome.connectNative 来保持连接打开。具体来说,我的问题是让 MacOS 应用程序继续侦听传入消息,直到通过浏览器扩展程序调用 port.disconnect 关闭端口。

我已经试过这个代码,但它不起作用。

int main(int argc, char * argv[]) {
  @autoreleasepool {
    NSFileHandle *stdIn = [NSFileHandle fileHandleWithStandardInput];
  
    [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification
                                                          object:stdIn
                                                         queue:[NSOperationQueue mainQueue]
                                                      usingBlock:^(NSNotification *note) {
      NSError * stdinError = nil;
      NSData * rawReqLen = [stdIn readDataUpToLength:4 error:&stdinError];
      if(rawReqLen == nil || stdinError != nil) exit(1);
      NSUInteger reqLen;
      [rawReqLen getBytes:&reqLen length:4];

      NSData * req = [stdIn readDataUpToLength:reqLen error:&stdinError];
      if(stdinError != nil) exit(1);

      handleRequest(req);
    }];
    [stdIn waitForDataInBackgroundAndNotify];
  }
}

有人知道如何实现吗?

解决方法是:

int main(int argc, char * argv[]) {
  @autoreleasepool {
    NSFileHandle *stdIn = [NSFileHandle fileHandleWithStandardInput];
  
    [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification
                                                          object:stdIn
                                                         queue:[NSOperationQueue mainQueue]
                                                      usingBlock:^(NSNotification *note) {
      NSError * stdinError = nil;
      NSData * rawReqLen = [stdIn readDataUpToLength:4 error:&stdinError];
      if(rawReqLen == nil || stdinError != nil) exit(1);
      uint32_t reqLen;
      [rawReqLen getBytes:&reqLen length:4];
      reqLen = OSSwapLittleToHostInt32(reqLen);

      NSData * req = [stdIn readDataUpToLength:reqLen error:&stdinError];
      if(stdinError != nil) exit(1);

      handleRequest(req);
      [stdIn waitForDataInBackgroundAndNotify];
    }];
    [stdIn waitForDataInBackgroundAndNotify];
  }

  NSRunLoop *loop = [NSRunLoop currentRunLoop];
  [loop acceptInputForMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
  [loop run];
  return 0;
}

如果您想保持端口打开,也可以将调用 exit(1) 替换为对 [stdIn waitForDataInBackgroundAndNotify]; 的新调用。

这可以很好地处理传入请求,但会破坏响应:写入 stdout 不再有效,我可能需要为每个响应停止 运行 循环并在完成后重新启动它。 (尚未测试)