OS X:如何使用 NSRunLoop 观察套接字读取事件?

Posted

技术标签:

【中文标题】OS X:如何使用 NSRunLoop 观察套接字读取事件?【英文标题】:OS X: how to watch socket read events with NSRunLoop? 【发布时间】:2010-02-16 15:33:18 【问题描述】:

我正在编写一个 Cocoa 应用程序。应用程序中有一个套接字,每当套接字变得可读时,我想从套接字读取数据,处理数据,并相应地更新用户界面。我想在主循环中集成读取事件检查,即我想将套接字附加到主循环并让主循环在该套接字变得可读时调用回调。

我编写了一个测试应用程序,但由于某种原因它不起作用:

#include <stdio.h>
#include <Foundation/NSAutoReleasePool.h>
#include <Foundation/NSRunLoop.h>
#include <Foundation/NSPort.h>

@interface MyDelegate : NSObject <NSPortDelegate> 

- (void)handlePortMessage:(NSPortMessage *)portMessage;
@end

@implementation MyDelegate
- (void)handlePortMessage:(NSPortMessage *)portMessage 
    printf("Haiz\n");

@end

int
main() 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSSocketPort *server = [NSSocketPort alloc];
    MyDelegate *foo = [MyDelegate alloc];
    [server initWithTCPPort: 1234];
    [server setDelegate: foo];
    [[NSRunLoop mainRunLoop] addPort: server forMode: NSDefaultRunLoopMode];
    [[NSRunLoop mainRunLoop] run];
    [pool release];
    return 0;

该应用程序应该在 localhost 端口 1234 上进行侦听,并且每当有人连接到服务器或向服务器发送数据时,该应用程序应该在控制台上打印“Haiz”。但是,该应用程序根本什么都不做。套接字已创建,我可以远程登录到端口 1234,但应用程序不会向控制台打印任何内容。

我做错了什么?

【问题讨论】:

除其他外,将alloc 发送到类但不将init 发送到实例。您使用 MyDelegate 对象完全忘记了这一点,这很好地说明了为什么您应该始终将 allocinit 放在同一个消息表达式中:MyDelegate *foo = [[[MyDelegate alloc] init] autorelease]; 另请注意,您必须释放您分配的对象。查看内存管理规则:developer.apple.com/mac/library/documentation/General/… 【参考方案1】:

来自文档:

NSSocketPort 对象可以用作分布式对象连接的端点。

这不是你在这里做的。

您希望 NSFileHandle 围绕来自 BSD 套接字 API 的套接字文件描述符,或者 CFSocket。这将让您将套接字放在运行循环中。

【讨论】:

NSFileHandle 不是 NSPort,也没有 CFRunLoopSource,所以我无法将它附加到主循环。 我知道 NSFileHandle 不是 NSPort。 NSPorts 用于 IPC。您的委托方法不会被调用,因为您从未在该套接字上收到端口消息。至于附加到运行循环,要么向文件句柄发送acceptConnectionInBackgroundAndNotify 消息,要么改用CFSocket。 我的印象是 FoundationKit 只是围绕 CoreFoundation 的一个客观的 C 包装器,但现在看来它们完全不同了,有些事情我可以用 CoreFoundation 做,但不能用 FoundationKit 做?我之所以试图坚持使用 FoundationKit,是因为我实际上是在 MacRuby 中编写应用程序,而不是 Objective C,并且它在 CoreFoundation API 所期望的函数指针方面存在一些问题。 Foundation 早于 Core Foundation。一些 Foundation 类现在是 CF 类的包装器或免费桥接,但不是全部,有些事情你可以用它们做而你不能用另一个做。【参考方案2】:

您想以您正在做的方式使用NSSocketPort,然后创建一个NSFileHandle 以接受套接字上的连接。您可以像您期望的那样在主线程上获得回调,首先是新连接,然后是这些连接上的数据。使用 O'Reilly article 并忽略 HTTP 内容。

【讨论】:

以上是关于OS X:如何使用 NSRunLoop 观察套接字读取事件?的主要内容,如果未能解决你的问题,请参考以下文章

如何自定义 OS X 的 Finder 边栏

NSRunLoop

卡住试图在 OS X 中的套接字上打开写入流

NSRunLoop 和 NSAutoreleasePool,它们是如何交互的?

OS X 相当于 SO_BINDTODEVICE

无法在 Mac OS X 上启动 mysql 得到 mysql.sock 连接错误