如何在当前的RunLoop中执行GCDAsyncSocket中的didReadData?

时间:2011-11-03 15:07:35

标签: runloop cocoaasyncsocket

我正在尝试使用GCDAsyncSocket一个简单的例子,并且发现我缺少某些理解,并希望你们能帮助解释这个问题。

我在下面设置了GCDAsyncSocket:

dispatch_queue_t mainQueue = dispatch_get_main_queue();
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:mainQueue];

NSString *host = @"192.168.169.132";
uint16_t port = 2112;

DDLogInfo(@"Connecting to \"%@\" on port %hu...", host, port);
self.viewController.label.text = @"Connecting...";

NSError *error = nil;
if (![asyncSocket connectToHost:host onPort:port withTimeout:5.0 error:&error])
{
    DDLogError(@"Error connecting: %@", error);
    self.viewController.label.text = @"Oops";
}
else
{
    DDLogVerbose(@"Connecting...");
}


- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    DDLogInfo(@"socket:%p didConnectToHost:%@ port:%hu", sock, host, port);
    self.viewController.label.text = @"Connected";

    // We're just going to send a test string to the server.

    NSString *myStr = @"testing...123...\r\n";
    NSData *myData = [myStr dataUsingEncoding:NSUTF8StringEncoding];

    [asyncSocket writeData:myData withTimeout:5.0 tag:0];
}

可以看到我的套接字测试服务器应用程序收到字符串

  

“测试... 123 ... \ r \ n” 个

但是当我让套接字测试服务器发回字符串时,我天真地期望 didReadData 委托执行

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

然而,冷酷的现实迫使我学习,直到我打电话

[asyncSocket readDataWithTimeout:5.0 tag:0];

... didReadData 委托将不会被调用。

好的,没关系。我明白了。

再次阅读the documentation,明确指出

  

AsyncSocket是一个基于RunLoop的TCP套接字库。

所以现在我正在看这个 RunLoop 这个东西,在我看来就像Message loop in Microsoft Windows。因为iOS是一个事件/ msg驱动的架构(就像Win32一样),那么我目前使用的默认主线程显然有自己的msg循环来处理事件。

我的困惑现在让iOS RunLoop看起来像是一个独立的实体,必须使GCDAsyncSocket正常工作。

当它声明其默认的运行循环模式集是NSDefaultRunLoopMode时,它位于主线程中。

困惑了吗?

所以在Win32下我的comm事件处理代码如下所示:

while( sCOMport.hCOMport != INVALID_HANDLE_VALUE )  // ...while the COM port is open...
{
    // Wait for an event to occur on the port.
    WaitCommEvent( sCOMport.hCOMport, &dwCommStatus, NULL );

它当然会在它自己的线程中(还没有使用GCDAsyncSocket),但在某种程度上它将是它自己的“RunLoop”。

如何使用 GCDAsyncSocket 执行相同操作,以便我不会陷入某些使用 [asyncSocket readDataWithTimeout] 调用填充队列的轮询循环?

我觉得我们在使用这个库时需要更好的例子。

2 个答案:

答案 0 :(得分:18)

好的,我以某种方式使用它。

如果这违反了某些“最佳做法”,请告诉我。

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    // setup as normal...

    // then ...

    // Instigate the first read
    [asyncSocket readDataWithTimeout:-1 tag:0];

.
.
}

然后......当数据进来时......

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    // Do whatever you need to do with the data ...
.
.
.
    // and at the end ...
.
.
    // Always keep a 'read' in the queue.
    [asyncSocket readDataWithTimeout:-1 tag:0];
}

这将为您提供RunLoop操作,而无需使用计时器或其他构造。并且可以包含在自己的线程中。 (那仍然是TBD)

答案 1 :(得分:1)

我知道这是一个老问题,已经有了一个可接受的答案,但这是我已经在我的一个应用程序中使用的解决方案:

连接到主机后运行以下调度队列:

dispatch_queue_t alwaysReadQueue = dispatch_queue_create("com.cocoaasyncsocket.alwaysReadQueue", NULL);

dispatch_async(alwaysReadQueue, ^{
    while(![socket isDisconnected]) {
        [NSThread sleepForTimeInterval:5];
        [socket readDataWithTimeout:-1 tag:0];
    }
});

您可以使用相同的队列发送心跳请求,以保持连接活跃。