我试图得到一个简单的例子与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...rn";
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工作正常。
当它声明它的运行循环模式的默认设置是nsdefaulunloopmode时,它在主线程中。
困惑了吗?
所以在Win32下,我的通信事件处理代码看起来像这样:
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]调用?
好了,我让它以某种方式工作了。
让我知道这是否违背了某些"最佳实践"。
- (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操作,而不使用计时器或其他结构。并且可以被包含在自己的线程中。
我知道这是一个老问题,已经有一个公认的答案,但这里是我的解决方案,我已经在我的一个应用程序中使用:
连接到主机后,运行以下调度队列:
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];
}
});
您可以使用相同的队列来发送心跳请求,以保持连接存活。