本人有若干成套學習視頻, 可試看! 可試看! 可試看, 重要的事情說三遍 包含Java
, 數據結構與算法
, iOS
, 安卓
, python
, flutter
等等, 如有需要, 聯系微信tsaievan
.
GCDAsynSocket是一個建立在GCD上的TCP socket庫, 這個項目同樣包含基于Runloop的版本, 以及UDP socket庫.
GCDAsynSocket項目是一個成熟的開源框架, 大約成型于2013年. 得益于廣大網絡開發者提交代碼或者給出建議. 這個項目的目的是創建一個功能強大, 便于使用的socket庫.
GCDAsynSocket的特點包括:
-
支持經典代理模式.
以下所有操作都會調用代理方法 : 連接, 接收, 讀, 寫, 進程, 斷開連接, 錯誤處理等, 代理方法包含一個socket參數, 使得你便于區別不同的socket實例.
-
代理分發
每一個代理方法都會在可配置的線程被調用. 所以, 并發socket IO, 并發數據處理, 以及線程安全成為可能.
-
讀寫隊列不阻塞, 超時可選
你告訴socket讀什么, 寫什么, 完成之后調用代理
-
socket自動接收
如果你告訴socket允許連接, 它就會調用代理告知連接成功, 當然, 你也可以立即斷開連接
支持 IPv4和IPv6
支持SSL/TLS
基于GCD和kqueues的最新技術
-
在類中, 自己包含自己
你不必糾纏于各種流和sockets, 這個類都幫你處理了
GCDAsynSocket的一個強大特征之一就是它的隊列結構, 這便于你控制這些socket, 而不必等到socket告訴你已經就緒. 例如:
// 開始異步連接
// 下面這個方法很快會return掉
// 連接完成之后, 代理方法socket:didConnectToHost:port: 就會立即被調用
[asyncSocket connectToHost:host onPort:port error:nil];
// 走到這一行代碼時, socket還沒有連接
// 只是試圖開始異步連接.
// 但是這個框架就是設計用來讓你便于使用socket的
// 你完全可以在這里就開始往socket里面讀數據或者寫數據
// 所以我們在這里發起我們消息頭的讀請求
// 讀請求會自動被加入隊列
// 等到socket連接成功后, 讀請求會自動出列并執行
[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
// 發出 startTLS 確認 ACK.
// 記住, 這是一個異步操作
[asyncSocket writeData:ack withTimeout:TIMEOUT_NONE tag:TAG_ACK];
// 我們不必等到上一個寫操作完成, 就可以開始發起startTLS
// startTLS請求會自動被加入隊列
// 等到寫操作完成之后, startTLS請求會自動出列并執行
// 一旦請求王城, SSL/TLS 就會自動升級并執行
[asyncSocket startTLS:tlsSettings];
// 再說一遍, 我們不會等到安全握手完成
// 我們可以立即將下一個操作加入隊列
// 所以我們可以立即讀取客戶端的下一條請求
// 這個讀請求會在安全連接之后發生
[asyncSocket readDataToData:msgSeparator withTimeout:TIMEOUT_NONE tag:TAG_MSG];
超時對于大多數操作來說是可選參數
另外, 你可能注意到了tag參數, 你傳進去的tag參數, 在讀寫操作完成之后調用代理方法的時候再回傳給你, 這個參數并不會寫到socket里面, 再從socket里面寫出來. 這樣設計是為了方便你在代理方法中更簡單地編碼, 例如:
#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是線程安全的
執照
這個類是公開的
最初由 Robbie Hanson 于2010年第三季度創建
由 Deusty Designs 和 the Mac 開發社區維護
文檔
代理方法
- socket: didConnnectToHost:port:
- socket: didReadData:WithTag:
- socket: didReadPartialDataOfLength:tag:
- socket: shouldTimeoutReadWithTag:elapsed:bytesDone:
- socket: didWriteDataWithTag:
- socket: didWritePartialDataOfLength:tag:
- socket: shouldTimeoutWriteWithTag:elapsed:bytesDone:
- socketDidSecure:
- socket: didAcceptNewSocket:
- newSocketQueueForConnectionFromAddress:onSocket:
- socketDidCloseReadStream:
- socketDidDisconnect:withError:
初始化
- init
- initWithSocketQueue:
- initWithDelegate:delegateQueue:
- initWithDelegate:delegateQueue:socketQueue:
配置
- delegate
- setDelegate:
- delegateQueue
- setDelegateQueue:
- getDelegate:delegateQueue:
- setDelegate:delegateQueue:
- autoDisconnectOnClosedReadStream
- setAutoDisconnectOnClosedReadStream:
- isIPv4Enabled
- setIPv4Enabled:
- isIPv6Enabled
- setIPv6Enabled:
- isIPv4PreferredOverIPv6
- setPreferIPv4OverIPv6:
接受
- acceptOnPort:error:
- acceptOnInterface:port:error:
連接
connectToHost: onPort: error:
connectToHost: onPort: withTimeout: error:
connectToHost: onPort: viaInterface: withTimeout: error:
ReadingreadDataWithTimeout: tag:
readDataWithTimeout: buffer: bufferOffset: tag:
readDataWithTimeout: buffer: bufferOffset: maxLength: tag:
readDataToLength: withTimeout: tag:
readDataToLength: withTimeout: buffer: bufferOffset: tag:
readDataToData: withTimeout: tag:
readDataToData: withTimeout: buffer: bufferOffset: tag:
readDataToData: withTimeout: maxLength: tag:
readDataToData: withTimeout: buffer: bufferOffset: maxLength: tag:
寫
- writeData: withTimeout: tag:
診斷
- isDisconnected
- connectedHost
- connectedPort
- localHost
- localPort
- connectedAddress
- localAddress
- isIPv4
- isIPv6
斷開連接
- disconnect
- disconnectAfterReading
- disconnectAfterWriting
- disconnectAfterReadingAndWriting
安全
- startTLS
高級
- performBlock:
- socketFD
- socket4FD
- socket6FD
- readStream
- writeStream
- sslContext
實用
- hostFromAddress:
- portFromAddress:
- getHost: port: fromAddress:
- CRLFData
- CRData
- LFData
- ZeroData
代理方法
GCDAsyncSocket是異步的, 所以對大多數方法來說, 當你在socket上開始一個操作時(連接, 接受, 讀, 寫), 方法都會立即返回, 操作的結果將會通過相關的響應代理方法返回給你.
socket: didConnectToHost: port:
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
當socket成功連接并準備開始讀寫的時候調用, 主機參數是IP地址, 而不是DNS解析域名
socket: didReadData: withTag:
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
當socket完成讀取請求數據進內存的時候調用, 如果發生錯誤, 則不會調用
tag參數是你請求讀操作的時候傳入的, 比如在readDataWithTimeout: tag: 方法中.
socket: didReadPartialDataOfLength: tag:
- (void)socket:(GCDAsyncSocket *)sock didReadPartialDataOfLength:(NSUInteger)partialLength tag:(long)tag
當socket還在讀數據, 但是還沒有完成讀操作的時候調用. 當你使用readDataToData:或者 readDataToLength: 等方法時調用, 這個代理方法可以用在例如更新進度條等例子里.
tag參數是你請求讀操作的時候傳入的, 比如在readDataToLength: WithTimeout: tag: 方法中.
socket: shouldTimeoutReadWithTag: elapsed: bytesDone:
- (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag
elapsed:(NSTimeInterval)elapsed
bytesDone:(NSUInteger)length
讀操作超時, 未完成時調用. 這個方法允許你設置超時時長. 如果你返回一個正的時間間隔(>0), 讀操作的超時就將按照設置的來進行, 如果你未實現這個代理方法, 或者返回一個非正時間間隔(<=0), 讀操作的超時就跟平常一樣
流逝時間(elapsed)參數是初始超時, 加上每一次調用這個方法時超時時間累加之和, 長度(length)參數表示到目前為止, 讀操作已讀的字節長度
注意: 如果你在這個代理方法中返回一個正數, 那么在一次讀操作中, 這個方法會被調用多次
socket: didWriteDataWithTag:
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
當socket完成寫入請求的時候調用, 如果發生錯誤, 則不會調用
tag參數是你請求讀操作的時候傳入的, 比如在writeDataWithTimeout: tag: 方法中.
socket: didWritePartialDataOfLength: tag:
- (void)socket:(GCDAsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag
當socket已經寫入一些數據, 但是還沒有完成整個寫操作時調用, 這個方法可能用于更新進度條之類的例子中
tag參數是你請求讀操作的時候傳入的, 比如在writeDataWithTimeout: tag: 方法
socket: shouldTimeoutWriteWithTag: elapsed: bytesDone:
- (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag
elapsed:(NSTimeInterval)elapsed
bytesDone:(NSUInteger)length;
寫操作超時, 未完成時調用. 這個方法允許你設置超時時長. 如果你返回一個正的時間間隔(>0), 寫操作的超時就將按照設置的來進行, 如果你未實現這個代理方法, 或者返回一個非正時間間隔(<=0), 寫操作的超時就跟平常一樣
流逝時間(elapsed)參數是初始超時, 加上每一次調用這個方法時超時時間累加之和, 長度(length)參數表示到目前為止, 寫操作已寫入的字節長度
注意: 如果你在這個代理方法中返回一個正數, 那么在一次寫操作中, 這個方法會被調用多次
socketDidSecure:
- (void)socketDidSecure:(GCDAsyncSocket *)sock
當socket成功完成SSL/TLS談判的時候調用, 這個方法, 只有在你使用了startTLS方法之后才會調用, 否則不會調用
如果SSL/TLS談判失敗(例如證書無效等原因), socket將會立即關閉, 且socketDidDisconnect:withError:這個代理方法將會被立即調用, 錯誤代碼就是SSL錯誤代碼
在蘋果的Security.framework中的SecureTransport.h文件中查閱SSL錯誤碼以及其對應的含義
socket: didAcceptNewSocket:
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
當一個"服務器"socket接受一個連過來的"客戶端"socket時, 將會調用此代理方法. 另一個socket將會產生去處理它.
你必須持有newSocket如果你希望保持連接的話, 否則, 新的socket實例將會被釋放, 產生的連接將會關閉
默認新的socket將會使用同一個代理和代理隊列, 當然, 你也可以隨時改變它
默認socket將會產生自己內部的socket隊列去運行. 這也是可以通過實現newSocketQueueForConnectionFromAddress:onSocket: method這個代理方法來配置
newSocketQueueForConnectionFromAddress: onSocket:
- (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock;
這個方法將會在socket:didAcceptNewSocket:方法之前立即調用, 這個方法允許監聽socket去指定socketQueue(socket隊列)給新的那個已經接受的socket, 如果這個方法沒有實現, 或者返回NULL, 新的已經接受的socket將會創建一個自己的默認隊列.
因為你無法自動釋放一個dispatch_queue(派發隊列), 這個方法使用"new"這個前綴在方法名前去指定這個返回的隊列已經被持有了
因而你可以在實現中這樣寫:
return dispatch_queue_create("MyQueue", NULL);
如果你將多個socket放在同一個隊列中, 那么就要注意, 每調用一次這個方法, 都要增加隊列的引用計數
例如, 你的代碼應該這樣實現:
dispatch_retain(myExistingQueue);
return myExistingQueue;
socketDidCloseReadStream:
- (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock
如果讀流關閉將會調用此方法, 但寫入流依然可寫
這個方法只有在autoDisconnectOnClosedReadStream被設置為NO的時候才會調用, 看autoDisconnectOnClosedReadStream的討論以獲取更多信息
socketDidDisconnect: withError:
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)error
當socket斷開連接時調用, 不管是正常斷開還是因為出錯斷開
如果你調用disconnect方法, 且socket還沒有斷開, 那么這個代理方法將會在disconnect方法return之前調用 (因為disconnect方法是同步的)
初始化
GCDAsyncSocket使用標準的代理范例, 但它在指定的代理分發隊列中執行代理回調. 這使得在同一時間允許最大并發, 且保證線程安全
你必須在試圖使用socket之前設置代理和代理隊列, 否則將會報錯
socket隊列是一個分發隊列, 在這個隊列中, GCDAsyncSocket實例在這個隊列中進行相應的操作. 你可以在初始化時可選地設置socket隊列. 如果你不選擇, 或者傳入NULL, GCDAsyncSocket將自動創建它自己的隊列, 如果你選擇提供socket隊列, 這個socket隊列必須不是一個并發隊列.
代理隊列和socket隊列可以是同一個
init
- (id)init
觸發指定構造器, 參數值為nil. 你需要在使用socket之前設置代理以及代理隊列
initWithSocketQueue:
- (id)initWithSocketQueue:(dispatch_queue_t)sq
以指定socket隊列, 觸發指定構造器. 你需要在使用socket之前設置代理以及代理隊列
initWithDelegate: delegateQueue:
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq
以給定的代理和代理隊列觸發指定構造器
initWithDelegate: delegateQueue: socketQueue:
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq
指定構造器
以指定的代理和代理分發隊列創建socket
socket分發隊列是可選的. socket將在這條隊列內部進行操作. 如果是NULL, 新的分發隊列將自動被創建. 如果你選擇提供一條socket隊列, socket隊列不能是并發隊列
代理隊列和socket隊列可以是一樣的
配置
delegate
- (id)delegate
返回當前socket設置的代理對象
setDelegate:
- (void)setDelegate:(id)delegate
推薦你在釋放socket對象之前設置socket代理. 在disconnect方法中獲取更多信息
delegateQueue
- (dispatch_queue_t)delegateQueue
返回當前socket設置的代理隊列, 所有代理方法將在這個隊列里異步調用
setDelegateQueue:
- (void)setDelegateQueue:(dispatch_queue_t)delegateQueue
為socket設置代理隊列. 調用此方法后, 將來所有代理方法將被分發到指定隊列
getDelegate: delegateQueue:
- (void)getDelegate:(id *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr
代理和代理隊列通常同時出現, 這個方法提供了一個線程安全的途徑在一個操作中去獲取當前代理配置(包括代理及其隊列)
setDelegate: delegateQueue:
- (void)setDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
提供了一條簡便且線程安全的方法在一個操作中去更改代理和代理隊列
如果你打算更改代理和代理隊列, 推薦使用這個方法
autoDisconnectOnClosedReadStream
- (BOOL)autoDisconnectOnClosedReadStream
一般來講, socket不會關閉直到會話結束. 然而, 技術上可以實現遠端關閉socket寫入流. socket將會被通知沒有更多的數據將要被讀, 但socket將依然可寫并且遠端繼續接收數據
這個令人困惑的實用參數阻止了一個觀點, 那就是客戶端在發送一個請求給服務器之后, 將關閉其寫入流, 然后通知服務器, 沒有更多的請求了. 實際上, 這一技術對服務器開發者沒什么幫助
更糟糕的是, 從TCP的角度來說, 沒有辦法區別讀流關閉和整個socket關閉, 這都將導致TCP棧收到一個FIN包. 唯一的方法區別就是依靠繼續寫入socket. 如果只有讀流被關閉, 寫將仍然可以進行, 否則, 將會發生錯誤(當遠端發給我們一個RST包)
另外技術上的挑戰和困惑是, 很多高級的socket/stream 沒有提供解決此類問題的API. 如果讀流被關閉, API將通知socket立即關閉, 同時也關閉寫入流, 事實上, 蘋果的CFStream就是這么做的. 起初看起來是一個糟糕的設計, 但事實上它簡化了開發.
大部分情況下, 讀流被關閉是因為遠端關閉了socket. 因而在這個定上關閉socket就能夠說得通了. 事實上, 這是大多數網絡開發者希望看到的. 然而, 如果你在寫一個服務器, 這個服務器跟過多的客戶端交互, 你可能會遇到一個客戶端, 使用這種不被鼓勵的關閉寫流的技術. 如果是這種情況的話, 你可以設置屬性為NO, 然后利用 socketDidCloseReadStream 代理方法.
默認值為YES
setAutoDisconnectOnClosedReadStream:
- (void)setAutoDisconnectOnClosedReadStream:(BOOL)flag
設置autoDisconnectOnClosedReadStream配置選項, 詳見autoDisconnectOnClosedReadStream方法
isIPv4Enabled
- (BOOL)isIPv4Enabled
默認情況下, IPv4和IPv6都可用的
對于接收連接時, 這意味著GCDAsyncSocket自動支持兩種協議, 并且能夠接收任意一種協議的鏈接
對于發送鏈接時, 這意味著GCDAsyncSocket能夠連接運行任意一種協議的主機, 如果DNS解析返回IPv4結果, 那么GCDAsyncSocket自動使用IPv4, 如果DNS解析返回IPv6結果, 那么GCDAsyncSocket自動使用IPv6, 如果DNS解析返回IPv4結果和IPv6結果, 那么GCDAsyncSocket會使用優先的那一個, 優先的是IPv4, 但也可能是根據要求配置的.
setIPv4Enabled:
- (void)setIPv4Enabled:(BOOL)flag
啟用和禁用IPv4的支持
注意: 如果socket已經連接或者接收連接, 更改這個屬性無法影響當前socket, 只會改變以后的socket連接(當前的socket連接切斷之后). 一旦設置, 這個偏好將會影響以后所有的基于GCDAsyncSocket實例的socket連接.
isIPv6Enabled
- (BOOL)isIPv6Enabled
默認情況下, IPv4和IPv6都可用的
對于接收連接時, 這意味著GCDAsyncSocket自動支持兩種協議, 并且能夠接收任意一種協議的鏈接
對于發送鏈接時, 這意味著GCDAsyncSocket能夠連接運行任意一種協議的主機, 如果DNS解析返回IPv4結果, 那么GCDAsyncSocket自動使用IPv4, 如果DNS解析返回IPv6結果, 那么GCDAsyncSocket自動使用IPv6, 如果DNS解析返回IPv4結果和IPv6結果, 那么GCDAsyncSocket會使用優先的那一個, 優先的是IPv4, 但也可能是根據要求配置的.
setIPv6Enabled:
- (void)setIPv6Enabled:(BOOL)flag
啟用和禁用IPv6的支持
注意: 如果socket已經連接或者接收連接, 更改這個屬性無法影響當前socket, 只會改變以后的socket連接(當前的socket連接切斷之后). 一旦設置, 這個偏好將會影響以后所有的基于GCDAsyncSocket實例的socket連接.
isIPv4PreferredOverIPv6
- (BOOL)isIPv4PreferredOverIPv6
默認情況下, 優先的協議是IPv4
setPreferIPv4OverIPv6:
- (void)setPreferIPv4OverIPv6:(BOOL)flag
設置有限的協議, 詳情請看isIPv4Enabled方法
接收
一旦接收或者連接方法被調用, GCDAsyncSocket實例將被鎖定, 其他的接收/連接將無法被調用, 除非socket連接斷開
當socket接收一個連接的時候, GCDAsyncSocket調用以下代理方法(按時間先后順序):
- newSocketQueueForConnectionFromAddress:onSocket:
- socket:didAcceptNewSocket:
你的服務器代碼必須持有已經接收的socket(如果你想接收它的話), 否則, 新接收的socket將會在代理方法返回之后被銷毀(此時socketDidDisconnect:withError:將會被調用)
acceptOnPort: error:
- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr
告訴socket在指定端口監聽和接收連接. 當一個連接被接收, 新的GCDAsyncSocket實例對象將被產生去處理這個連接, socket:didAcceptNewSocket: 這個代理方法將會被調用
這個socket將會在所有可得的接口監聽(例如: wifi, 以太網等)
如果socket能夠開始監聽, 那么這個方法返回YES, 如果發生錯誤, 這個方法返回NO, 并且設置可選的errPtr參數. 例如, 代理沒有設置, 或者已經接收連接, 都會導致這個發生錯誤
acceptOnInterface: port: error:
- (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port error:(NSError **)errPtr
這個方法和acceptOnPort: error:差不多, 只是添加了可選參數去指定接口去監聽
例如, 你可以指定socket只接收基于以太網的連接, 而不是其他連接, 例如wifi之上的
這個接口你可以通過名稱來指定(例如 "en1" 或者 "lo0"), 或者通過IP地址來指定(例如 "192.168.4.34"). 你也可以通過特殊的字符串 "localhost" 或者 "loopback" 來指定socket值連接本地機器的socket
你可以通過命令行工具"ifconfig"查看這些接口列表, 或者通過編程, 利用 getifaddrs()這個函數來獲取
如果要接收任何接口的連接, interface參數就傳nil. 或者就簡單地使用acceptOnPort: error:這個方法
如果socket能夠開始監聽, 那么這個方法返回YES, 如果發生錯誤, 這個方法返回NO, 并且設置可選的errPtr參數. 例如, 代理沒有設置, 或者已經接收連接, 或者請求的接口找不到, 都會導致這個發生錯誤
連接
一旦接收或者連接方法被調用, GCDAsyncSocket實例將被鎖定, 其他的接收/連接將無法被調用, 除非socket連接斷開
connectToHost: onPort: error:
- (BOOL)connectToHost:(NSString *)host onPort:(UInt16)port error:(NSError **)errPtr
連接到指定主機和端口
這個方法將會調用connectToHost:onPort:viaInterface:withTimeout:error: 這個方法, 使用默認網絡接口, 沒有超時時長
返回YES表示異步的連接請求開始. 返回NO表示檢測到發生錯誤并且會給errPtr參數賦值
connectToHost: onPort: withTimeout: error:
- (BOOL)connectToHost:(NSString *)host onPort:(UInt16)port withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr
連接到指定主機和端口, 超時時長可選
這個方法將會調用connectToHost:onPort:viaInterface:withTimeout:error: 這個方法, 使用默認網絡接口
connectToHost: onPort: viaInterface: withTimeout: error:
- (BOOL)connectToHost:(NSString *)host
onPort:(UInt16)port
viaInterface:(NSString *)interface
withTimeout:(NSTimeInterval)timeout
error:(NSError **)errPtr
連接到指定主機和端口, 通過可選的網絡接口, 以及可選的超時時長
主機名可以是域名(例如: "deusty.com") 或者 IP地址字符串 (例如 "192.168.0.2"). 網絡接口可以是名字 (例如 "en1" 或者 "lo0")或者相應的IP地址 (例如 "192.168.4.35")
沒有超時時長請傳入負值
這個方法將會返回NO, 如果檢測到錯誤的話,并且會給錯誤參數賦值(如果指定的話). 可能的錯誤是,一個空的主機, 無效的網絡接口, 或者socket已經連接
如果沒有檢測到錯誤, 這個方法將會啟動后臺連接操作并立刻返回YES, 代理回調將被用來通知你socket連接成功, 或者找不到主機
由于這個類支持隊列讀和寫, 你可以理科開始讀或者寫操作, 所有的讀/寫操作都將被加入隊列, 在socket連接成功之后, 這些讀寫操作將會出列并且按順序執行