GCDAsyncSocket 是基於 Grand Central Dispatch 構建的 TCP 套接字網路庫。該專案還包含一個基於 RunLoop 的版本,以及 UDP 套接字型檔。
CocoaAsyncSocket 專案是一個成熟的開源框架,自 2003 年以來一直存在。因此,它已經受益於廣泛的網路開發人員,他們已經提交了程式碼或建議的功能。該專案的目標是建立強大而易用的套接字型檔。
GCDAsyncSocket 的特性包括:
- 原生 Objective-C 實現,完全獨立在一個類之中。
不需要與 socket 或 stream 混在一起。這個類為您處理所有事情。 - 完整的 delegate 支援
錯誤、連線、讀完成、寫完成、進度和斷開連線都會呼叫委託方法。 - 佇列和非阻塞讀寫,帶有可選的超時選項。
你告訴它讀什麼或寫什麼,它會為你處理一切事情。在自動處理的流中自動排隊、緩衝和搜尋終止序列。 - 自動接收 socket。
啟動一個 socket 服務,告訴它接受連線,它會為每個連線呼叫自己的新例項。 - 支援 IPv4 及 IPv6 上的 TCP 流。
自動連線到 IPv4 或 IPv6 主機。使用這個類的一個例項自動接受透過 IPv4 和 IPv6 傳入的連線。不用再擔心多個 socket。 - 支援 TLS/SSL。
只使用一個方法呼叫就可以輕鬆地保護您的 socket。支援客戶端和服務端 socket。 - 完全基於 GCD 和執行緒安全
它完全在它自己的 GCDdispatch_queue
中執行,並且是完全執行緒安全的。此外,委託方法全部非同步呼叫到您選擇的dispatch_queue
上。這意味著它可以並行執行你的 socket 程式碼和委託/處理程式碼。
GCDAsyncSocket 的一個比較強大的功能是它的佇列架構。這允許您在方便的時候控制套接字,而不是當套接字告訴您它準備好了。舉幾個例子。
// 開始非同步連線。
// 下面的方法將立即返回。
// 並且委託方法socket:didConnectToHost:port:將在連線完成後被呼叫。
[asyncSocket connectToHost:host onPort:port error:nil];
// 此時此刻,套接字還沒有連線。
// 它剛剛開始非同步連線嘗試。
// 但 AsyncSocket 的設計是為了讓你更容易地進行套接字程式設計。
// 如果你方便的話,你可以自由地開始讀/寫。
// 所以我們現在要開始讀取訊息頭的請求。
// 讀取請求會自動排隊。
// 當套接字連線後,這個讀取請求將自動被去掉佇列並執行。
[asyncSocket readDataToLength:LENGTH_HEADER withTimeout:TIMEOUT_NONE tag:TAG_HEADER];
除此之外,你還可以根據方便呼叫多個讀寫請求。
// 開始非同步寫操作
[asyncSocket writeData:msgHeader withTimeout:TIMEOUT_NONE tag:TAG_HEADER];
// 我們不需要等待寫完後再開始下一次寫
[asyncSocket writeData:msgBody withTimeout:TIMEOUT_NONE tag:TAG_BODY];
// 開始非同步讀取操作。
// 讀取並忽略歡迎資訊。
[asyncSocket readDataToData:msgSeparator withTimeout:TIMEOUT_NONE tag:TAG_WELCOME];
// 我們不必等待該次讀取完成後再開始下一次讀取。
// 讀取伺服器能力。
[asyncSocket readDataToData:msgSeparator withTimeout:TIMEOUT_NONE tag:TAG_CAPABILITIES];
佇列式架構甚至延伸到SSL/TLS支援!
// Send startTLS confirmation ACK.
// Remember this is an asynchronous operation.
[asyncSocket writeData:ack withTimeout:TIMEOUT_NONE tag:TAG_ACK];
// We don't have to wait for the write to complete before invoking startTLS.
// The socket will automatically queue the operation, and wait for previous reads/writes to complete.
// Once that has happened, the upgrade to SSL/TLS will automatically start.
[asyncSocket startTLS:tlsSettings];
// Again, we don't have to wait for the security handshakes to complete.
// We can immediately queue our next operation if it's convenient for us.
// So we can start reading the next request from the client.
// This read will occur over a secure connection.
[asyncSocket readDataToData:msgSeparator withTimeout:TIMEOUT_NONE tag:TAG_MSG];
超時(Timeouts)是大多數操作的可選引數。
除此之外,你可能已經注意到了標籤引數。讀/寫操作中傳遞的標籤會在讀/寫操作完成後透過委託方法傳回給你。它不會透過套接字傳送或從套接字中讀取。它的設計是為了幫助簡化您的委託方法中的程式碼。例如,你的委託方法可能是這樣的。
#define TAG_WELCOME 10
#define TAG_CAPABILITIES 11
#define TAG_MSG 12
...
- (void)socket:(AsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
if (tag == TAG_WELCOME)
{
// Ignore welcome message
}
else if (tag == TAG_CAPABILITIES)
{
[self processCapabilities:data];
}
else if (tag == TAG_MSG)
{
[self processMessage:data];
}
}
GCDAsyncSocket是執行緒安全的。
License
這個類是在公共領域。
最初由 Robbie Hanson 在 2010 年第三季度建立。
由 Deusty Designs 和 Mac 開發社群更新和維護。
Reference
...
建立 socket 方法:
/**
* GCDAsyncSocket 使用標準的委託模式。
* 但會在給定的委託佇列上執行所有的委託回撥。
* 這可以允許最大的併發性,同時提供簡單的執行緒安全。
*
* 您必須先設定一個委託和委託佇列,然後再嘗試使用 socket,否則你會得到一個錯誤。
*
* socket 佇列是可選的
* 如果傳遞 NULL,GCDAsyncSocket 將自動建立自己的 socket 佇列。
* 如果你選擇自己提供一個 socket 佇列,那麼該 socket 佇列必須不是一個併發佇列。
* 如果選擇提供一個 socket 佇列,並且 socket 佇列有一個配置的 target 佇列,那麼GCDAsyncSocket 將自動建立自己的套接字佇列。
* 那麼請看 markSocketQueueTargetQueue 方法的討論。
*
* 委託佇列和 socket 佇列可以選擇使用相同的佇列。
**/
- (instancetype)init;
- (instancetype)initWithSocketQueue:(nullable dispatch_queue_t)sq;
- (instancetype)initWithDelegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq;
- (instancetype)initWithDelegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq NS_DESIGNATED_INITIALIZER;
當 socket 斷開連線時被呼叫:
/**
* 當 socket 斷開連線時呼叫,可能有錯誤,可能沒有錯誤。
*
* 如果你呼叫 disconnect 方法,而 socket 還沒有被斷開。
* 那麼對該委託方法的呼叫將被列入委託人佇列(delegateQueue)。
* 在斷開方法返回之前。
*
* 注意:如果 GCDAsyncSocket 例項在連線時被 deallocated,
* 而同時 delegate 沒有被 deallocated,那麼這個方法也會被呼叫。
* 但回撥方法中的 sock 引數將是nil。(它必須是 nil,因為它已經不可用了)。
* 這種情況一般很少見,但如果寫這樣的程式碼是可能的。
*
* asyncSocket = nil; // 我隱含地斷開了 socket 連線。
*
* 在這種情況下,最好事先將 delegate 作廢,比如這樣。
*
* asyncSocket.delegate = nil; // 不要呼叫我的 delegate 方法。
* asyncSocket = nil; // 我隱含地斷開了 socket 連線。
*
* 當然,這取決於你的狀態機是如何配置的。
**/
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullable NSError *)err;
讀取資料的幾個方法:
//讀取資料,有資料就會觸發代理
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
// 讀取指定長度的資料,才會觸發代理
- (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
// 讀取到指定 data 邊界,才會觸發代理
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
©著作權歸作者所有,轉載或內容合作請聯絡作者