AsyncSocket API簡述

iPhone的標準推薦是CFNetwork 庫編程,其封裝好的開源庫是 cocoa AsyncSocket庫,用它來簡化CFNetwork的調用,它提供了異步操作

主要特性有:

隊列的非阻塞的讀和寫,而且可選超時。你可以調用它讀取和寫入,它會當完成后告知你

自動的socket接收。如果你調用它接收連接,它將為每個連接啟動新的實例,當然,也可以立即關閉這些連接

委托(delegate)支持。錯誤、連接、接收、完整的讀取、完整的寫入、進度以及斷開連接,都可以通過委托模式調用

基于run loop的,而不是線程的。雖然可以在主線程或者工作線程中使用它,但你不需要這樣做。它異步的調用委托方法,使用NSRunLoop。委托方法包括socket的參數,可讓你在多個實例中區分

自包含在一個類中。你無需操作流或者socket,這個類幫你做了全部

支持基于IPV4和IPV6的TCP流

加入:AsynSocket.h .m與AsynUdpSocket.h .m四個文件 及CFNetwork.framework

TCP客戶端

#import "AsyncSocket.h"

@interface HelloiPhoneViewController : UIViewController {

UITextField ? ?* textField;

AsyncSocket * asyncSocket;

}

@property (retain, nonatomic) IBOutlet UITextField *textField;

- (IBAction) buttonPressed: (id)sender;

- (IBAction) textFieldDoneEditing: (id)sender;

@end

在需要聯接地方使用connectToHost聯接服務器

其中initWithDelegate的參數中self是必須。這個對象指針中的各個Socket響應的函數將被ASyncSocket所調用.initWithDelegate把將當前對象傳遞進去,這樣只要在當前對象方法實現相應方法

asyncSocket = [[AsyncSocket alloc] initWithDelegate:self];

NSError *err = nil;

if(![asyncSocket connectToHost:host on:port error:&err])

{

NSLog(@"Error: %@", err);

}

關于NSData對象

無論SOCKET收發都采用NSData對象.它的定義是?http://developer.apple.com/library/mac /#documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/Reference/Reference.html

NSData主要是帶一個(id)data指向的數據空間和長度 length.

NSString 轉換成NSData 對象

NSData* xmlData = [@"testdata" dataUsingEncoding:NSUTF8StringEncoding];

NSData 轉換成NSString對象

NSData * data;

NSString *result = [[NSString alloc] initWithData:data ??????? encoding:NSUTF8StringEncoding];

發送數據

AsyncSocket ?writeData ? ?方法來發送數據,它有如下定義

- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

以下是一個實例語句.

NSData* aData= [@"test data" dataUsingEncoding: NSUTF8StringEncoding];

[sock writeData:aData withTimeout:-1 tag:1];

在onSocket重載函數,有如定義采用是專門用來處理SOCKET的發送數據的:

-(void)onSocket(AsyncSocket *)sock didWriteDataWithTag:(long)tag

{

NSLog(@"thread(%),onSocket:%p didWriteDataWithTag:%d",[[NSThread currentThread] name],

sock,tag);

}

接收Socket數據.

在onSocket重載函數,有如定義采用是專門用來處理SOCKET的接收數據的.

-(void) onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

在中間將其轉換成NSString進行顯示.

NSString* aStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

NSLog(@"===%@",aStr);

[aStr release];

6、TCP連接讀取制定長度的數據

socket連接,可能會讀取固定長度的字節

[socket readDataToLength: withTimeout :tag]

各方法的解析

-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;

發生錯誤,socket關閉,可以在call-back過程調用"unreadData"去取得socket的最后的數據字節,當連接的時候,該委托方法在??? onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調用

-(void)onSocketDidDisconnect:(ASyncSocket *)sock;

當socket由于或沒有錯誤而斷開連接,如果你想要在斷開連接后release socket,在此方法工作,而在onSocket:willDisconnectWithError 釋放則不安全

-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;

當產生一個socket去處理連接時調用,此方法會返回 線程上的run-loop 的新的socket和其應處理的委托,如果省略,則使用[NSRunLoop cunrrentRunLoop]

-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;

-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;

當socket連接正準備讀和寫的時候調用,host屬性是一個IP地址,而不是一個DNS 名稱

-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;

當socket已完成所要求的數據讀入內存時調用,如果有錯誤則不調用

-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;

當一個socket讀取數據,但尚未完成讀操作的時候調用,如果使用 readToData: or readToLength: 方法 會發生,可以被用來更新進度條等東西

-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;

當一個socket已完成請求數據的寫入時候調用

-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;

當一個socket寫入一些數據,但還沒有完成整個寫入時調用,它可以用來更新進度條等東西

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length

使用讀操作已超時但還沒完成時調用,此方法允許隨意延遲超時,如果返回一個正的時間間隔,讀取的超時將有一定量的擴展,如果不實現這個方法,或會像往常一樣返回一個負的時間間隔,elapsed參數是? 原超時的總和,加上先前通過這種方法添加的任何補充, length參數是 讀操作到目前為止已讀取的字節數, 注意,如果返回正數的話,這個方法可能被一個單獨的讀取多次調用

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;

如果一個寫操作已達到其超時但還沒完成時調用,同上

-(void)onSocketDidSecure:(AsyncSocket *)sock;

在socket成功完成ssl/tls協商時調用,此方法除非你使用提供startTLS方法時候才調用,

如果ssl/tls是無效的證書,socket將會立即關閉,onSocket:willDisconnectWithError:代理方法竟會與特定的ssl錯誤代碼一起調用

-(BOOL)canSafelySetDelegate

用來查看在改變它之前,是否帶有與當前的委托有懸而未決的業務(讀/寫)。當然,應在安全連接或接受委托之前改變委托

一旦接收或連接方法之一被調用,AsyncSocket實例會被鎖定,其他接收/連接方法在沒有先斷開socket不會被調用

如果嘗試失敗或超時,這些方法要么返回NO 要么調用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect

當傳入的連接被接受,AsyncSocket調用多個委托方法。這些方法按照時間順序排列:

1.onSocket:didAcceptNewSocket:

2.onSocket:wantsRunLoopForNewSocket:

3. onSocketWillConnect:

你的服務器的代碼將需要保留公認的socket(如果要接受它),最好的地方是要做到這一點可能在onSocket:didAcceptNewSocket:方法

在讀和寫流已經為新接受的socket設置,onSocket:didConnectToHost:port 方法將在適當的運行循環調用

多線程注意,如果要想通過實施onSocket:wantsRunLoopForNewSocket:,移動另一個新接受的socket去到另一個循環的socket。然后,應該在調用讀和寫或者startTLS方法前,等待直到onSocket:didConnectToHost:port:方法。否則讀和寫時間原定于不正確的runloop,混亂可能會隨之而來

-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;

告訴socket開始聽取和接受制定端口上的連接,當一個連接到來的時候,AsyncSocket實例將調用各種委托方法,socket將聽取所有可用的接口(wifi,以太網等)

-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;

連接給定的主機和端口,主機hostname可以是域名或者是Ip地址

-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;

連接到一個給定的地址,制定一個sockaddr結構包裹住一個NSData對象,例如,NSData對象從NSNetService的地址方法返回,如果有一個現有的sockaddr結構,可以將它轉換到一個NSData對象,像這樣:

struct sockaddr sa? -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];

struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];

-(void)disconnect;

立即斷開,任何未處理的讀或寫都將被丟棄

如果socket還沒有斷開,在這個方法返回之前,onSocketDidDisconnect 委托方法將會被立即調用

注意推薦釋放AsyncSocket實例的方式:

[asyncSocket setDelegate:nil];

[asyncSocket disconnect];

[asyncSocket release];

-(void)disconnectAfterReading;

在已經完成了所有懸而未決的讀取時 斷開,在調用之后,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入

- (NSString *)connectedHost;

- (UInt16)connectedPort;

- (NSString *)localHost;

- (UInt16)localPort;

返回本地和遠程主機和端口給連接的socket,如果沒有連接會返回nil或0,主機將會是一個IP地址

-(NSData *)connectedAddress

-(NSData *)localAddresss

返回本地和遠程的地址給連接的socket,指定一個socketaddr結構包裹在一個NSData對象

readData和writeData方法不會是block(它們是異步的)

當讀完成 onSocket:didReadData:withTag: 委托方法時調用

當寫完成 onSocket:didWriteDataWithTag: 委托方法時調用

可以選擇任何讀/寫操作的超時設置(為了不超時,使用負時間間隔。)

如果讀/寫操作超時,相應的 onSocket:shouldTimeout...委托方法被調用去選擇性地允許我們去延長超時

超時后,onSocket:willDisconnectWithError: 方法被調用,緊接著是 onSocketDidDisconnect

tag是為了方便,可以使用它作為數組的索引、步數、state id 、指針等

-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;

讀取socket上第一次成為可用的字節,如果timeout值是負數的,讀操作將不使用timeout

- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;

讀取socket上第一次成為可用的字節

字節將被追加到給定的字節緩沖區,從給定的偏移量開始

如果需要,給定的緩沖區大小將會自動增加

如果timeout值是負數的,讀操作將不使用timeout

如果緩沖區為空,socket會為我們創建一個緩沖區

如果bufferOffset是大于給定的緩沖區的長度,該方法將無用,委托將不會被調用

如果你傳遞一個緩沖區,當AsyncSocket在使用它的時候你不能以任何方式改變它

完成之后,onSocket:didReadData:withTag 返回的數據將是一個給定的緩沖區的子集

也就是說,它將會被引用到被追加的給定的緩沖區的字節

-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取給定的字節數,如果length為0,方法將無用,委托將不會被調用

-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取給定的字節數,在給定的偏移開始,字節將被追加到給定的字節緩沖區

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的"data"參數

如果傳遞0或者0長度的數據,"data"參數,該方法將無用,委托將不會被調用

從socket讀取一行,使用"data"參數作為行的分隔符 (如HTTP的CRLF)

注意,此方法不是字符集,因此,如果一個分隔符出現,它自然可以作為進行編碼的一部分,讀取將提前結束

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的“data”參數,在給定的偏移量開始,字節將被追加到給定的字節緩沖區。

從socket讀取一行,使用"data"參數作為行的分隔符(如HTTP的CRLF)

-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;

將data寫入socket,當完成的時候委托被調用

- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

返回當前讀或寫的進度,從0.0 到 1.0 或者 如果沒有讀/寫的時候返回Nan(使用isNan來檢查)

tag、done、total如果不為空的話,它們將會被填補

- (void)startTLS:(NSDictionary *)tlsSettings;

確保使用ssl/tls連接

這方法可被隨時調用,tls握手將會發生在所有懸而未決的讀/寫完成之后。這緊跟著一個發送依賴 StartTLS消息的協議選項,在排隊升級到TLS的同一時間,而不必等待寫入完成。在這個方法被調用后,任何讀寫計劃 將會發生在安全鏈接

對于可能的keys和TLS設置的值是有據可查的

一些可能的keys是:

* - kCFStreamSSLLevel

* - kCFStreamSSLAllowsExpiredCertificates

* - kCFStreamSSLAllowsExpiredRoots

* - kCFStreamSSLAllowsAnyRoot

* - kCFStreamSSLValidatesCertificateChain

* - kCFStreamSSLPeerName

* - kCFStreamSSLCertificates

* - kCFStreamSSLIsServer

如果你傳遞空或者空字典,將使用默認的字典

默認設置將檢查以確保由簽署可信的第三方證書機構和沒有過期的遠程連接的證書

然而,它不會驗證證書上的名字,除非你給它一個名字,通過kCFStreamSSLPeerName鍵去驗證

這對安全的影響是重要的理解

想象一下你正試圖創建一個到MySecureServer.com的安全連接,但因為一個被攻擊的DNS服務器,所以你的socket被定向到MaliciousServer.com

如果你只是使用默認設置,MaliciousServer.com 有一個有效的證書

默認設置將無法監測到任何問題,因為證書是有效的

在這個特殊的情況下,要妥善保護你的連接,應設置kCFStreamSSLPeerName性質為MySecureServer.com.

如果事前你不知道對等的名字的遠程主機(例如,你不確認它是domain.com" or "www.domain.com"),那么你可以使用默認設置來驗證證書,然后在獲得驗證的發行后使用X509Certificate類來驗證,X509Certificate類的CocoaAsyncSocket開源項目的一部分

-(void)enablePrebuffering

對于處理readDataToData請求,數據是必須從socket以小增量的方式讀取出來的

性能通過允許AsyncSocket去一次性讀大塊的數據和存儲任何一個小的內部緩沖區溢出的東西來大大提高

這被稱為預緩沖,就好像一些數據在你要求它之前就可能被讀取出來

如果你經常使用readDataToData,使用預緩沖會有更好的性能,尤其是在iphone上

默認的預緩沖狀態是由DEFAULT_PREBUFFERING 定義控制的,強烈建議設置其為yes

這方法存在一些預緩沖需要一些不可預見的原因被默認禁用的情況,這時,這種方法存在允許當就緒時,可輕松啟用預緩沖

-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;

當你創建一個AsyncSocket,它被添加到當前線程runloop

對于手動創建的socket,在線程上你打算使用它,它是最容易簡單的創建的線程上的socket

當一個新的socket被接受,委托方法 onSocket:wantsRunLoopForNewSocket 會被調用 允許你在一個單獨的線程上放置socket,這個工作最好結合在同一個線程池設計

如果,但是,在一個單獨的線程上,在之后的時間,你需要移動一個socket,這個方法可以用來完成任務

此方法必須從 當前運行的 線程/runloop 的socket 調用

注意:此方法調用后,所有進一步的方法應該從給定的runloop上調用這個對象

此外,所有委托調用將會發送到給定的runloop

- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;

- (BOOL)addRunLoopMode:(NSString *)runLoopMode;

- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;

允許你配置 socket 使用的 運行循環模式

運行循環模式設置默認是NSRunLoopCommonModes

如果你想你的socket 在其他模式下繼續操作,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes

可接受的socket將自動 繼承相同的運行循環模式就像偵聽socket

注意:NSRunLoopCommonModes 定義在10.5,對于之前的版本可使用 kCFRunLoopCommonModes

-(NSArray *)runLoopModes

返回當前正在運行的循環模式的AsyncSocket實例, run loop modes的默認設置是NSDefaultRunLoopMode

-(NSData *)unreadData;

一個錯誤的事件,在 onSocket:willDisconnectWithError: 將會被調用 去讀取留在socket上的任何數據

+ (NSData *)CRLFData;?? // 0x0D0A

各方法的解析

-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;

發生錯誤,socket關閉,可以在call-back過程調用"unreadData"去取得socket的最后的數據字節,當連接的時候,該委托方法在??? onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調用

-(void)onSocketDidDisconnect:(ASyncSocket *)sock;

當socket由于或沒有錯誤而斷開連接,如果你想要在斷開連接后release socket,在此方法工作,而在onSocket:willDisconnectWithError 釋放則不安全

-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;

當產生一個socket去處理連接時調用,此方法會返回 線程上的run-loop 的新的socket和其應處理的委托,如果省略,則使用[NSRunLoop cunrrentRunLoop]

-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;

-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;

當socket連接正準備讀和寫的時候調用,host屬性是一個IP地址,而不是一個DNS 名稱

-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;

當socket已完成所要求的數據讀入內存時調用,如果有錯誤則不調用

-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;

當一個socket讀取數據,但尚未完成讀操作的時候調用,如果使用 readToData: or readToLength: 方法 會發生,可以被用來更新進度條等東西

-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;

當一個socket已完成請求數據的寫入時候調用

-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;

當一個socket寫入一些數據,但還沒有完成整個寫入時調用,它可以用來更新進度條等東西

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length

使用讀操作已超時但還沒完成時調用,此方法允許隨意延遲超時,如果返回一個正的時間間隔,讀取的超時將有一定量的擴展,如果不實現這個方法,或會像往常一樣返回一個負的時間間隔,elapsed參數是? 原超時的總和,加上先前通過這種方法添加的任何補充, length參數是 讀操作到目前為止已讀取的字節數, 注意,如果返回正數的話,這個方法可能被一個單獨的讀取多次調用

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;

如果一個寫操作已達到其超時但還沒完成時調用,同上

-(void)onSocketDidSecure:(AsyncSocket *)sock;

在socket成功完成ssl/tls協商時調用,此方法除非你使用提供startTLS方法時候才調用,

如果ssl/tls是無效的證書,socket將會立即關閉,onSocket:willDisconnectWithError:代理方法竟會與特定的ssl錯誤代碼一起調用

-(BOOL)canSafelySetDelegate

用來查看在改變它之前,是否帶有與當前的委托有懸而未決的業務(讀/寫)。當然,應在安全連接或接受委托之前改變委托

一旦接收或連接方法之一被調用,AsyncSocket實例會被鎖定,其他接收/連接方法在沒有先斷開socket不會被調用

如果嘗試失敗或超時,這些方法要么返回NO 要么調用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect

當傳入的連接被接受,AsyncSocket調用多個委托方法。這些方法按照時間順序排列:

1.onSocket:didAcceptNewSocket:

2.onSocket:wantsRunLoopForNewSocket:

3. onSocketWillConnect:

你的服務器的代碼將需要保留公認的socket(如果要接受它),最好的地方是要做到這一點可能在onSocket:didAcceptNewSocket:方法

在讀和寫流已經為新接受的socket設置,onSocket:didConnectToHost:port 方法將在適當的運行循環調用

多線程注意,如果要想通過實施onSocket:wantsRunLoopForNewSocket:,移動另一個新接受的socket去到另一個循環的socket。然后,應該在調用讀和寫或者startTLS方法前,等待直到onSocket:didConnectToHost:port:方法。否則讀和寫時間原定于不正確的runloop,混亂可能會隨之而來

-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;

告訴socket開始聽取和接受制定端口上的連接,當一個連接到來的時候,AsyncSocket實例將調用各種委托方法,socket將聽取所有可用的接口(wifi,以太網等)

-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;

連接給定的主機和端口,主機hostname可以是域名或者是Ip地址

-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;

連接到一個給定的地址,制定一個sockaddr結構包裹住一個NSData對象,例如,NSData對象從NSNetService的地址方法返回,如果有一個現有的sockaddr結構,可以將它轉換到一個NSData對象,像這樣:

struct sockaddr sa? -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];

struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];

-(void)disconnect;

立即斷開,任何未處理的讀或寫都將被丟棄

如果socket還沒有斷開,在這個方法返回之前,onSocketDidDisconnect 委托方法將會被立即調用

注意推薦釋放AsyncSocket實例的方式:

[asyncSocket setDelegate:nil];

[asyncSocket disconnect];

[asyncSocket release];

-(void)disconnectAfterReading;

在已經完成了所有懸而未決的讀取時 斷開,在調用之后,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入

- (NSString *)connectedHost;

- (UInt16)connectedPort;

- (NSString *)localHost;

- (UInt16)localPort;

返回本地和遠程主機和端口給連接的socket,如果沒有連接會返回nil或0,主機將會是一個IP地址

-(NSData *)connectedAddress

-(NSData *)localAddresss

返回本地和遠程的地址給連接的socket,指定一個socketaddr結構包裹在一個NSData對象

readData和writeData方法不會是block(它們是異步的)

當讀完成 onSocket:didReadData:withTag: 委托方法時調用

當寫完成 onSocket:didWriteDataWithTag: 委托方法時調用

可以選擇任何讀/寫操作的超時設置(為了不超時,使用負時間間隔。)

如果讀/寫操作超時,相應的 onSocket:shouldTimeout...委托方法被調用去選擇性地允許我們去延長超時

超時后,onSocket:willDisconnectWithError: 方法被調用,緊接著是 onSocketDidDisconnect

tag是為了方便,可以使用它作為數組的索引、步數、state id 、指針等

-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;

讀取socket上第一次成為可用的字節,如果timeout值是負數的,讀操作將不使用timeout

- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;

讀取socket上第一次成為可用的字節

字節將被追加到給定的字節緩沖區,從給定的偏移量開始

如果需要,給定的緩沖區大小將會自動增加

如果timeout值是負數的,讀操作將不使用timeout

如果緩沖區為空,socket會為我們創建一個緩沖區

如果bufferOffset是大于給定的緩沖區的長度,該方法將無用,委托將不會被調用

如果你傳遞一個緩沖區,當AsyncSocket在使用它的時候你不能以任何方式改變它

完成之后,onSocket:didReadData:withTag 返回的數據將是一個給定的緩沖區的子集

也就是說,它將會被引用到被追加的給定的緩沖區的字節

-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取給定的字節數,如果length為0,方法將無用,委托將不會被調用

-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取給定的字節數,在給定的偏移開始,字節將被追加到給定的字節緩沖區

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的"data"參數

如果傳遞0或者0長度的數據,"data"參數,該方法將無用,委托將不會被調用

從socket讀取一行,使用"data"參數作為行的分隔符 (如HTTP的CRLF)

注意,此方法不是字符集,因此,如果一個分隔符出現,它自然可以作為進行編碼的一部分,讀取將提前結束

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的“data”參數,在給定的偏移量開始,字節將被追加到給定的字節緩沖區。

從socket讀取一行,使用"data"參數作為行的分隔符(如HTTP的CRLF)

-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;

將data寫入socket,當完成的時候委托被調用

- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

返回當前讀或寫的進度,從0.0 到 1.0 或者 如果沒有讀/寫的時候返回Nan(使用isNan來檢查)

tag、done、total如果不為空的話,它們將會被填補

- (void)startTLS:(NSDictionary *)tlsSettings;

確保使用ssl/tls連接

這方法可被隨時調用,tls握手將會發生在所有懸而未決的讀/寫完成之后。這緊跟著一個發送依賴 StartTLS消息的協議選項,在排隊升級到TLS的同一時間,而不必等待寫入完成。在這個方法被調用后,任何讀寫計劃 將會發生在安全鏈接

對于可能的keys和TLS設置的值是有據可查的

一些可能的keys是:

* - kCFStreamSSLLevel

* - kCFStreamSSLAllowsExpiredCertificates

* - kCFStreamSSLAllowsExpiredRoots

* - kCFStreamSSLAllowsAnyRoot

* - kCFStreamSSLValidatesCertificateChain

* - kCFStreamSSLPeerName

* - kCFStreamSSLCertificates

* - kCFStreamSSLIsServer

如果你傳遞空或者空字典,將使用默認的字典

默認設置將檢查以確保由簽署可信的第三方證書機構和沒有過期的遠程連接的證書

然而,它不會驗證證書上的名字,除非你給它一個名字,通過kCFStreamSSLPeerName鍵去驗證

這對安全的影響是重要的理解

想象一下你正試圖創建一個到MySecureServer.com的安全連接,但因為一個被攻擊的DNS服務器,所以你的socket被定向到MaliciousServer.com

如果你只是使用默認設置,MaliciousServer.com 有一個有效的證書

默認設置將無法監測到任何問題,因為證書是有效的

在這個特殊的情況下,要妥善保護你的連接,應設置kCFStreamSSLPeerName性質為MySecureServer.com.

如果事前你不知道對等的名字的遠程主機(例如,你不確認它是domain.com" or "www.domain.com"),那么你可以使用默認設置來驗證證書,然后在獲得驗證的發行后使用X509Certificate類來驗證,X509Certificate類的CocoaAsyncSocket開源項目的一部分

-(void)enablePrebuffering

對于處理readDataToData請求,數據是必須從socket以小增量的方式讀取出來的

性能通過允許AsyncSocket去一次性讀大塊的數據和存儲任何一個小的內部緩沖區溢出的東西來大大提高

這被稱為預緩沖,就好像一些數據在你要求它之前就可能被讀取出來

如果你經常使用readDataToData,使用預緩沖會有更好的性能,尤其是在iphone上

默認的預緩沖狀態是由DEFAULT_PREBUFFERING 定義控制的,強烈建議設置其為yes

這方法存在一些預緩沖需要一些不可預見的原因被默認禁用的情況,這時,這種方法存在允許當就緒時,可輕松啟用預緩沖

-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;

當你創建一個AsyncSocket,它被添加到當前線程runloop

對于手動創建的socket,在線程上你打算使用它,它是最容易簡單的創建的線程上的socket

當一個新的socket被接受,委托方法 onSocket:wantsRunLoopForNewSocket 會被調用 允許你在一個單獨的線程上放置socket,這個工作最好結合在同一個線程池設計

如果,但是,在一個單獨的線程上,在之后的時間,你需要移動一個socket,這個方法可以用來完成任務

此方法必須從 當前運行的 線程/runloop 的socket 調用

注意:此方法調用后,所有進一步的方法應該從給定的runloop上調用這個對象

此外,所有委托調用將會發送到給定的runloop

- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;

- (BOOL)addRunLoopMode:(NSString *)runLoopMode;

- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;

允許你配置 socket 使用的 運行循環模式

運行循環模式設置默認是NSRunLoopCommonModes

如果你想你的socket 在其他模式下繼續操作,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes

可接受的socket將自動 繼承相同的運行循環模式就像偵聽socket

注意:NSRunLoopCommonModes 定義在10.5,對于之前的版本可使用 kCFRunLoopCommonModes

-(NSArray *)runLoopModes

返回當前正在運行的循環模式的AsyncSocket實例, run loop modes的默認設置是NSDefaultRunLoopMode

-(NSData *)unreadData;

一個錯誤的事件,在 onSocket:willDisconnectWithError: 將會被調用 去讀取留在socket上的任何數據

+ (NSData *)CRLFData;?? // 0x0D0A

各方法的解析

-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;

發生錯誤,socket關閉,可以在call-back過程調用"unreadData"去取得socket的最后的數據字節,當連接的時候,該委托方法在??? onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調用

-(void)onSocketDidDisconnect:(ASyncSocket *)sock;

當socket由于或沒有錯誤而斷開連接,如果你想要在斷開連接后release socket,在此方法工作,而在onSocket:willDisconnectWithError 釋放則不安全

-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;

當產生一個socket去處理連接時調用,此方法會返回 線程上的run-loop 的新的socket和其應處理的委托,如果省略,則使用[NSRunLoop cunrrentRunLoop]

-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;

-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;

當socket連接正準備讀和寫的時候調用,host屬性是一個IP地址,而不是一個DNS 名稱

-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;

當socket已完成所要求的數據讀入內存時調用,如果有錯誤則不調用

-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;

當一個socket讀取數據,但尚未完成讀操作的時候調用,如果使用 readToData: or readToLength: 方法 會發生,可以被用來更新進度條等東西

-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;

當一個socket已完成請求數據的寫入時候調用

-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;

當一個socket寫入一些數據,但還沒有完成整個寫入時調用,它可以用來更新進度條等東西

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length

使用讀操作已超時但還沒完成時調用,此方法允許隨意延遲超時,如果返回一個正的時間間隔,讀取的超時將有一定量的擴展,如果不實現這個方法,或會像往常一樣返回一個負的時間間隔,elapsed參數是? 原超時的總和,加上先前通過這種方法添加的任何補充, length參數是 讀操作到目前為止已讀取的字節數, 注意,如果返回正數的話,這個方法可能被一個單獨的讀取多次調用

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;

如果一個寫操作已達到其超時但還沒完成時調用,同上

-(void)onSocketDidSecure:(AsyncSocket *)sock;

在socket成功完成ssl/tls協商時調用,此方法除非你使用提供startTLS方法時候才調用,

如果ssl/tls是無效的證書,socket將會立即關閉,onSocket:willDisconnectWithError:代理方法竟會與特定的ssl錯誤代碼一起調用

-(BOOL)canSafelySetDelegate

用來查看在改變它之前,是否帶有與當前的委托有懸而未決的業務(讀/寫)。當然,應在安全連接或接受委托之前改變委托

一旦接收或連接方法之一被調用,AsyncSocket實例會被鎖定,其他接收/連接方法在沒有先斷開socket不會被調用

如果嘗試失敗或超時,這些方法要么返回NO 要么調用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect

當傳入的連接被接受,AsyncSocket調用多個委托方法。這些方法按照時間順序排列:

1.onSocket:didAcceptNewSocket:

2.onSocket:wantsRunLoopForNewSocket:

3. onSocketWillConnect:

你的服務器的代碼將需要保留公認的socket(如果要接受它),最好的地方是要做到這一點可能在onSocket:didAcceptNewSocket:方法

在讀和寫流已經為新接受的socket設置,onSocket:didConnectToHost:port 方法將在適當的運行循環調用

多線程注意,如果要想通過實施onSocket:wantsRunLoopForNewSocket:,移動另一個新接受的socket去到另一個循環的socket。然后,應該在調用讀和寫或者startTLS方法前,等待直到onSocket:didConnectToHost:port:方法。否則讀和寫時間原定于不正確的runloop,混亂可能會隨之而來

-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;

告訴socket開始聽取和接受制定端口上的連接,當一個連接到來的時候,AsyncSocket實例將調用各種委托方法,socket將聽取所有可用的接口(wifi,以太網等)

-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;

連接給定的主機和端口,主機hostname可以是域名或者是Ip地址

-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;

連接到一個給定的地址,制定一個sockaddr結構包裹住一個NSData對象,例如,NSData對象從NSNetService的地址方法返回,如果有一個現有的sockaddr結構,可以將它轉換到一個NSData對象,像這樣:

struct sockaddr sa? -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];

struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];

-(void)disconnect;

立即斷開,任何未處理的讀或寫都將被丟棄

如果socket還沒有斷開,在這個方法返回之前,onSocketDidDisconnect 委托方法將會被立即調用

注意推薦釋放AsyncSocket實例的方式:

[asyncSocket setDelegate:nil];

[asyncSocket disconnect];

[asyncSocket release];

-(void)disconnectAfterReading;

在已經完成了所有懸而未決的讀取時 斷開,在調用之后,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入

- (NSString *)connectedHost;

- (UInt16)connectedPort;

- (NSString *)localHost;

- (UInt16)localPort;

返回本地和遠程主機和端口給連接的socket,如果沒有連接會返回nil或0,主機將會是一個IP地址

-(NSData *)connectedAddress

-(NSData *)localAddresss

返回本地和遠程的地址給連接的socket,指定一個socketaddr結構包裹在一個NSData對象

readData和writeData方法不會是block(它們是異步的)

當讀完成 onSocket:didReadData:withTag: 委托方法時調用

當寫完成 onSocket:didWriteDataWithTag: 委托方法時調用

可以選擇任何讀/寫操作的超時設置(為了不超時,使用負時間間隔。)

如果讀/寫操作超時,相應的 onSocket:shouldTimeout...委托方法被調用去選擇性地允許我們去延長超時

超時后,onSocket:willDisconnectWithError: 方法被調用,緊接著是 onSocketDidDisconnect

tag是為了方便,可以使用它作為數組的索引、步數、state id 、指針等

-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;

讀取socket上第一次成為可用的字節,如果timeout值是負數的,讀操作將不使用timeout

- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;

讀取socket上第一次成為可用的字節

字節將被追加到給定的字節緩沖區,從給定的偏移量開始

如果需要,給定的緩沖區大小將會自動增加

如果timeout值是負數的,讀操作將不使用timeout

如果緩沖區為空,socket會為我們創建一個緩沖區

如果bufferOffset是大于給定的緩沖區的長度,該方法將無用,委托將不會被調用

如果你傳遞一個緩沖區,當AsyncSocket在使用它的時候你不能以任何方式改變它

完成之后,onSocket:didReadData:withTag 返回的數據將是一個給定的緩沖區的子集

也就是說,它將會被引用到被追加的給定的緩沖區的字節

-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取給定的字節數,如果length為0,方法將無用,委托將不會被調用

-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取給定的字節數,在給定的偏移開始,字節將被追加到給定的字節緩沖區

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的"data"參數

如果傳遞0或者0長度的數據,"data"參數,該方法將無用,委托將不會被調用

從socket讀取一行,使用"data"參數作為行的分隔符 (如HTTP的CRLF)

注意,此方法不是字符集,因此,如果一個分隔符出現,它自然可以作為進行編碼的一部分,讀取將提前結束

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的“data”參數,在給定的偏移量開始,字節將被追加到給定的字節緩沖區。

從socket讀取一行,使用"data"參數作為行的分隔符(如HTTP的CRLF)

-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;

將data寫入socket,當完成的時候委托被調用

- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

返回當前讀或寫的進度,從0.0 到 1.0 或者 如果沒有讀/寫的時候返回Nan(使用isNan來檢查)

tag、done、total如果不為空的話,它們將會被填補

- (void)startTLS:(NSDictionary *)tlsSettings;

確保使用ssl/tls連接

這方法可被隨時調用,tls握手將會發生在所有懸而未決的讀/寫完成之后。這緊跟著一個發送依賴 StartTLS消息的協議選項,在排隊升級到TLS的同一時間,而不必等待寫入完成。在這個方法被調用后,任何讀寫計劃 將會發生在安全鏈接

對于可能的keys和TLS設置的值是有據可查的

一些可能的keys是:

* - kCFStreamSSLLevel

* - kCFStreamSSLAllowsExpiredCertificates

* - kCFStreamSSLAllowsExpiredRoots

* - kCFStreamSSLAllowsAnyRoot

* - kCFStreamSSLValidatesCertificateChain

* - kCFStreamSSLPeerName

* - kCFStreamSSLCertificates

* - kCFStreamSSLIsServer

如果你傳遞空或者空字典,將使用默認的字典

默認設置將檢查以確保由簽署可信的第三方證書機構和沒有過期的遠程連接的證書

然而,它不會驗證證書上的名字,除非你給它一個名字,通過kCFStreamSSLPeerName鍵去驗證

這對安全的影響是重要的理解

想象一下你正試圖創建一個到MySecureServer.com的安全連接,但因為一個被攻擊的DNS服務器,所以你的socket被定向到MaliciousServer.com

如果你只是使用默認設置,MaliciousServer.com 有一個有效的證書

默認設置將無法監測到任何問題,因為證書是有效的

在這個特殊的情況下,要妥善保護你的連接,應設置kCFStreamSSLPeerName性質為MySecureServer.com.

如果事前你不知道對等的名字的遠程主機(例如,你不確認它是domain.com" or "www.domain.com"),那么你可以使用默認設置來驗證證書,然后在獲得驗證的發行后使用X509Certificate類來驗證,X509Certificate類的CocoaAsyncSocket開源項目的一部分

-(void)enablePrebuffering

對于處理readDataToData請求,數據是必須從socket以小增量的方式讀取出來的

性能通過允許AsyncSocket去一次性讀大塊的數據和存儲任何一個小的內部緩沖區溢出的東西來大大提高

這被稱為預緩沖,就好像一些數據在你要求它之前就可能被讀取出來

如果你經常使用readDataToData,使用預緩沖會有更好的性能,尤其是在iphone上

默認的預緩沖狀態是由DEFAULT_PREBUFFERING 定義控制的,強烈建議設置其為yes

這方法存在一些預緩沖需要一些不可預見的原因被默認禁用的情況,這時,這種方法存在允許當就緒時,可輕松啟用預緩沖

-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;

當你創建一個AsyncSocket,它被添加到當前線程runloop

對于手動創建的socket,在線程上你打算使用它,它是最容易簡單的創建的線程上的socket

當一個新的socket被接受,委托方法 onSocket:wantsRunLoopForNewSocket 會被調用 允許你在一個單獨的線程上放置socket,這個工作最好結合在同一個線程池設計

如果,但是,在一個單獨的線程上,在之后的時間,你需要移動一個socket,這個方法可以用來完成任務

此方法必須從 當前運行的 線程/runloop 的socket 調用

注意:此方法調用后,所有進一步的方法應該從給定的runloop上調用這個對象

此外,所有委托調用將會發送到給定的runloop

- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;

- (BOOL)addRunLoopMode:(NSString *)runLoopMode;

- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;

允許你配置 socket 使用的 運行循環模式

運行循環模式設置默認是NSRunLoopCommonModes

如果你想你的socket 在其他模式下繼續操作,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes

可接受的socket將自動 繼承相同的運行循環模式就像偵聽socket

注意:NSRunLoopCommonModes 定義在10.5,對于之前的版本可使用 kCFRunLoopCommonModes

-(NSArray *)runLoopModes

返回當前正在運行的循環模式的AsyncSocket實例, run loop modes的默認設置是NSDefaultRunLoopMode

-(NSData *)unreadData;

一個錯誤的事件,在 onSocket:willDisconnectWithError: 將會被調用 去讀取留在socket上的任何數據

+ (NSData *)CRLFData;?? // 0x0D0A

各方法的解析

-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;

發生錯誤,socket關閉,可以在call-back過程調用"unreadData"去取得socket的最后的數據字節,當連接的時候,該委托方法在??? onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調用

-(void)onSocketDidDisconnect:(ASyncSocket *)sock;

當socket由于或沒有錯誤而斷開連接,如果你想要在斷開連接后release socket,在此方法工作,而在onSocket:willDisconnectWithError 釋放則不安全

-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;

當產生一個socket去處理連接時調用,此方法會返回 線程上的run-loop 的新的socket和其應處理的委托,如果省略,則使用[NSRunLoop cunrrentRunLoop]

-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;

-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;

當socket連接正準備讀和寫的時候調用,host屬性是一個IP地址,而不是一個DNS 名稱

-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;

當socket已完成所要求的數據讀入內存時調用,如果有錯誤則不調用

-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;

當一個socket讀取數據,但尚未完成讀操作的時候調用,如果使用 readToData: or readToLength: 方法 會發生,可以被用來更新進度條等東西

-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;

當一個socket已完成請求數據的寫入時候調用

-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;

當一個socket寫入一些數據,但還沒有完成整個寫入時調用,它可以用來更新進度條等東西

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length

使用讀操作已超時但還沒完成時調用,此方法允許隨意延遲超時,如果返回一個正的時間間隔,讀取的超時將有一定量的擴展,如果不實現這個方法,或會像往常一樣返回一個負的時間間隔,elapsed參數是? 原超時的總和,加上先前通過這種方法添加的任何補充, length參數是 讀操作到目前為止已讀取的字節數, 注意,如果返回正數的話,這個方法可能被一個單獨的讀取多次調用

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;

如果一個寫操作已達到其超時但還沒完成時調用,同上

-(void)onSocketDidSecure:(AsyncSocket *)sock;

在socket成功完成ssl/tls協商時調用,此方法除非你使用提供startTLS方法時候才調用,

如果ssl/tls是無效的證書,socket將會立即關閉,onSocket:willDisconnectWithError:代理方法竟會與特定的ssl錯誤代碼一起調用

-(BOOL)canSafelySetDelegate

用來查看在改變它之前,是否帶有與當前的委托有懸而未決的業務(讀/寫)。當然,應在安全連接或接受委托之前改變委托

一旦接收或連接方法之一被調用,AsyncSocket實例會被鎖定,其他接收/連接方法在沒有先斷開socket不會被調用

如果嘗試失敗或超時,這些方法要么返回NO 要么調用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect

當傳入的連接被接受,AsyncSocket調用多個委托方法。這些方法按照時間順序排列:

1.onSocket:didAcceptNewSocket:

2.onSocket:wantsRunLoopForNewSocket:

3. onSocketWillConnect:

你的服務器的代碼將需要保留公認的socket(如果要接受它),最好的地方是要做到這一點可能在onSocket:didAcceptNewSocket:方法

在讀和寫流已經為新接受的socket設置,onSocket:didConnectToHost:port 方法將在適當的運行循環調用

多線程注意,如果要想通過實施onSocket:wantsRunLoopForNewSocket:,移動另一個新接受的socket去到另一個循環的socket。然后,應該在調用讀和寫或者startTLS方法前,等待直到onSocket:didConnectToHost:port:方法。否則讀和寫時間原定于不正確的runloop,混亂可能會隨之而來

-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;

告訴socket開始聽取和接受制定端口上的連接,當一個連接到來的時候,AsyncSocket實例將調用各種委托方法,socket將聽取所有可用的接口(wifi,以太網等)

-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;

連接給定的主機和端口,主機hostname可以是域名或者是Ip地址

-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;

連接到一個給定的地址,制定一個sockaddr結構包裹住一個NSData對象,例如,NSData對象從NSNetService的地址方法返回,如果有一個現有的sockaddr結構,可以將它轉換到一個NSData對象,像這樣:

struct sockaddr sa? -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];

struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];

-(void)disconnect;

立即斷開,任何未處理的讀或寫都將被丟棄

如果socket還沒有斷開,在這個方法返回之前,onSocketDidDisconnect 委托方法將會被立即調用

注意推薦釋放AsyncSocket實例的方式:

[asyncSocket setDelegate:nil];

[asyncSocket disconnect];

[asyncSocket release];

-(void)disconnectAfterReading;

在已經完成了所有懸而未決的讀取時 斷開,在調用之后,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入

- (NSString *)connectedHost;

- (UInt16)connectedPort;

- (NSString *)localHost;

- (UInt16)localPort;

返回本地和遠程主機和端口給連接的socket,如果沒有連接會返回nil或0,主機將會是一個IP地址

-(NSData *)connectedAddress

-(NSData *)localAddresss

返回本地和遠程的地址給連接的socket,指定一個socketaddr結構包裹在一個NSData對象

readData和writeData方法不會是block(它們是異步的)

當讀完成 onSocket:didReadData:withTag: 委托方法時調用

當寫完成 onSocket:didWriteDataWithTag: 委托方法時調用

可以選擇任何讀/寫操作的超時設置(為了不超時,使用負時間間隔。)

如果讀/寫操作超時,相應的 onSocket:shouldTimeout...委托方法被調用去選擇性地允許我們去延長超時

超時后,onSocket:willDisconnectWithError: 方法被調用,緊接著是 onSocketDidDisconnect

tag是為了方便,可以使用它作為數組的索引、步數、state id 、指針等

-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;

讀取socket上第一次成為可用的字節,如果timeout值是負數的,讀操作將不使用timeout

- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;

讀取socket上第一次成為可用的字節

字節將被追加到給定的字節緩沖區,從給定的偏移量開始

如果需要,給定的緩沖區大小將會自動增加

如果timeout值是負數的,讀操作將不使用timeout

如果緩沖區為空,socket會為我們創建一個緩沖區

如果bufferOffset是大于給定的緩沖區的長度,該方法將無用,委托將不會被調用

如果你傳遞一個緩沖區,當AsyncSocket在使用它的時候你不能以任何方式改變它

完成之后,onSocket:didReadData:withTag 返回的數據將是一個給定的緩沖區的子集

也就是說,它將會被引用到被追加的給定的緩沖區的字節

-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取給定的字節數,如果length為0,方法將無用,委托將不會被調用

-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取給定的字節數,在給定的偏移開始,字節將被追加到給定的字節緩沖區

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的"data"參數

如果傳遞0或者0長度的數據,"data"參數,該方法將無用,委托將不會被調用

從socket讀取一行,使用"data"參數作為行的分隔符 (如HTTP的CRLF)

注意,此方法不是字符集,因此,如果一個分隔符出現,它自然可以作為進行編碼的一部分,讀取將提前結束

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的“data”參數,在給定的偏移量開始,字節將被追加到給定的字節緩沖區。

從socket讀取一行,使用"data"參數作為行的分隔符(如HTTP的CRLF)

-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;

將data寫入socket,當完成的時候委托被調用

- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

返回當前讀或寫的進度,從0.0 到 1.0 或者 如果沒有讀/寫的時候返回Nan(使用isNan來檢查)

tag、done、total如果不為空的話,它們將會被填補

- (void)startTLS:(NSDictionary *)tlsSettings;

確保使用ssl/tls連接

這方法可被隨時調用,tls握手將會發生在所有懸而未決的讀/寫完成之后。這緊跟著一個發送依賴 StartTLS消息的協議選項,在排隊升級到TLS的同一時間,而不必等待寫入完成。在這個方法被調用后,任何讀寫計劃 將會發生在安全鏈接

對于可能的keys和TLS設置的值是有據可查的

一些可能的keys是:

* - kCFStreamSSLLevel

* - kCFStreamSSLAllowsExpiredCertificates

* - kCFStreamSSLAllowsExpiredRoots

* - kCFStreamSSLAllowsAnyRoot

* - kCFStreamSSLValidatesCertificateChain

* - kCFStreamSSLPeerName

* - kCFStreamSSLCertificates

* - kCFStreamSSLIsServer

如果你傳遞空或者空字典,將使用默認的字典

默認設置將檢查以確保由簽署可信的第三方證書機構和沒有過期的遠程連接的證書

然而,它不會驗證證書上的名字,除非你給它一個名字,通過kCFStreamSSLPeerName鍵去驗證

這對安全的影響是重要的理解

想象一下你正試圖創建一個到MySecureServer.com的安全連接,但因為一個被攻擊的DNS服務器,所以你的socket被定向到MaliciousServer.com

如果你只是使用默認設置,MaliciousServer.com 有一個有效的證書

默認設置將無法監測到任何問題,因為證書是有效的

在這個特殊的情況下,要妥善保護你的連接,應設置kCFStreamSSLPeerName性質為MySecureServer.com.

如果事前你不知道對等的名字的遠程主機(例如,你不確認它是domain.com" or "www.domain.com"),那么你可以使用默認設置來驗證證書,然后在獲得驗證的發行后使用X509Certificate類來驗證,X509Certificate類的CocoaAsyncSocket開源項目的一部分

-(void)enablePrebuffering

對于處理readDataToData請求,數據是必須從socket以小增量的方式讀取出來的

性能通過允許AsyncSocket去一次性讀大塊的數據和存儲任何一個小的內部緩沖區溢出的東西來大大提高

這被稱為預緩沖,就好像一些數據在你要求它之前就可能被讀取出來

如果你經常使用readDataToData,使用預緩沖會有更好的性能,尤其是在iphone上

默認的預緩沖狀態是由DEFAULT_PREBUFFERING 定義控制的,強烈建議設置其為yes

這方法存在一些預緩沖需要一些不可預見的原因被默認禁用的情況,這時,這種方法存在允許當就緒時,可輕松啟用預緩沖

-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;

當你創建一個AsyncSocket,它被添加到當前線程runloop

對于手動創建的socket,在線程上你打算使用它,它是最容易簡單的創建的線程上的socket

當一個新的socket被接受,委托方法 onSocket:wantsRunLoopForNewSocket 會被調用 允許你在一個單獨的線程上放置socket,這個工作最好結合在同一個線程池設計

如果,但是,在一個單獨的線程上,在之后的時間,你需要移動一個socket,這個方法可以用來完成任務

此方法必須從 當前運行的 線程/runloop 的socket 調用

注意:此方法調用后,所有進一步的方法應該從給定的runloop上調用這個對象

此外,所有委托調用將會發送到給定的runloop

- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;

- (BOOL)addRunLoopMode:(NSString *)runLoopMode;

- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;

允許你配置 socket 使用的 運行循環模式

運行循環模式設置默認是NSRunLoopCommonModes

如果你想你的socket 在其他模式下繼續操作,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes

可接受的socket將自動 繼承相同的運行循環模式就像偵聽socket

注意:NSRunLoopCommonModes 定義在10.5,對于之前的版本可使用 kCFRunLoopCommonModes

-(NSArray *)runLoopModes

返回當前正在運行的循環模式的AsyncSocket實例, run loop modes的默認設置是NSDefaultRunLoopMode

-(NSData *)unreadData;

一個錯誤的事件,在 onSocket:willDisconnectWithError: 將會被調用 去讀取留在socket上的任何數據

+ (NSData *)CRLFData;?? // 0x0D0A

各方法的解析

-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;

發生錯誤,socket關閉,可以在call-back過程調用"unreadData"去取得socket的最后的數據字節,當連接的時候,該委托方法在??? onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調用

-(void)onSocketDidDisconnect:(ASyncSocket *)sock;

當socket由于或沒有錯誤而斷開連接,如果你想要在斷開連接后release socket,在此方法工作,而在onSocket:willDisconnectWithError 釋放則不安全

-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;

當產生一個socket去處理連接時調用,此方法會返回 線程上的run-loop 的新的socket和其應處理的委托,如果省略,則使用[NSRunLoop cunrrentRunLoop]

-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;

-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;

當socket連接正準備讀和寫的時候調用,host屬性是一個IP地址,而不是一個DNS 名稱

-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;

當socket已完成所要求的數據讀入內存時調用,如果有錯誤則不調用

-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;

當一個socket讀取數據,但尚未完成讀操作的時候調用,如果使用 readToData: or readToLength: 方法 會發生,可以被用來更新進度條等東西

-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;

當一個socket已完成請求數據的寫入時候調用

-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;

當一個socket寫入一些數據,但還沒有完成整個寫入時調用,它可以用來更新進度條等東西

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length

使用讀操作已超時但還沒完成時調用,此方法允許隨意延遲超時,如果返回一個正的時間間隔,讀取的超時將有一定量的擴展,如果不實現這個方法,或會像往常一樣返回一個負的時間間隔,elapsed參數是? 原超時的總和,加上先前通過這種方法添加的任何補充, length參數是 讀操作到目前為止已讀取的字節數, 注意,如果返回正數的話,這個方法可能被一個單獨的讀取多次調用

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;

如果一個寫操作已達到其超時但還沒完成時調用,同上

-(void)onSocketDidSecure:(AsyncSocket *)sock;

在socket成功完成ssl/tls協商時調用,此方法除非你使用提供startTLS方法時候才調用,

如果ssl/tls是無效的證書,socket將會立即關閉,onSocket:willDisconnectWithError:代理方法竟會與特定的ssl錯誤代碼一起調用

-(BOOL)canSafelySetDelegate

用來查看在改變它之前,是否帶有與當前的委托有懸而未決的業務(讀/寫)。當然,應在安全連接或接受委托之前改變委托

一旦接收或連接方法之一被調用,AsyncSocket實例會被鎖定,其他接收/連接方法在沒有先斷開socket不會被調用

如果嘗試失敗或超時,這些方法要么返回NO 要么調用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect

當傳入的連接被接受,AsyncSocket調用多個委托方法。這些方法按照時間順序排列:

1.onSocket:didAcceptNewSocket:

2.onSocket:wantsRunLoopForNewSocket:

3. onSocketWillConnect:

你的服務器的代碼將需要保留公認的socket(如果要接受它),最好的地方是要做到這一點可能在onSocket:didAcceptNewSocket:方法

在讀和寫流已經為新接受的socket設置,onSocket:didConnectToHost:port 方法將在適當的運行循環調用

多線程注意,如果要想通過實施onSocket:wantsRunLoopForNewSocket:,移動另一個新接受的socket去到另一個循環的socket。然后,應該在調用讀和寫或者startTLS方法前,等待直到onSocket:didConnectToHost:port:方法。否則讀和寫時間原定于不正確的runloop,混亂可能會隨之而來

-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;

告訴socket開始聽取和接受制定端口上的連接,當一個連接到來的時候,AsyncSocket實例將調用各種委托方法,socket將聽取所有可用的接口(wifi,以太網等)

-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;

連接給定的主機和端口,主機hostname可以是域名或者是Ip地址

-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;

連接到一個給定的地址,制定一個sockaddr結構包裹住一個NSData對象,例如,NSData對象從NSNetService的地址方法返回,如果有一個現有的sockaddr結構,可以將它轉換到一個NSData對象,像這樣:

struct sockaddr sa? -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];

struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];

-(void)disconnect;

立即斷開,任何未處理的讀或寫都將被丟棄

如果socket還沒有斷開,在這個方法返回之前,onSocketDidDisconnect 委托方法將會被立即調用

注意推薦釋放AsyncSocket實例的方式:

[asyncSocket setDelegate:nil];

[asyncSocket disconnect];

[asyncSocket release];

-(void)disconnectAfterReading;

在已經完成了所有懸而未決的讀取時 斷開,在調用之后,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入

- (NSString *)connectedHost;

- (UInt16)connectedPort;

- (NSString *)localHost;

- (UInt16)localPort;

返回本地和遠程主機和端口給連接的socket,如果沒有連接會返回nil或0,主機將會是一個IP地址

-(NSData *)connectedAddress

-(NSData *)localAddresss

返回本地和遠程的地址給連接的socket,指定一個socketaddr結構包裹在一個NSData對象

readData和writeData方法不會是block(它們是異步的)

當讀完成 onSocket:didReadData:withTag: 委托方法時調用

當寫完成 onSocket:didWriteDataWithTag: 委托方法時調用

可以選擇任何讀/寫操作的超時設置(為了不超時,使用負時間間隔。)

如果讀/寫操作超時,相應的 onSocket:shouldTimeout...委托方法被調用去選擇性地允許我們去延長超時

超時后,onSocket:willDisconnectWithError: 方法被調用,緊接著是 onSocketDidDisconnect

tag是為了方便,可以使用它作為數組的索引、步數、state id 、指針等

-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;

讀取socket上第一次成為可用的字節,如果timeout值是負數的,讀操作將不使用timeout

- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;

讀取socket上第一次成為可用的字節

字節將被追加到給定的字節緩沖區,從給定的偏移量開始

如果需要,給定的緩沖區大小將會自動增加

如果timeout值是負數的,讀操作將不使用timeout

如果緩沖區為空,socket會為我們創建一個緩沖區

如果bufferOffset是大于給定的緩沖區的長度,該方法將無用,委托將不會被調用

如果你傳遞一個緩沖區,當AsyncSocket在使用它的時候你不能以任何方式改變它

完成之后,onSocket:didReadData:withTag 返回的數據將是一個給定的緩沖區的子集

也就是說,它將會被引用到被追加的給定的緩沖區的字節

-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取給定的字節數,如果length為0,方法將無用,委托將不會被調用

-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取給定的字節數,在給定的偏移開始,字節將被追加到給定的字節緩沖區

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的"data"參數

如果傳遞0或者0長度的數據,"data"參數,該方法將無用,委托將不會被調用

從socket讀取一行,使用"data"參數作為行的分隔符 (如HTTP的CRLF)

注意,此方法不是字符集,因此,如果一個分隔符出現,它自然可以作為進行編碼的一部分,讀取將提前結束

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的“data”參數,在給定的偏移量開始,字節將被追加到給定的字節緩沖區。

從socket讀取一行,使用"data"參數作為行的分隔符(如HTTP的CRLF)

-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;

將data寫入socket,當完成的時候委托被調用

- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

返回當前讀或寫的進度,從0.0 到 1.0 或者 如果沒有讀/寫的時候返回Nan(使用isNan來檢查)

tag、done、total如果不為空的話,它們將會被填補

- (void)startTLS:(NSDictionary *)tlsSettings;

確保使用ssl/tls連接

這方法可被隨時調用,tls握手將會發生在所有懸而未決的讀/寫完成之后。這緊跟著一個發送依賴 StartTLS消息的協議選項,在排隊升級到TLS的同一時間,而不必等待寫入完成。在這個方法被調用后,任何讀寫計劃 將會發生在安全鏈接

對于可能的keys和TLS設置的值是有據可查的

一些可能的keys是:

* - kCFStreamSSLLevel

* - kCFStreamSSLAllowsExpiredCertificates

* - kCFStreamSSLAllowsExpiredRoots

* - kCFStreamSSLAllowsAnyRoot

* - kCFStreamSSLValidatesCertificateChain

* - kCFStreamSSLPeerName

* - kCFStreamSSLCertificates

* - kCFStreamSSLIsServer

如果你傳遞空或者空字典,將使用默認的字典

默認設置將檢查以確保由簽署可信的第三方證書機構和沒有過期的遠程連接的證書

然而,它不會驗證證書上的名字,除非你給它一個名字,通過kCFStreamSSLPeerName鍵去驗證

這對安全的影響是重要的理解

想象一下你正試圖創建一個到MySecureServer.com的安全連接,但因為一個被攻擊的DNS服務器,所以你的socket被定向到MaliciousServer.com

如果你只是使用默認設置,MaliciousServer.com 有一個有效的證書

默認設置將無法監測到任何問題,因為證書是有效的

在這個特殊的情況下,要妥善保護你的連接,應設置kCFStreamSSLPeerName性質為MySecureServer.com.

如果事前你不知道對等的名字的遠程主機(例如,你不確認它是domain.com" or "www.domain.com"),那么你可以使用默認設置來驗證證書,然后在獲得驗證的發行后使用X509Certificate類來驗證,X509Certificate類的CocoaAsyncSocket開源項目的一部分

-(void)enablePrebuffering

對于處理readDataToData請求,數據是必須從socket以小增量的方式讀取出來的

性能通過允許AsyncSocket去一次性讀大塊的數據和存儲任何一個小的內部緩沖區溢出的東西來大大提高

這被稱為預緩沖,就好像一些數據在你要求它之前就可能被讀取出來

如果你經常使用readDataToData,使用預緩沖會有更好的性能,尤其是在iphone上

默認的預緩沖狀態是由DEFAULT_PREBUFFERING 定義控制的,強烈建議設置其為yes

這方法存在一些預緩沖需要一些不可預見的原因被默認禁用的情況,這時,這種方法存在允許當就緒時,可輕松啟用預緩沖

-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;

當你創建一個AsyncSocket,它被添加到當前線程runloop

對于手動創建的socket,在線程上你打算使用它,它是最容易簡單的創建的線程上的socket

當一個新的socket被接受,委托方法 onSocket:wantsRunLoopForNewSocket 會被調用 允許你在一個單獨的線程上放置socket,這個工作最好結合在同一個線程池設計

如果,但是,在一個單獨的線程上,在之后的時間,你需要移動一個socket,這個方法可以用來完成任務

此方法必須從 當前運行的 線程/runloop 的socket 調用

注意:此方法調用后,所有進一步的方法應該從給定的runloop上調用這個對象

此外,所有委托調用將會發送到給定的runloop

- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;

- (BOOL)addRunLoopMode:(NSString *)runLoopMode;

- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;

允許你配置 socket 使用的 運行循環模式

運行循環模式設置默認是NSRunLoopCommonModes

如果你想你的socket 在其他模式下繼續操作,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes

可接受的socket將自動 繼承相同的運行循環模式就像偵聽socket

注意:NSRunLoopCommonModes 定義在10.5,對于之前的版本可使用 kCFRunLoopCommonModes

-(NSArray *)runLoopModes

返回當前正在運行的循環模式的AsyncSocket實例, run loop modes的默認設置是NSDefaultRunLoopMode

-(NSData *)unreadData;

一個錯誤的事件,在 onSocket:willDisconnectWithError: 將會被調用 去讀取留在socket上的任何數據

+ (NSData *)CRLFData;?? // 0x0D0A

各方法的解析

-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;

發生錯誤,socket關閉,可以在call-back過程調用"unreadData"去取得socket的最后的數據字節,當連接的時候,該委托方法在??? onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調用

發生錯誤,socket關閉,可以在call-back過程調用"unreadData"去取得socket的最后的數據字節,當連接的時候,該委托方法在??? onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調用

-(void)onSocketDidDisconnect:(ASyncSocket *)sock;

當socket由于或沒有錯誤而斷開連接,如果你想要在斷開連接后release socket,在此方法工作,而在onSocket:willDisconnectWithError 釋放則不安全

-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;

當產生一個socket去處理連接時調用,此方法會返回 線程上的run-loop 的新的socket和其應處理的委托,如果省略,則使用[NSRunLoop cunrrentRunLoop]

當產生一個socket去處理連接時調用,此方法會返回 線程上的run-loop 的新的socket和其應處理的委托,如果省略,則使用[NSRunLoop cunrrentRunLoop]

-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;

-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;

當socket連接正準備讀和寫的時候調用,host屬性是一個IP地址,而不是一個DNS 名稱

-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;

當socket已完成所要求的數據讀入內存時調用,如果有錯誤則不調用

-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;

當一個socket讀取數據,但尚未完成讀操作的時候調用,如果使用 readToData: or readToLength: 方法 會發生,可以被用來更新進度條等東西

-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;

當一個socket已完成請求數據的寫入時候調用

-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;

當一個socket寫入一些數據,但還沒有完成整個寫入時調用,它可以用來更新進度條等東西

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length

使用讀操作已超時但還沒完成時調用,此方法允許隨意延遲超時,如果返回一個正的時間間隔,讀取的超時將有一定量的擴展,如果不實現這個方法,或會像往常一樣返回一個負的時間間隔,elapsed參數是? 原超時的總和,加上先前通過這種方法添加的任何補充, length參數是 讀操作到目前為止已讀取的字節數, 注意,如果返回正數的話,這個方法可能被一個單獨的讀取多次調用

-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;

如果一個寫操作已達到其超時但還沒完成時調用,同上

-(void)onSocketDidSecure:(AsyncSocket *)sock;

在socket成功完成ssl/tls協商時調用,此方法除非你使用提供startTLS方法時候才調用,

如果ssl/tls是無效的證書,socket將會立即關閉,onSocket:willDisconnectWithError:代理方法竟會與特定的ssl錯誤代碼一起調用

-(BOOL)canSafelySetDelegate

用來查看在改變它之前,是否帶有與當前的委托有懸而未決的業務(讀/寫)。當然,應在安全連接或接受委托之前改變委托

一旦接收或連接方法之一被調用,AsyncSocket實例會被鎖定,其他接收/連接方法在沒有先斷開socket不會被調用

如果嘗試失敗或超時,這些方法要么返回NO 要么調用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect

當傳入的連接被接受,AsyncSocket調用多個委托方法。這些方法按照時間順序排列:

1.onSocket:didAcceptNewSocket:

2.onSocket:wantsRunLoopForNewSocket:

3. onSocketWillConnect:

你的服務器的代碼將需要保留公認的socket(如果要接受它),最好的地方是要做到這一點可能在onSocket:didAcceptNewSocket:方法

在讀和寫流已經為新接受的socket設置,onSocket:didConnectToHost:port 方法將在適當的運行循環調用

多線程注意,如果要想通過實施onSocket:wantsRunLoopForNewSocket:,移動另一個新接受的socket去到另一個循環的socket。然后,應該在調用讀和寫或者startTLS方法前,等待直到onSocket:didConnectToHost:port:方法。否則讀和寫時間原定于不正確的runloop,混亂可能會隨之而來

-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;

告訴socket開始聽取和接受制定端口上的連接,當一個連接到來的時候,AsyncSocket實例將調用各種委托方法,socket將聽取所有可用的接口(wifi,以太網等)

-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;

連接給定的主機和端口,主機hostname可以是域名或者是Ip地址

-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;

連接到一個給定的地址,制定一個sockaddr結構包裹住一個NSData對象,例如,NSData對象從NSNetService的地址方法返回,如果有一個現有的sockaddr結構,可以將它轉換到一個NSData對象,像這樣:

struct sockaddr sa? -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];

struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];

-(void)disconnect;

立即斷開,任何未處理的讀或寫都將被丟棄

如果socket還沒有斷開,在這個方法返回之前,onSocketDidDisconnect 委托方法將會被立即調用

注意推薦釋放AsyncSocket實例的方式:

[asyncSocket setDelegate:nil];

[asyncSocket disconnect];

[asyncSocket release];

-(void)disconnectAfterReading;

在已經完成了所有懸而未決的讀取時 斷開,在調用之后,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入

- (NSString *)connectedHost;

- (UInt16)connectedPort;

- (NSString *)localHost;

- (UInt16)localPort;

返回本地和遠程主機和端口給連接的socket,如果沒有連接會返回nil或0,主機將會是一個IP地址

-(NSData *)connectedAddress

-(NSData *)localAddresss

返回本地和遠程的地址給連接的socket,指定一個socketaddr結構包裹在一個NSData對象

readData和writeData方法不會是block(它們是異步的)

當讀完成 onSocket:didReadData:withTag: 委托方法時調用

當寫完成 onSocket:didWriteDataWithTag: 委托方法時調用

可以選擇任何讀/寫操作的超時設置(為了不超時,使用負時間間隔。)

如果讀/寫操作超時,相應的 onSocket:shouldTimeout...委托方法被調用去選擇性地允許我們去延長超時

超時后,onSocket:willDisconnectWithError: 方法被調用,緊接著是 onSocketDidDisconnect

tag是為了方便,可以使用它作為數組的索引、步數、state id 、指針等

-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;

讀取socket上第一次成為可用的字節,如果timeout值是負數的,讀操作將不使用timeout

- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;

讀取socket上第一次成為可用的字節

字節將被追加到給定的字節緩沖區,從給定的偏移量開始

如果需要,給定的緩沖區大小將會自動增加

如果timeout值是負數的,讀操作將不使用timeout

如果緩沖區為空,socket會為我們創建一個緩沖區

如果bufferOffset是大于給定的緩沖區的長度,該方法將無用,委托將不會被調用

如果你傳遞一個緩沖區,當AsyncSocket在使用它的時候你不能以任何方式改變它

完成之后,onSocket:didReadData:withTag 返回的數據將是一個給定的緩沖區的子集

也就是說,它將會被引用到被追加的給定的緩沖區的字節

-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取給定的字節數,如果length為0,方法將無用,委托將不會被調用

-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取給定的字節數,在給定的偏移開始,字節將被追加到給定的字節緩沖區

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的"data"參數

如果傳遞0或者0長度的數據,"data"參數,該方法將無用,委托將不會被調用

從socket讀取一行,使用"data"參數作為行的分隔符 (如HTTP的CRLF)

注意,此方法不是字符集,因此,如果一個分隔符出現,它自然可以作為進行編碼的一部分,讀取將提前結束

-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;

讀取字節直到(包括)傳入的作為分隔的“data”參數,在給定的偏移量開始,字節將被追加到給定的字節緩沖區。

從socket讀取一行,使用"data"參數作為行的分隔符(如HTTP的CRLF)

-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;

將data寫入socket,當完成的時候委托被調用

- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;

返回當前讀或寫的進度,從0.0 到 1.0 或者 如果沒有讀/寫的時候返回Nan(使用isNan來檢查)

tag、done、total如果不為空的話,它們將會被填補

- (void)startTLS:(NSDictionary *)tlsSettings;

確保使用ssl/tls連接

這方法可被隨時調用,tls握手將會發生在所有懸而未決的讀/寫完成之后。這緊跟著一個發送依賴 StartTLS消息的協議選項,在排隊升級到TLS的同一時間,而不必等待寫入完成。在這個方法被調用后,任何讀寫計劃 將會發生在安全鏈接

對于可能的keys和TLS設置的值是有據可查的

一些可能的keys是:

* - kCFStreamSSLLevel

* - kCFStreamSSLAllowsExpiredCertificates

* - kCFStreamSSLAllowsExpiredRoots

* - kCFStreamSSLAllowsAnyRoot

* - kCFStreamSSLValidatesCertificateChain

* - kCFStreamSSLPeerName

* - kCFStreamSSLCertificates

* - kCFStreamSSLIsServer

如果你傳遞空或者空字典,將使用默認的字典

默認設置將檢查以確保由簽署可信的第三方證書機構和沒有過期的遠程連接的證書

然而,它不會驗證證書上的名字,除非你給它一個名字,通過kCFStreamSSLPeerName鍵去驗證

這對安全的影響是重要的理解

想象一下你正試圖創建一個到MySecureServer.com的安全連接,但因為一個被攻擊的DNS服務器,所以你的socket被定向到MaliciousServer.com

如果你只是使用默認設置,MaliciousServer.com 有一個有效的證書

默認設置將無法監測到任何問題,因為證書是有效的

在這個特殊的情況下,要妥善保護你的連接,應設置kCFStreamSSLPeerName性質為MySecureServer.com.

如果事前你不知道對等的名字的遠程主機(例如,你不確認它是domain.com" or "www.domain.com"),那么你可以使用默認設置來驗證證書,然后在獲得驗證的發行后使用X509Certificate類來驗證,X509Certificate類的CocoaAsyncSocket開源項目的一部分

-(void)enablePrebuffering

對于處理readDataToData請求,數據是必須從socket以小增量的方式讀取出來的

性能通過允許AsyncSocket去一次性讀大塊的數據和存儲任何一個小的內部緩沖區溢出的東西來大大提高

這被稱為預緩沖,就好像一些數據在你要求它之前就可能被讀取出來

如果你經常使用readDataToData,使用預緩沖會有更好的性能,尤其是在iphone上

默認的預緩沖狀態是由DEFAULT_PREBUFFERING 定義控制的,強烈建議設置其為yes

這方法存在一些預緩沖需要一些不可預見的原因被默認禁用的情況,這時,這種方法存在允許當就緒時,可輕松啟用預緩沖

-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;

當你創建一個AsyncSocket,它被添加到當前線程runloop

對于手動創建的socket,在線程上你打算使用它,它是最容易簡單的創建的線程上的socket

當一個新的socket被接受,委托方法 onSocket:wantsRunLoopForNewSocket 會被調用 允許你在一個單獨的線程上放置socket,這個工作最好結合在同一個線程池設計

如果,但是,在一個單獨的線程上,在之后的時間,你需要移動一個socket,這個方法可以用來完成任務

此方法必須從 當前運行的 線程/runloop 的socket 調用

注意:此方法調用后,所有進一步的方法應該從給定的runloop上調用這個對象

此外,所有委托調用將會發送到給定的runloop

- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;

- (BOOL)addRunLoopMode:(NSString *)runLoopMode;

- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;

允許你配置 socket 使用的 運行循環模式

運行循環模式設置默認是NSRunLoopCommonModes

如果你想你的socket 在其他模式下繼續操作,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes

可接受的socket將自動 繼承相同的運行循環模式就像偵聽socket

注意:NSRunLoopCommonModes 定義在10.5,對于之前的版本可使用 kCFRunLoopCommonModes

-(NSArray *)runLoopModes

返回當前正在運行的循環模式的AsyncSocket實例, run loop modes的默認設置是NSDefaultRunLoopMode

-(NSData *)unreadData;

一個錯誤的事件,在 onSocket:willDisconnectWithError: 將會被調用 去讀取留在socket上的任何數據

+ (NSData *)CRLFData;?? // 0x0D0A

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容