2011-11-03 29 views
7

我正在嘗試使用GCDAsyncSocket來獲得一個簡單的例子,並且發現我錯過了某些理解,希望你能夠幫助解釋這一點。如何在當前RunLoop內執行GCDAsyncSocket內的didReadData?

我設置下面的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是一個事件/味精驅動的體系結構(就像Win32一樣),那麼我目前使用的默認主線程顯然擁有自己的msg循環來處理事件。

我的困惑是現在讓iOS RunLoop看起來像一些獨立的實體,必須使GCDAsyncSocket正常工作。

當它聲明其默認的運行循環模式集是NSDefaultRunLoopMode,它位於主線程中。

困惑了嗎?

所以在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]電話一樣嗎?

我覺得我們需要使用這個庫的更好的例子。

回答

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操作,而無需使用定時器或其他結構。並且可以包含在自己的線程中。 (這仍然待定雖然)

+1

我其實也是這麼做的。只是一次又一次「重新排列」聽衆。如果這是要走的路,我也很好奇。像你一樣,我會希望didReadData被調用而不需要做任何事情。這不就是套接字的全部原因...... – renevdkooi

+0

我現在很困惑,我必須使用suck集羣才能在它們到達時接收數據。 :S – Rihards

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]; 
    } 
}); 

您可以使用相同的隊列來發送心跳請求,只是爲了保持連接活着。

+0

這是不是會對套接字進行強大的保留並防止重新分配?我建議使用'while(![weakSocket isDisconnected])',然後在使用'__strong GCDAsyncSocket * strongSocket = weakSocket;'檢查後檢查它是否爲零,如果是則打破。即使你對套接字使用了一個弱引用,條件'[socket isDisconnected]'也會產生NO,當否定時會導致YES,這意味着你的調度將永遠不會退出。但我可能會誤解。 –

相關問題