7

我试图获得一个使用 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委托不会被调用。

好没问题。我得到它。

更多地阅读文档,它清楚地表明

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

所以现在我在看这个RunLoop的东西,在我看来它就像Microsoft Windows 中的 Message 循环。由于 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]调用填充队列的一些轮询循环中?

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

4

2 回答 2

19

好的,我得到了这个以某种方式工作。

让我知道这是否违反某些“最佳实践”。

- (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 操作,而无需使用计时器或其他构造。并且可以包含在它自己的线程中。(虽然这仍然是待定)

于 2011-11-03T16:02:10.750 回答
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];
    }
});

您可以使用相同的队列来发送心跳请求,以保持连接处于活动状态。

于 2014-03-31T09:47:28.417 回答