iPhone的標(biāo)準(zhǔn)推薦是CFNetwork 庫編程谦絮,其封裝好的開源庫是 cocoa AsyncSocket庫两嘴,用它來簡化CFNetwork的調(diào)用翰苫,它提供了異步操作
主要特性有:
隊(duì)列的非阻塞的讀和寫菩咨,而且可選超時(shí)俱箱。你可以調(diào)用它讀取和寫入,它會(huì)當(dāng)完成后告知你
自動(dòng)的socket接收些椒。如果你調(diào)用它接收連接播瞳,它將為每個(gè)連接啟動(dòng)新的實(shí)例,當(dāng)然免糕,也可以立即關(guān)閉這些連接
委托(delegate)支持赢乓。錯(cuò)誤忧侧、連接、接收骏全、完整的讀取苍柏、完整的寫入、進(jìn)度以及斷開連接姜贡,都可以通過委托模式調(diào)用
基于run loop的试吁,而不是線程的。雖然可以在主線程或者工作線程中使用它楼咳,但你不需要這樣做熄捍。它異步的調(diào)用委托方法,使用NSRunLoop母怜。委托方法包括socket的參數(shù)余耽,可讓你在多個(gè)實(shí)例中區(qū)分
自包含在一個(gè)類中。你無需操作流或者socket苹熏,這個(gè)類幫你做了全部
支持基于IPV4和IPV6的TCP流
加入:AsynSocket.h .m與AsynUdpSocket.h .m四個(gè)文件 及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
在需要聯(lián)接地方使用connectToHost聯(lián)接服務(wù)器
其中initWithDelegate的參數(shù)中self是必須碟贾。這個(gè)對(duì)象指針中的各個(gè)Socket響應(yīng)的函數(shù)將被ASyncSocket所調(diào)用.initWithDelegate把將當(dāng)前對(duì)象傳遞進(jìn)去,這樣只要在當(dāng)前對(duì)象方法實(shí)現(xiàn)相應(yīng)方法
asyncSocket = [[AsyncSocket alloc] initWithDelegate:self];
NSError *err = nil;
if(![asyncSocket connectToHost:host on:port error:&err])
{
NSLog(@"Error: %@", err);
}
關(guān)于NSData對(duì)象
無論SOCKET收發(fā)都采用NSData對(duì)象.它的定義是 http://developer.apple.com/library/mac /#documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/Reference/Reference.html
NSData主要是帶一個(gè)(id)data指向的數(shù)據(jù)空間和長度 length.
NSString 轉(zhuǎn)換成NSData 對(duì)象
NSData* xmlData = [@"testdata" dataUsingEncoding:NSUTF8StringEncoding];
NSData 轉(zhuǎn)換成NSString對(duì)象
NSData * data;
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
發(fā)送數(shù)據(jù)
AsyncSocket writeData 方法來發(fā)送數(shù)據(jù)轨域,它有如下定義
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
以下是一個(gè)實(shí)例語句.
NSData* aData= [@"test data" dataUsingEncoding: NSUTF8StringEncoding];
[sock writeData:aData withTimeout:-1 tag:1];
在onSocket重載函數(shù)袱耽,有如定義采用是專門用來處理SOCKET的發(fā)送數(shù)據(jù)的:
-(void)onSocket(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
NSLog(@"thread(%),onSocket:%p didWriteDataWithTag:%d",[[NSThread currentThread] name],
sock,tag);
}
接收Socket數(shù)據(jù).
在onSocket重載函數(shù),有如定義采用是專門用來處理SOCKET的接收數(shù)據(jù)的.
-(void) onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
在中間將其轉(zhuǎn)換成NSString進(jìn)行顯示.
NSString* aStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"===%@",aStr);
[aStr release];
6干发、TCP連接讀取制定長度的數(shù)據(jù)
socket連接朱巨,可能會(huì)讀取固定長度的字節(jié)
[socket readDataToLength: withTimeout :tag]
各方法的解析
-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
發(fā)生錯(cuò)誤,socket關(guān)閉枉长,可以在call-back過程調(diào)用"unreadData"去取得socket的最后的數(shù)據(jù)字節(jié)冀续,當(dāng)連接的時(shí)候,該委托方法在 onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調(diào)用
-(void)onSocketDidDisconnect:(ASyncSocket *)sock;
當(dāng)socket由于或沒有錯(cuò)誤而斷開連接必峰,如果你想要在斷開連接后release socket洪唐,在此方法工作,而在onSocket:willDisconnectWithError 釋放則不安全
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
當(dāng)產(chǎn)生一個(gè)socket去處理連接時(shí)調(diào)用自点,此方法會(huì)返回 線程上的run-loop 的新的socket和其應(yīng)處理的委托桐罕,如果省略,則使用[NSRunLoop cunrrentRunLoop]
-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;
-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;
當(dāng)socket連接正準(zhǔn)備讀和寫的時(shí)候調(diào)用桂敛,host屬性是一個(gè)IP地址,而不是一個(gè)DNS 名稱
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;
當(dāng)socket已完成所要求的數(shù)據(jù)讀入內(nèi)存時(shí)調(diào)用溅潜,如果有錯(cuò)誤則不調(diào)用
-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;
當(dāng)一個(gè)socket讀取數(shù)據(jù)术唬,但尚未完成讀操作的時(shí)候調(diào)用,如果使用 readToData: or readToLength: 方法 會(huì)發(fā)生,可以被用來更新進(jìn)度條等東西
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
當(dāng)一個(gè)socket已完成請(qǐng)求數(shù)據(jù)的寫入時(shí)候調(diào)用
-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
當(dāng)一個(gè)socket寫入一些數(shù)據(jù)滚澜,但還沒有完成整個(gè)寫入時(shí)調(diào)用粗仓,它可以用來更新進(jìn)度條等東西
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length
使用讀操作已超時(shí)但還沒完成時(shí)調(diào)用,此方法允許隨意延遲超時(shí),如果返回一個(gè)正的時(shí)間間隔借浊,讀取的超時(shí)將有一定量的擴(kuò)展塘淑,如果不實(shí)現(xiàn)這個(gè)方法,或會(huì)像往常一樣返回一個(gè)負(fù)的時(shí)間間隔蚂斤,elapsed參數(shù)是 原超時(shí)的總和存捺,加上先前通過這種方法添加的任何補(bǔ)充, length參數(shù)是 讀操作到目前為止已讀取的字節(jié)數(shù)曙蒸, 注意捌治,如果返回正數(shù)的話,這個(gè)方法可能被一個(gè)單獨(dú)的讀取多次調(diào)用
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;
如果一個(gè)寫操作已達(dá)到其超時(shí)但還沒完成時(shí)調(diào)用纽窟,同上
-(void)onSocketDidSecure:(AsyncSocket *)sock;
在socket成功完成ssl/tls協(xié)商時(shí)調(diào)用肖油,此方法除非你使用提供startTLS方法時(shí)候才調(diào)用,
如果ssl/tls是無效的證書臂港,socket將會(huì)立即關(guān)閉森枪,onSocket:willDisconnectWithError:代理方法竟會(huì)與特定的ssl錯(cuò)誤代碼一起調(diào)用
-(BOOL)canSafelySetDelegate
用來查看在改變它之前,是否帶有與當(dāng)前的委托有懸而未決的業(yè)務(wù)(讀/寫)审孽。當(dāng)然县袱,應(yīng)在安全連接或接受委托之前改變委托
一旦接收或連接方法之一被調(diào)用,AsyncSocket實(shí)例會(huì)被鎖定瓷胧,其他接收/連接方法在沒有先斷開socket不會(huì)被調(diào)用
如果嘗試失敗或超時(shí)显拳,這些方法要么返回NO 要么調(diào)用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect
當(dāng)傳入的連接被接受,AsyncSocket調(diào)用多個(gè)委托方法搓萧。這些方法按照時(shí)間順序排列:
1.onSocket:didAcceptNewSocket:
2.onSocket:wantsRunLoopForNewSocket:
3. onSocketWillConnect:
你的服務(wù)器的代碼將需要保留公認(rèn)的socket(如果要接受它)杂数,最好的地方是要做到這一點(diǎn)可能在onSocket:didAcceptNewSocket:方法
在讀和寫流已經(jīng)為新接受的socket設(shè)置,onSocket:didConnectToHost:port 方法將在適當(dāng)?shù)倪\(yùn)行循環(huán)調(diào)用
多線程注意瘸洛,如果要想通過實(shí)施onSocket:wantsRunLoopForNewSocket:揍移,移動(dòng)另一個(gè)新接受的socket去到另一個(gè)循環(huán)的socket。然后反肋,應(yīng)該在調(diào)用讀和寫或者startTLS方法前那伐,等待直到onSocket:didConnectToHost:port:方法。否則讀和寫時(shí)間原定于不正確的runloop石蔗,混亂可能會(huì)隨之而來
-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;
告訴socket開始聽取和接受制定端口上的連接罕邀,當(dāng)一個(gè)連接到來的時(shí)候,AsyncSocket實(shí)例將調(diào)用各種委托方法养距,socket將聽取所有可用的接口(wifi,以太網(wǎng)等)
-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;
連接給定的主機(jī)和端口诉探,主機(jī)hostname可以是域名或者是Ip地址
-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;
連接到一個(gè)給定的地址,制定一個(gè)sockaddr結(jié)構(gòu)包裹住一個(gè)NSData對(duì)象,例如棍厌,NSData對(duì)象從NSNetService的地址方法返回,如果有一個(gè)現(xiàn)有的sockaddr結(jié)構(gòu)肾胯,可以將它轉(zhuǎn)換到一個(gè)NSData對(duì)象竖席,像這樣:
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;
立即斷開,任何未處理的讀或?qū)懚紝⒈粊G棄
如果socket還沒有斷開敬肚,在這個(gè)方法返回之前毕荐,onSocketDidDisconnect 委托方法將會(huì)被立即調(diào)用
注意推薦釋放AsyncSocket實(shí)例的方式:
[asyncSocket setDelegate:nil];
[asyncSocket disconnect];
[asyncSocket release];
-(void)disconnectAfterReading;
在已經(jīng)完成了所有懸而未決的讀取時(shí) 斷開,在調(diào)用之后,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入
- (NSString *)connectedHost;
- (UInt16)connectedPort;
- (NSString *)localHost;
- (UInt16)localPort;
返回本地和遠(yuǎn)程主機(jī)和端口給連接的socket艳馒,如果沒有連接會(huì)返回nil或0,主機(jī)將會(huì)是一個(gè)IP地址
-(NSData *)connectedAddress
-(NSData *)localAddresss
返回本地和遠(yuǎn)程的地址給連接的socket憎亚,指定一個(gè)socketaddr結(jié)構(gòu)包裹在一個(gè)NSData對(duì)象
readData和writeData方法不會(huì)是block(它們是異步的)
當(dāng)讀完成 onSocket:didReadData:withTag: 委托方法時(shí)調(diào)用
當(dāng)寫完成 onSocket:didWriteDataWithTag: 委托方法時(shí)調(diào)用
可以選擇任何讀/寫操作的超時(shí)設(shè)置(為了不超時(shí),使用負(fù)時(shí)間間隔鹰溜。)
如果讀/寫操作超時(shí)虽填,相應(yīng)的 onSocket:shouldTimeout...委托方法被調(diào)用去選擇性地允許我們?nèi)パ娱L超時(shí)
超時(shí)后,onSocket:willDisconnectWithError: 方法被調(diào)用,緊接著是 onSocketDidDisconnect
tag是為了方便曹动,可以使用它作為數(shù)組的索引斋日、步數(shù)、state id 墓陈、指針等
-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;
讀取socket上第一次成為可用的字節(jié),如果timeout值是負(fù)數(shù)的恶守,讀操作將不使用timeout
- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;
讀取socket上第一次成為可用的字節(jié)
字節(jié)將被追加到給定的字節(jié)緩沖區(qū),從給定的偏移量開始
如果需要贡必,給定的緩沖區(qū)大小將會(huì)自動(dòng)增加
如果timeout值是負(fù)數(shù)的兔港,讀操作將不使用timeout
如果緩沖區(qū)為空,socket會(huì)為我們創(chuàng)建一個(gè)緩沖區(qū)
如果bufferOffset是大于給定的緩沖區(qū)的長度仔拟,該方法將無用衫樊,委托將不會(huì)被調(diào)用
如果你傳遞一個(gè)緩沖區(qū),當(dāng)AsyncSocket在使用它的時(shí)候你不能以任何方式改變它
完成之后利花,onSocket:didReadData:withTag 返回的數(shù)據(jù)將是一個(gè)給定的緩沖區(qū)的子集
也就是說科侈,它將會(huì)被引用到被追加的給定的緩沖區(qū)的字節(jié)
-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取給定的字節(jié)數(shù),如果length為0炒事,方法將無用臀栈,委托將不會(huì)被調(diào)用
-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取給定的字節(jié)數(shù),在給定的偏移開始,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的"data"參數(shù)
如果傳遞0或者0長度的數(shù)據(jù)挠乳,"data"參數(shù)权薯,該方法將無用,委托將不會(huì)被調(diào)用
從socket讀取一行睡扬,使用"data"參數(shù)作為行的分隔符 (如HTTP的CRLF)
注意盟蚣,此方法不是字符集,因此卖怜,如果一個(gè)分隔符出現(xiàn)刁俭,它自然可以作為進(jìn)行編碼的一部分,讀取將提前結(jié)束
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的“data”參數(shù)韧涨,在給定的偏移量開始牍戚,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)。
從socket讀取一行虑粥,使用"data"參數(shù)作為行的分隔符(如HTTP的CRLF)
-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;
將data寫入socket如孝,當(dāng)完成的時(shí)候委托被調(diào)用
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
返回當(dāng)前讀或?qū)懙倪M(jìn)度,從0.0 到 1.0 或者 如果沒有讀/寫的時(shí)候返回Nan(使用isNan來檢查)
tag娩贷、done第晰、total如果不為空的話,它們將會(huì)被填補(bǔ)
- (void)startTLS:(NSDictionary *)tlsSettings;
確保使用ssl/tls連接
這方法可被隨時(shí)調(diào)用彬祖,tls握手將會(huì)發(fā)生在所有懸而未決的讀/寫完成之后茁瘦。這緊跟著一個(gè)發(fā)送依賴 StartTLS消息的協(xié)議選項(xiàng),在排隊(duì)升級(jí)到TLS的同一時(shí)間储笑,而不必等待寫入完成甜熔。在這個(gè)方法被調(diào)用后,任何讀寫計(jì)劃 將會(huì)發(fā)生在安全鏈接
對(duì)于可能的keys和TLS設(shè)置的值是有據(jù)可查的
一些可能的keys是:
* - kCFStreamSSLLevel
* - kCFStreamSSLAllowsExpiredCertificates
* - kCFStreamSSLAllowsExpiredRoots
* - kCFStreamSSLAllowsAnyRoot
* - kCFStreamSSLValidatesCertificateChain
* - kCFStreamSSLPeerName
* - kCFStreamSSLCertificates
* - kCFStreamSSLIsServer
如果你傳遞空或者空字典,將使用默認(rèn)的字典
默認(rèn)設(shè)置將檢查以確保由簽署可信的第三方證書機(jī)構(gòu)和沒有過期的遠(yuǎn)程連接的證書
然而突倍,它不會(huì)驗(yàn)證證書上的名字腔稀,除非你給它一個(gè)名字,通過kCFStreamSSLPeerName鍵去驗(yàn)證
這對(duì)安全的影響是重要的理解
想象一下你正試圖創(chuàng)建一個(gè)到MySecureServer.com的安全連接羽历,但因?yàn)橐粋€(gè)被攻擊的DNS服務(wù)器焊虏,所以你的socket被定向到MaliciousServer.com
如果你只是使用默認(rèn)設(shè)置,MaliciousServer.com 有一個(gè)有效的證書
默認(rèn)設(shè)置將無法監(jiān)測(cè)到任何問題秕磷,因?yàn)樽C書是有效的
在這個(gè)特殊的情況下诵闭,要妥善保護(hù)你的連接,應(yīng)設(shè)置kCFStreamSSLPeerName性質(zhì)為MySecureServer.com.
如果事前你不知道對(duì)等的名字的遠(yuǎn)程主機(jī)(例如澎嚣,你不確認(rèn)它是domain.com" or "www.domain.com")疏尿,那么你可以使用默認(rèn)設(shè)置來驗(yàn)證證書,然后在獲得驗(yàn)證的發(fā)行后使用X509Certificate類來驗(yàn)證,X509Certificate類的CocoaAsyncSocket開源項(xiàng)目的一部分
-(void)enablePrebuffering
對(duì)于處理readDataToData請(qǐng)求币叹,數(shù)據(jù)是必須從socket以小增量的方式讀取出來的
性能通過允許AsyncSocket去一次性讀大塊的數(shù)據(jù)和存儲(chǔ)任何一個(gè)小的內(nèi)部緩沖區(qū)溢出的東西來大大提高
這被稱為預(yù)緩沖润歉,就好像一些數(shù)據(jù)在你要求它之前就可能被讀取出來
如果你經(jīng)常使用readDataToData,使用預(yù)緩沖會(huì)有更好的性能颈抚,尤其是在iphone上
默認(rèn)的預(yù)緩沖狀態(tài)是由DEFAULT_PREBUFFERING 定義控制的踩衩,強(qiáng)烈建議設(shè)置其為yes
這方法存在一些預(yù)緩沖需要一些不可預(yù)見的原因被默認(rèn)禁用的情況,這時(shí)贩汉,這種方法存在允許當(dāng)就緒時(shí)驱富,可輕松啟用預(yù)緩沖
-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;
當(dāng)你創(chuàng)建一個(gè)AsyncSocket,它被添加到當(dāng)前線程runloop
對(duì)于手動(dòng)創(chuàng)建的socket匹舞,在線程上你打算使用它褐鸥,它是最容易簡單的創(chuàng)建的線程上的socket
當(dāng)一個(gè)新的socket被接受,委托方法 onSocket:wantsRunLoopForNewSocket 會(huì)被調(diào)用 允許你在一個(gè)單獨(dú)的線程上放置socket赐稽,這個(gè)工作最好結(jié)合在同一個(gè)線程池設(shè)計(jì)
如果叫榕,但是浑侥,在一個(gè)單獨(dú)的線程上,在之后的時(shí)間晰绎,你需要移動(dòng)一個(gè)socket寓落,這個(gè)方法可以用來完成任務(wù)
此方法必須從 當(dāng)前運(yùn)行的 線程/runloop 的socket 調(diào)用
注意:此方法調(diào)用后,所有進(jìn)一步的方法應(yīng)該從給定的runloop上調(diào)用這個(gè)對(duì)象
此外荞下,所有委托調(diào)用將會(huì)發(fā)送到給定的runloop
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
- (BOOL)addRunLoopMode:(NSString *)runLoopMode;
- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;
允許你配置 socket 使用的 運(yùn)行循環(huán)模式
運(yùn)行循環(huán)模式設(shè)置默認(rèn)是NSRunLoopCommonModes
如果你想你的socket 在其他模式下繼續(xù)操作伶选,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes
可接受的socket將自動(dòng) 繼承相同的運(yùn)行循環(huán)模式就像偵聽socket
注意:NSRunLoopCommonModes 定義在10.5,對(duì)于之前的版本可使用 kCFRunLoopCommonModes
-(NSArray *)runLoopModes
返回當(dāng)前正在運(yùn)行的循環(huán)模式的AsyncSocket實(shí)例尖昏, run loop modes的默認(rèn)設(shè)置是NSDefaultRunLoopMode
-(NSData *)unreadData;
一個(gè)錯(cuò)誤的事件仰税,在 onSocket:willDisconnectWithError: 將會(huì)被調(diào)用 去讀取留在socket上的任何數(shù)據(jù)
+ (NSData *)CRLFData; // 0x0D0A
各方法的解析
-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
發(fā)生錯(cuò)誤,socket關(guān)閉抽诉,可以在call-back過程調(diào)用"unreadData"去取得socket的最后的數(shù)據(jù)字節(jié)陨簇,當(dāng)連接的時(shí)候,該委托方法在 onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調(diào)用
-(void)onSocketDidDisconnect:(ASyncSocket *)sock;
當(dāng)socket由于或沒有錯(cuò)誤而斷開連接掸鹅,如果你想要在斷開連接后release socket塞帐,在此方法工作,而在onSocket:willDisconnectWithError 釋放則不安全
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
當(dāng)產(chǎn)生一個(gè)socket去處理連接時(shí)調(diào)用巍沙,此方法會(huì)返回 線程上的run-loop 的新的socket和其應(yīng)處理的委托葵姥,如果省略,則使用[NSRunLoop cunrrentRunLoop]
-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;
-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;
當(dāng)socket連接正準(zhǔn)備讀和寫的時(shí)候調(diào)用句携,host屬性是一個(gè)IP地址榔幸,而不是一個(gè)DNS 名稱
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;
當(dāng)socket已完成所要求的數(shù)據(jù)讀入內(nèi)存時(shí)調(diào)用,如果有錯(cuò)誤則不調(diào)用
-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;
當(dāng)一個(gè)socket讀取數(shù)據(jù)矮嫉,但尚未完成讀操作的時(shí)候調(diào)用削咆,如果使用 readToData: or readToLength: 方法 會(huì)發(fā)生,可以被用來更新進(jìn)度條等東西
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
當(dāng)一個(gè)socket已完成請(qǐng)求數(shù)據(jù)的寫入時(shí)候調(diào)用
-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
當(dāng)一個(gè)socket寫入一些數(shù)據(jù),但還沒有完成整個(gè)寫入時(shí)調(diào)用蠢笋,它可以用來更新進(jìn)度條等東西
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length
使用讀操作已超時(shí)但還沒完成時(shí)調(diào)用拨齐,此方法允許隨意延遲超時(shí),如果返回一個(gè)正的時(shí)間間隔昨寞,讀取的超時(shí)將有一定量的擴(kuò)展瞻惋,如果不實(shí)現(xiàn)這個(gè)方法,或會(huì)像往常一樣返回一個(gè)負(fù)的時(shí)間間隔援岩,elapsed參數(shù)是 原超時(shí)的總和歼狼,加上先前通過這種方法添加的任何補(bǔ)充, length參數(shù)是 讀操作到目前為止已讀取的字節(jié)數(shù)享怀, 注意羽峰,如果返回正數(shù)的話,這個(gè)方法可能被一個(gè)單獨(dú)的讀取多次調(diào)用
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;
如果一個(gè)寫操作已達(dá)到其超時(shí)但還沒完成時(shí)調(diào)用,同上
-(void)onSocketDidSecure:(AsyncSocket *)sock;
在socket成功完成ssl/tls協(xié)商時(shí)調(diào)用梅屉,此方法除非你使用提供startTLS方法時(shí)候才調(diào)用值纱,
如果ssl/tls是無效的證書,socket將會(huì)立即關(guān)閉履植,onSocket:willDisconnectWithError:代理方法竟會(huì)與特定的ssl錯(cuò)誤代碼一起調(diào)用
-(BOOL)canSafelySetDelegate
用來查看在改變它之前计雌,是否帶有與當(dāng)前的委托有懸而未決的業(yè)務(wù)(讀/寫)。當(dāng)然玫霎,應(yīng)在安全連接或接受委托之前改變委托
一旦接收或連接方法之一被調(diào)用,AsyncSocket實(shí)例會(huì)被鎖定妈橄,其他接收/連接方法在沒有先斷開socket不會(huì)被調(diào)用
如果嘗試失敗或超時(shí)庶近,這些方法要么返回NO 要么調(diào)用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect
當(dāng)傳入的連接被接受,AsyncSocket調(diào)用多個(gè)委托方法眷蚓。這些方法按照時(shí)間順序排列:
1.onSocket:didAcceptNewSocket:
2.onSocket:wantsRunLoopForNewSocket:
3. onSocketWillConnect:
你的服務(wù)器的代碼將需要保留公認(rèn)的socket(如果要接受它)鼻种,最好的地方是要做到這一點(diǎn)可能在onSocket:didAcceptNewSocket:方法
在讀和寫流已經(jīng)為新接受的socket設(shè)置,onSocket:didConnectToHost:port 方法將在適當(dāng)?shù)倪\(yùn)行循環(huán)調(diào)用
多線程注意沙热,如果要想通過實(shí)施onSocket:wantsRunLoopForNewSocket:叉钥,移動(dòng)另一個(gè)新接受的socket去到另一個(gè)循環(huán)的socket。然后篙贸,應(yīng)該在調(diào)用讀和寫或者startTLS方法前投队,等待直到onSocket:didConnectToHost:port:方法。否則讀和寫時(shí)間原定于不正確的runloop爵川,混亂可能會(huì)隨之而來
-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;
告訴socket開始聽取和接受制定端口上的連接敷鸦,當(dāng)一個(gè)連接到來的時(shí)候,AsyncSocket實(shí)例將調(diào)用各種委托方法寝贡,socket將聽取所有可用的接口(wifi,以太網(wǎng)等)
-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;
連接給定的主機(jī)和端口扒披,主機(jī)hostname可以是域名或者是Ip地址
-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;
連接到一個(gè)給定的地址,制定一個(gè)sockaddr結(jié)構(gòu)包裹住一個(gè)NSData對(duì)象,例如圃泡,NSData對(duì)象從NSNetService的地址方法返回,如果有一個(gè)現(xiàn)有的sockaddr結(jié)構(gòu)碟案,可以將它轉(zhuǎn)換到一個(gè)NSData對(duì)象,像這樣:
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;
立即斷開颇蜡,任何未處理的讀或?qū)懚紝⒈粊G棄
如果socket還沒有斷開价说,在這個(gè)方法返回之前,onSocketDidDisconnect 委托方法將會(huì)被立即調(diào)用
注意推薦釋放AsyncSocket實(shí)例的方式:
[asyncSocket setDelegate:nil];
[asyncSocket disconnect];
[asyncSocket release];
-(void)disconnectAfterReading;
在已經(jīng)完成了所有懸而未決的讀取時(shí) 斷開,在調(diào)用之后澡匪,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入
- (NSString *)connectedHost;
- (UInt16)connectedPort;
- (NSString *)localHost;
- (UInt16)localPort;
返回本地和遠(yuǎn)程主機(jī)和端口給連接的socket熔任,如果沒有連接會(huì)返回nil或0,主機(jī)將會(huì)是一個(gè)IP地址
-(NSData *)connectedAddress
-(NSData *)localAddresss
返回本地和遠(yuǎn)程的地址給連接的socket,指定一個(gè)socketaddr結(jié)構(gòu)包裹在一個(gè)NSData對(duì)象
readData和writeData方法不會(huì)是block(它們是異步的)
當(dāng)讀完成 onSocket:didReadData:withTag: 委托方法時(shí)調(diào)用
當(dāng)寫完成 onSocket:didWriteDataWithTag: 委托方法時(shí)調(diào)用
可以選擇任何讀/寫操作的超時(shí)設(shè)置(為了不超時(shí)唁情,使用負(fù)時(shí)間間隔疑苔。)
如果讀/寫操作超時(shí),相應(yīng)的 onSocket:shouldTimeout...委托方法被調(diào)用去選擇性地允許我們?nèi)パ娱L超時(shí)
超時(shí)后甸鸟,onSocket:willDisconnectWithError: 方法被調(diào)用,緊接著是 onSocketDidDisconnect
tag是為了方便惦费,可以使用它作為數(shù)組的索引兵迅、步數(shù)、state id 薪贫、指針等
-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;
讀取socket上第一次成為可用的字節(jié),如果timeout值是負(fù)數(shù)的恍箭,讀操作將不使用timeout
- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;
讀取socket上第一次成為可用的字節(jié)
字節(jié)將被追加到給定的字節(jié)緩沖區(qū),從給定的偏移量開始
如果需要瞧省,給定的緩沖區(qū)大小將會(huì)自動(dòng)增加
如果timeout值是負(fù)數(shù)的扯夭,讀操作將不使用timeout
如果緩沖區(qū)為空,socket會(huì)為我們創(chuàng)建一個(gè)緩沖區(qū)
如果bufferOffset是大于給定的緩沖區(qū)的長度鞍匾,該方法將無用交洗,委托將不會(huì)被調(diào)用
如果你傳遞一個(gè)緩沖區(qū),當(dāng)AsyncSocket在使用它的時(shí)候你不能以任何方式改變它
完成之后橡淑,onSocket:didReadData:withTag 返回的數(shù)據(jù)將是一個(gè)給定的緩沖區(qū)的子集
也就是說构拳,它將會(huì)被引用到被追加的給定的緩沖區(qū)的字節(jié)
-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取給定的字節(jié)數(shù),如果length為0梁棠,方法將無用置森,委托將不會(huì)被調(diào)用
-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取給定的字節(jié)數(shù),在給定的偏移開始,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的"data"參數(shù)
如果傳遞0或者0長度的數(shù)據(jù)符糊,"data"參數(shù)凫海,該方法將無用,委托將不會(huì)被調(diào)用
從socket讀取一行濒蒋,使用"data"參數(shù)作為行的分隔符 (如HTTP的CRLF)
注意盐碱,此方法不是字符集,因此沪伙,如果一個(gè)分隔符出現(xiàn)瓮顽,它自然可以作為進(jìn)行編碼的一部分,讀取將提前結(jié)束
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的“data”參數(shù)围橡,在給定的偏移量開始暖混,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)。
從socket讀取一行翁授,使用"data"參數(shù)作為行的分隔符(如HTTP的CRLF)
-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;
將data寫入socket拣播,當(dāng)完成的時(shí)候委托被調(diào)用
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
返回當(dāng)前讀或?qū)懙倪M(jìn)度,從0.0 到 1.0 或者 如果沒有讀/寫的時(shí)候返回Nan(使用isNan來檢查)
tag收擦、done贮配、total如果不為空的話轮洋,它們將會(huì)被填補(bǔ)
- (void)startTLS:(NSDictionary *)tlsSettings;
確保使用ssl/tls連接
這方法可被隨時(shí)調(diào)用惋戏,tls握手將會(huì)發(fā)生在所有懸而未決的讀/寫完成之后于未。這緊跟著一個(gè)發(fā)送依賴 StartTLS消息的協(xié)議選項(xiàng),在排隊(duì)升級(jí)到TLS的同一時(shí)間兽间,而不必等待寫入完成辱士。在這個(gè)方法被調(diào)用后,任何讀寫計(jì)劃 將會(huì)發(fā)生在安全鏈接
對(duì)于可能的keys和TLS設(shè)置的值是有據(jù)可查的
一些可能的keys是:
* - kCFStreamSSLLevel
* - kCFStreamSSLAllowsExpiredCertificates
* - kCFStreamSSLAllowsExpiredRoots
* - kCFStreamSSLAllowsAnyRoot
* - kCFStreamSSLValidatesCertificateChain
* - kCFStreamSSLPeerName
* - kCFStreamSSLCertificates
* - kCFStreamSSLIsServer
如果你傳遞空或者空字典玉掸,將使用默認(rèn)的字典
默認(rèn)設(shè)置將檢查以確保由簽署可信的第三方證書機(jī)構(gòu)和沒有過期的遠(yuǎn)程連接的證書
然而伴挚,它不會(huì)驗(yàn)證證書上的名字,除非你給它一個(gè)名字沦辙,通過kCFStreamSSLPeerName鍵去驗(yàn)證
這對(duì)安全的影響是重要的理解
想象一下你正試圖創(chuàng)建一個(gè)到MySecureServer.com的安全連接夫植,但因?yàn)橐粋€(gè)被攻擊的DNS服務(wù)器,所以你的socket被定向到MaliciousServer.com
如果你只是使用默認(rèn)設(shè)置油讯,MaliciousServer.com 有一個(gè)有效的證書
默認(rèn)設(shè)置將無法監(jiān)測(cè)到任何問題详民,因?yàn)樽C書是有效的
在這個(gè)特殊的情況下,要妥善保護(hù)你的連接撞羽,應(yīng)設(shè)置kCFStreamSSLPeerName性質(zhì)為MySecureServer.com.
如果事前你不知道對(duì)等的名字的遠(yuǎn)程主機(jī)(例如阐斜,你不確認(rèn)它是domain.com" or "www.domain.com"),那么你可以使用默認(rèn)設(shè)置來驗(yàn)證證書诀紊,然后在獲得驗(yàn)證的發(fā)行后使用X509Certificate類來驗(yàn)證,X509Certificate類的CocoaAsyncSocket開源項(xiàng)目的一部分
-(void)enablePrebuffering
對(duì)于處理readDataToData請(qǐng)求,數(shù)據(jù)是必須從socket以小增量的方式讀取出來的
性能通過允許AsyncSocket去一次性讀大塊的數(shù)據(jù)和存儲(chǔ)任何一個(gè)小的內(nèi)部緩沖區(qū)溢出的東西來大大提高
這被稱為預(yù)緩沖隅俘,就好像一些數(shù)據(jù)在你要求它之前就可能被讀取出來
如果你經(jīng)常使用readDataToData邻奠,使用預(yù)緩沖會(huì)有更好的性能,尤其是在iphone上
默認(rèn)的預(yù)緩沖狀態(tài)是由DEFAULT_PREBUFFERING 定義控制的为居,強(qiáng)烈建議設(shè)置其為yes
這方法存在一些預(yù)緩沖需要一些不可預(yù)見的原因被默認(rèn)禁用的情況碌宴,這時(shí),這種方法存在允許當(dāng)就緒時(shí)蒙畴,可輕松啟用預(yù)緩沖
-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;
當(dāng)你創(chuàng)建一個(gè)AsyncSocket贰镣,它被添加到當(dāng)前線程runloop
對(duì)于手動(dòng)創(chuàng)建的socket,在線程上你打算使用它膳凝,它是最容易簡單的創(chuàng)建的線程上的socket
當(dāng)一個(gè)新的socket被接受碑隆,委托方法 onSocket:wantsRunLoopForNewSocket 會(huì)被調(diào)用 允許你在一個(gè)單獨(dú)的線程上放置socket,這個(gè)工作最好結(jié)合在同一個(gè)線程池設(shè)計(jì)
如果蹬音,但是上煤,在一個(gè)單獨(dú)的線程上,在之后的時(shí)間著淆,你需要移動(dòng)一個(gè)socket劫狠,這個(gè)方法可以用來完成任務(wù)
此方法必須從 當(dāng)前運(yùn)行的 線程/runloop 的socket 調(diào)用
注意:此方法調(diào)用后,所有進(jìn)一步的方法應(yīng)該從給定的runloop上調(diào)用這個(gè)對(duì)象
此外永部,所有委托調(diào)用將會(huì)發(fā)送到給定的runloop
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
- (BOOL)addRunLoopMode:(NSString *)runLoopMode;
- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;
允許你配置 socket 使用的 運(yùn)行循環(huán)模式
運(yùn)行循環(huán)模式設(shè)置默認(rèn)是NSRunLoopCommonModes
如果你想你的socket 在其他模式下繼續(xù)操作独泞,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes
可接受的socket將自動(dòng) 繼承相同的運(yùn)行循環(huán)模式就像偵聽socket
注意:NSRunLoopCommonModes 定義在10.5,對(duì)于之前的版本可使用 kCFRunLoopCommonModes
-(NSArray *)runLoopModes
返回當(dāng)前正在運(yùn)行的循環(huán)模式的AsyncSocket實(shí)例苔埋, run loop modes的默認(rèn)設(shè)置是NSDefaultRunLoopMode
-(NSData *)unreadData;
一個(gè)錯(cuò)誤的事件懦砂,在 onSocket:willDisconnectWithError: 將會(huì)被調(diào)用 去讀取留在socket上的任何數(shù)據(jù)
+ (NSData *)CRLFData; // 0x0D0A
各方法的解析
-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
發(fā)生錯(cuò)誤,socket關(guān)閉,可以在call-back過程調(diào)用"unreadData"去取得socket的最后的數(shù)據(jù)字節(jié)孕惜,當(dāng)連接的時(shí)候愧薛,該委托方法在 onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調(diào)用
-(void)onSocketDidDisconnect:(ASyncSocket *)sock;
當(dāng)socket由于或沒有錯(cuò)誤而斷開連接,如果你想要在斷開連接后release socket衫画,在此方法工作毫炉,而在onSocket:willDisconnectWithError 釋放則不安全
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
當(dāng)產(chǎn)生一個(gè)socket去處理連接時(shí)調(diào)用,此方法會(huì)返回 線程上的run-loop 的新的socket和其應(yīng)處理的委托削罩,如果省略瞄勾,則使用[NSRunLoop cunrrentRunLoop]
-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;
-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;
當(dāng)socket連接正準(zhǔn)備讀和寫的時(shí)候調(diào)用,host屬性是一個(gè)IP地址弥激,而不是一個(gè)DNS 名稱
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;
當(dāng)socket已完成所要求的數(shù)據(jù)讀入內(nèi)存時(shí)調(diào)用进陡,如果有錯(cuò)誤則不調(diào)用
-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;
當(dāng)一個(gè)socket讀取數(shù)據(jù),但尚未完成讀操作的時(shí)候調(diào)用微服,如果使用 readToData: or readToLength: 方法 會(huì)發(fā)生,可以被用來更新進(jìn)度條等東西
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
當(dāng)一個(gè)socket已完成請(qǐng)求數(shù)據(jù)的寫入時(shí)候調(diào)用
-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
當(dāng)一個(gè)socket寫入一些數(shù)據(jù)趾疚,但還沒有完成整個(gè)寫入時(shí)調(diào)用,它可以用來更新進(jìn)度條等東西
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length
使用讀操作已超時(shí)但還沒完成時(shí)調(diào)用以蕴,此方法允許隨意延遲超時(shí)糙麦,如果返回一個(gè)正的時(shí)間間隔,讀取的超時(shí)將有一定量的擴(kuò)展丛肮,如果不實(shí)現(xiàn)這個(gè)方法赡磅,或會(huì)像往常一樣返回一個(gè)負(fù)的時(shí)間間隔,elapsed參數(shù)是 原超時(shí)的總和宝与,加上先前通過這種方法添加的任何補(bǔ)充焚廊, length參數(shù)是 讀操作到目前為止已讀取的字節(jié)數(shù), 注意习劫,如果返回正數(shù)的話咆瘟,這個(gè)方法可能被一個(gè)單獨(dú)的讀取多次調(diào)用
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;
如果一個(gè)寫操作已達(dá)到其超時(shí)但還沒完成時(shí)調(diào)用,同上
-(void)onSocketDidSecure:(AsyncSocket *)sock;
在socket成功完成ssl/tls協(xié)商時(shí)調(diào)用榜聂,此方法除非你使用提供startTLS方法時(shí)候才調(diào)用搞疗,
如果ssl/tls是無效的證書,socket將會(huì)立即關(guān)閉须肆,onSocket:willDisconnectWithError:代理方法竟會(huì)與特定的ssl錯(cuò)誤代碼一起調(diào)用
-(BOOL)canSafelySetDelegate
用來查看在改變它之前匿乃,是否帶有與當(dāng)前的委托有懸而未決的業(yè)務(wù)(讀/寫)。當(dāng)然豌汇,應(yīng)在安全連接或接受委托之前改變委托
一旦接收或連接方法之一被調(diào)用幢炸,AsyncSocket實(shí)例會(huì)被鎖定,其他接收/連接方法在沒有先斷開socket不會(huì)被調(diào)用
如果嘗試失敗或超時(shí)拒贱,這些方法要么返回NO 要么調(diào)用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect
當(dāng)傳入的連接被接受宛徊,AsyncSocket調(diào)用多個(gè)委托方法佛嬉。這些方法按照時(shí)間順序排列:
1.onSocket:didAcceptNewSocket:
2.onSocket:wantsRunLoopForNewSocket:
3. onSocketWillConnect:
你的服務(wù)器的代碼將需要保留公認(rèn)的socket(如果要接受它),最好的地方是要做到這一點(diǎn)可能在onSocket:didAcceptNewSocket:方法
在讀和寫流已經(jīng)為新接受的socket設(shè)置闸天,onSocket:didConnectToHost:port 方法將在適當(dāng)?shù)倪\(yùn)行循環(huán)調(diào)用
多線程注意暖呕,如果要想通過實(shí)施onSocket:wantsRunLoopForNewSocket:,移動(dòng)另一個(gè)新接受的socket去到另一個(gè)循環(huán)的socket苞氮。然后湾揽,應(yīng)該在調(diào)用讀和寫或者startTLS方法前,等待直到onSocket:didConnectToHost:port:方法笼吟。否則讀和寫時(shí)間原定于不正確的runloop库物,混亂可能會(huì)隨之而來
-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;
告訴socket開始聽取和接受制定端口上的連接,當(dāng)一個(gè)連接到來的時(shí)候贷帮,AsyncSocket實(shí)例將調(diào)用各種委托方法戚揭,socket將聽取所有可用的接口(wifi,以太網(wǎng)等)
-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;
連接給定的主機(jī)和端口,主機(jī)hostname可以是域名或者是Ip地址
-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;
連接到一個(gè)給定的地址撵枢,制定一個(gè)sockaddr結(jié)構(gòu)包裹住一個(gè)NSData對(duì)象,例如民晒,NSData對(duì)象從NSNetService的地址方法返回,如果有一個(gè)現(xiàn)有的sockaddr結(jié)構(gòu),可以將它轉(zhuǎn)換到一個(gè)NSData對(duì)象锄禽,像這樣:
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;
立即斷開镀虐,任何未處理的讀或?qū)懚紝⒈粊G棄
如果socket還沒有斷開,在這個(gè)方法返回之前沟绪,onSocketDidDisconnect 委托方法將會(huì)被立即調(diào)用
注意推薦釋放AsyncSocket實(shí)例的方式:
[asyncSocket setDelegate:nil];
[asyncSocket disconnect];
[asyncSocket release];
-(void)disconnectAfterReading;
在已經(jīng)完成了所有懸而未決的讀取時(shí) 斷開,在調(diào)用之后,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入
- (NSString *)connectedHost;
- (UInt16)connectedPort;
- (NSString *)localHost;
- (UInt16)localPort;
返回本地和遠(yuǎn)程主機(jī)和端口給連接的socket空猜,如果沒有連接會(huì)返回nil或0,主機(jī)將會(huì)是一個(gè)IP地址
-(NSData *)connectedAddress
-(NSData *)localAddresss
返回本地和遠(yuǎn)程的地址給連接的socket绽慈,指定一個(gè)socketaddr結(jié)構(gòu)包裹在一個(gè)NSData對(duì)象
readData和writeData方法不會(huì)是block(它們是異步的)
當(dāng)讀完成 onSocket:didReadData:withTag: 委托方法時(shí)調(diào)用
當(dāng)寫完成 onSocket:didWriteDataWithTag: 委托方法時(shí)調(diào)用
可以選擇任何讀/寫操作的超時(shí)設(shè)置(為了不超時(shí),使用負(fù)時(shí)間間隔辈毯。)
如果讀/寫操作超時(shí)坝疼,相應(yīng)的 onSocket:shouldTimeout...委托方法被調(diào)用去選擇性地允許我們?nèi)パ娱L超時(shí)
超時(shí)后,onSocket:willDisconnectWithError: 方法被調(diào)用,緊接著是 onSocketDidDisconnect
tag是為了方便谆沃,可以使用它作為數(shù)組的索引钝凶、步數(shù)、state id 唁影、指針等
-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;
讀取socket上第一次成為可用的字節(jié),如果timeout值是負(fù)數(shù)的耕陷,讀操作將不使用timeout
- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;
讀取socket上第一次成為可用的字節(jié)
字節(jié)將被追加到給定的字節(jié)緩沖區(qū),從給定的偏移量開始
如果需要据沈,給定的緩沖區(qū)大小將會(huì)自動(dòng)增加
如果timeout值是負(fù)數(shù)的哟沫,讀操作將不使用timeout
如果緩沖區(qū)為空,socket會(huì)為我們創(chuàng)建一個(gè)緩沖區(qū)
如果bufferOffset是大于給定的緩沖區(qū)的長度锌介,該方法將無用嗜诀,委托將不會(huì)被調(diào)用
如果你傳遞一個(gè)緩沖區(qū)猾警,當(dāng)AsyncSocket在使用它的時(shí)候你不能以任何方式改變它
完成之后,onSocket:didReadData:withTag 返回的數(shù)據(jù)將是一個(gè)給定的緩沖區(qū)的子集
也就是說隆敢,它將會(huì)被引用到被追加的給定的緩沖區(qū)的字節(jié)
-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取給定的字節(jié)數(shù)发皿,如果length為0,方法將無用拂蝎,委托將不會(huì)被調(diào)用
-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取給定的字節(jié)數(shù),在給定的偏移開始穴墅,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的"data"參數(shù)
如果傳遞0或者0長度的數(shù)據(jù),"data"參數(shù)匣屡,該方法將無用封救,委托將不會(huì)被調(diào)用
從socket讀取一行,使用"data"參數(shù)作為行的分隔符 (如HTTP的CRLF)
注意捣作,此方法不是字符集誉结,因此,如果一個(gè)分隔符出現(xiàn)券躁,它自然可以作為進(jìn)行編碼的一部分惩坑,讀取將提前結(jié)束
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的“data”參數(shù),在給定的偏移量開始也拜,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)以舒。
從socket讀取一行,使用"data"參數(shù)作為行的分隔符(如HTTP的CRLF)
-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;
將data寫入socket慢哈,當(dāng)完成的時(shí)候委托被調(diào)用
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
返回當(dāng)前讀或?qū)懙倪M(jìn)度蔓钟,從0.0 到 1.0 或者 如果沒有讀/寫的時(shí)候返回Nan(使用isNan來檢查)
tag、done卵贱、total如果不為空的話滥沫,它們將會(huì)被填補(bǔ)
- (void)startTLS:(NSDictionary *)tlsSettings;
確保使用ssl/tls連接
這方法可被隨時(shí)調(diào)用,tls握手將會(huì)發(fā)生在所有懸而未決的讀/寫完成之后键俱。這緊跟著一個(gè)發(fā)送依賴 StartTLS消息的協(xié)議選項(xiàng)兰绣,在排隊(duì)升級(jí)到TLS的同一時(shí)間,而不必等待寫入完成编振。在這個(gè)方法被調(diào)用后,任何讀寫計(jì)劃 將會(huì)發(fā)生在安全鏈接
對(duì)于可能的keys和TLS設(shè)置的值是有據(jù)可查的
一些可能的keys是:
* - kCFStreamSSLLevel
* - kCFStreamSSLAllowsExpiredCertificates
* - kCFStreamSSLAllowsExpiredRoots
* - kCFStreamSSLAllowsAnyRoot
* - kCFStreamSSLValidatesCertificateChain
* - kCFStreamSSLPeerName
* - kCFStreamSSLCertificates
* - kCFStreamSSLIsServer
如果你傳遞空或者空字典缀辩,將使用默認(rèn)的字典
默認(rèn)設(shè)置將檢查以確保由簽署可信的第三方證書機(jī)構(gòu)和沒有過期的遠(yuǎn)程連接的證書
然而,它不會(huì)驗(yàn)證證書上的名字踪央,除非你給它一個(gè)名字臀玄,通過kCFStreamSSLPeerName鍵去驗(yàn)證
這對(duì)安全的影響是重要的理解
想象一下你正試圖創(chuàng)建一個(gè)到MySecureServer.com的安全連接,但因?yàn)橐粋€(gè)被攻擊的DNS服務(wù)器杯瞻,所以你的socket被定向到MaliciousServer.com
如果你只是使用默認(rèn)設(shè)置镐牺,MaliciousServer.com 有一個(gè)有效的證書
默認(rèn)設(shè)置將無法監(jiān)測(cè)到任何問題,因?yàn)樽C書是有效的
在這個(gè)特殊的情況下魁莉,要妥善保護(hù)你的連接睬涧,應(yīng)設(shè)置kCFStreamSSLPeerName性質(zhì)為MySecureServer.com.
如果事前你不知道對(duì)等的名字的遠(yuǎn)程主機(jī)(例如募胃,你不確認(rèn)它是domain.com" or "www.domain.com"),那么你可以使用默認(rèn)設(shè)置來驗(yàn)證證書畦浓,然后在獲得驗(yàn)證的發(fā)行后使用X509Certificate類來驗(yàn)證,X509Certificate類的CocoaAsyncSocket開源項(xiàng)目的一部分
-(void)enablePrebuffering
對(duì)于處理readDataToData請(qǐng)求痹束,數(shù)據(jù)是必須從socket以小增量的方式讀取出來的
性能通過允許AsyncSocket去一次性讀大塊的數(shù)據(jù)和存儲(chǔ)任何一個(gè)小的內(nèi)部緩沖區(qū)溢出的東西來大大提高
這被稱為預(yù)緩沖,就好像一些數(shù)據(jù)在你要求它之前就可能被讀取出來
如果你經(jīng)常使用readDataToData讶请,使用預(yù)緩沖會(huì)有更好的性能祷嘶,尤其是在iphone上
默認(rèn)的預(yù)緩沖狀態(tài)是由DEFAULT_PREBUFFERING 定義控制的,強(qiáng)烈建議設(shè)置其為yes
這方法存在一些預(yù)緩沖需要一些不可預(yù)見的原因被默認(rèn)禁用的情況夺溢,這時(shí)论巍,這種方法存在允許當(dāng)就緒時(shí),可輕松啟用預(yù)緩沖
-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;
當(dāng)你創(chuàng)建一個(gè)AsyncSocket风响,它被添加到當(dāng)前線程runloop
對(duì)于手動(dòng)創(chuàng)建的socket嘉汰,在線程上你打算使用它,它是最容易簡單的創(chuàng)建的線程上的socket
當(dāng)一個(gè)新的socket被接受状勤,委托方法 onSocket:wantsRunLoopForNewSocket 會(huì)被調(diào)用 允許你在一個(gè)單獨(dú)的線程上放置socket鞋怀,這個(gè)工作最好結(jié)合在同一個(gè)線程池設(shè)計(jì)
如果,但是持搜,在一個(gè)單獨(dú)的線程上密似,在之后的時(shí)間,你需要移動(dòng)一個(gè)socket葫盼,這個(gè)方法可以用來完成任務(wù)
此方法必須從 當(dāng)前運(yùn)行的 線程/runloop 的socket 調(diào)用
注意:此方法調(diào)用后残腌,所有進(jìn)一步的方法應(yīng)該從給定的runloop上調(diào)用這個(gè)對(duì)象
此外,所有委托調(diào)用將會(huì)發(fā)送到給定的runloop
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
- (BOOL)addRunLoopMode:(NSString *)runLoopMode;
- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;
允許你配置 socket 使用的 運(yùn)行循環(huán)模式
運(yùn)行循環(huán)模式設(shè)置默認(rèn)是NSRunLoopCommonModes
如果你想你的socket 在其他模式下繼續(xù)操作贫导,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes
可接受的socket將自動(dòng) 繼承相同的運(yùn)行循環(huán)模式就像偵聽socket
注意:NSRunLoopCommonModes 定義在10.5废累,對(duì)于之前的版本可使用 kCFRunLoopCommonModes
-(NSArray *)runLoopModes
返回當(dāng)前正在運(yùn)行的循環(huán)模式的AsyncSocket實(shí)例, run loop modes的默認(rèn)設(shè)置是NSDefaultRunLoopMode
-(NSData *)unreadData;
一個(gè)錯(cuò)誤的事件脱盲,在 onSocket:willDisconnectWithError: 將會(huì)被調(diào)用 去讀取留在socket上的任何數(shù)據(jù)
+ (NSData *)CRLFData; // 0x0D0A
各方法的解析
-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
發(fā)生錯(cuò)誤,socket關(guān)閉日缨,可以在call-back過程調(diào)用"unreadData"去取得socket的最后的數(shù)據(jù)字節(jié)钱反,當(dāng)連接的時(shí)候,該委托方法在 onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調(diào)用
-(void)onSocketDidDisconnect:(ASyncSocket *)sock;
當(dāng)socket由于或沒有錯(cuò)誤而斷開連接匣距,如果你想要在斷開連接后release socket面哥,在此方法工作,而在onSocket:willDisconnectWithError 釋放則不安全
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
當(dāng)產(chǎn)生一個(gè)socket去處理連接時(shí)調(diào)用毅待,此方法會(huì)返回 線程上的run-loop 的新的socket和其應(yīng)處理的委托尚卫,如果省略,則使用[NSRunLoop cunrrentRunLoop]
-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;
-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;
當(dāng)socket連接正準(zhǔn)備讀和寫的時(shí)候調(diào)用尸红,host屬性是一個(gè)IP地址吱涉,而不是一個(gè)DNS 名稱
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;
當(dāng)socket已完成所要求的數(shù)據(jù)讀入內(nèi)存時(shí)調(diào)用刹泄,如果有錯(cuò)誤則不調(diào)用
-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;
當(dāng)一個(gè)socket讀取數(shù)據(jù),但尚未完成讀操作的時(shí)候調(diào)用怎爵,如果使用 readToData: or readToLength: 方法 會(huì)發(fā)生,可以被用來更新進(jìn)度條等東西
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
當(dāng)一個(gè)socket已完成請(qǐng)求數(shù)據(jù)的寫入時(shí)候調(diào)用
-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
當(dāng)一個(gè)socket寫入一些數(shù)據(jù)特石,但還沒有完成整個(gè)寫入時(shí)調(diào)用,它可以用來更新進(jìn)度條等東西
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length
使用讀操作已超時(shí)但還沒完成時(shí)調(diào)用鳖链,此方法允許隨意延遲超時(shí)姆蘸,如果返回一個(gè)正的時(shí)間間隔,讀取的超時(shí)將有一定量的擴(kuò)展芙委,如果不實(shí)現(xiàn)這個(gè)方法逞敷,或會(huì)像往常一樣返回一個(gè)負(fù)的時(shí)間間隔,elapsed參數(shù)是 原超時(shí)的總和灌侣,加上先前通過這種方法添加的任何補(bǔ)充推捐, length參數(shù)是 讀操作到目前為止已讀取的字節(jié)數(shù), 注意顶瞳,如果返回正數(shù)的話玖姑,這個(gè)方法可能被一個(gè)單獨(dú)的讀取多次調(diào)用
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;
如果一個(gè)寫操作已達(dá)到其超時(shí)但還沒完成時(shí)調(diào)用,同上
-(void)onSocketDidSecure:(AsyncSocket *)sock;
在socket成功完成ssl/tls協(xié)商時(shí)調(diào)用慨菱,此方法除非你使用提供startTLS方法時(shí)候才調(diào)用焰络,
如果ssl/tls是無效的證書,socket將會(huì)立即關(guān)閉符喝,onSocket:willDisconnectWithError:代理方法竟會(huì)與特定的ssl錯(cuò)誤代碼一起調(diào)用
-(BOOL)canSafelySetDelegate
用來查看在改變它之前闪彼,是否帶有與當(dāng)前的委托有懸而未決的業(yè)務(wù)(讀/寫)。當(dāng)然协饲,應(yīng)在安全連接或接受委托之前改變委托
一旦接收或連接方法之一被調(diào)用畏腕,AsyncSocket實(shí)例會(huì)被鎖定,其他接收/連接方法在沒有先斷開socket不會(huì)被調(diào)用
如果嘗試失敗或超時(shí)茉稠,這些方法要么返回NO 要么調(diào)用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect
當(dāng)傳入的連接被接受描馅,AsyncSocket調(diào)用多個(gè)委托方法。這些方法按照時(shí)間順序排列:
1.onSocket:didAcceptNewSocket:
2.onSocket:wantsRunLoopForNewSocket:
3. onSocketWillConnect:
你的服務(wù)器的代碼將需要保留公認(rèn)的socket(如果要接受它)而线,最好的地方是要做到這一點(diǎn)可能在onSocket:didAcceptNewSocket:方法
在讀和寫流已經(jīng)為新接受的socket設(shè)置铭污,onSocket:didConnectToHost:port 方法將在適當(dāng)?shù)倪\(yùn)行循環(huán)調(diào)用
多線程注意,如果要想通過實(shí)施onSocket:wantsRunLoopForNewSocket:膀篮,移動(dòng)另一個(gè)新接受的socket去到另一個(gè)循環(huán)的socket嘹狞。然后,應(yīng)該在調(diào)用讀和寫或者startTLS方法前誓竿,等待直到onSocket:didConnectToHost:port:方法磅网。否則讀和寫時(shí)間原定于不正確的runloop,混亂可能會(huì)隨之而來
-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;
告訴socket開始聽取和接受制定端口上的連接筷屡,當(dāng)一個(gè)連接到來的時(shí)候涧偷,AsyncSocket實(shí)例將調(diào)用各種委托方法簸喂,socket將聽取所有可用的接口(wifi,以太網(wǎng)等)
-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;
連接給定的主機(jī)和端口,主機(jī)hostname可以是域名或者是Ip地址
-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;
連接到一個(gè)給定的地址嫂丙,制定一個(gè)sockaddr結(jié)構(gòu)包裹住一個(gè)NSData對(duì)象,例如娘赴,NSData對(duì)象從NSNetService的地址方法返回,如果有一個(gè)現(xiàn)有的sockaddr結(jié)構(gòu),可以將它轉(zhuǎn)換到一個(gè)NSData對(duì)象跟啤,像這樣:
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;
立即斷開诽表,任何未處理的讀或?qū)懚紝⒈粊G棄
如果socket還沒有斷開,在這個(gè)方法返回之前隅肥,onSocketDidDisconnect 委托方法將會(huì)被立即調(diào)用
注意推薦釋放AsyncSocket實(shí)例的方式:
[asyncSocket setDelegate:nil];
[asyncSocket disconnect];
[asyncSocket release];
-(void)disconnectAfterReading;
在已經(jīng)完成了所有懸而未決的讀取時(shí) 斷開,在調(diào)用之后竿奏,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入
- (NSString *)connectedHost;
- (UInt16)connectedPort;
- (NSString *)localHost;
- (UInt16)localPort;
返回本地和遠(yuǎn)程主機(jī)和端口給連接的socket,如果沒有連接會(huì)返回nil或0,主機(jī)將會(huì)是一個(gè)IP地址
-(NSData *)connectedAddress
-(NSData *)localAddresss
返回本地和遠(yuǎn)程的地址給連接的socket腥放,指定一個(gè)socketaddr結(jié)構(gòu)包裹在一個(gè)NSData對(duì)象
readData和writeData方法不會(huì)是block(它們是異步的)
當(dāng)讀完成 onSocket:didReadData:withTag: 委托方法時(shí)調(diào)用
當(dāng)寫完成 onSocket:didWriteDataWithTag: 委托方法時(shí)調(diào)用
可以選擇任何讀/寫操作的超時(shí)設(shè)置(為了不超時(shí)泛啸,使用負(fù)時(shí)間間隔。)
如果讀/寫操作超時(shí)秃症,相應(yīng)的 onSocket:shouldTimeout...委托方法被調(diào)用去選擇性地允許我們?nèi)パ娱L超時(shí)
超時(shí)后候址,onSocket:willDisconnectWithError: 方法被調(diào)用,緊接著是 onSocketDidDisconnect
tag是為了方便,可以使用它作為數(shù)組的索引种柑、步數(shù)岗仑、state id 、指針等
-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;
讀取socket上第一次成為可用的字節(jié),如果timeout值是負(fù)數(shù)的聚请,讀操作將不使用timeout
- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;
讀取socket上第一次成為可用的字節(jié)
字節(jié)將被追加到給定的字節(jié)緩沖區(qū)荠雕,從給定的偏移量開始
如果需要,給定的緩沖區(qū)大小將會(huì)自動(dòng)增加
如果timeout值是負(fù)數(shù)的驶赏,讀操作將不使用timeout
如果緩沖區(qū)為空炸卑,socket會(huì)為我們創(chuàng)建一個(gè)緩沖區(qū)
如果bufferOffset是大于給定的緩沖區(qū)的長度,該方法將無用煤傍,委托將不會(huì)被調(diào)用
如果你傳遞一個(gè)緩沖區(qū)盖文,當(dāng)AsyncSocket在使用它的時(shí)候你不能以任何方式改變它
完成之后,onSocket:didReadData:withTag 返回的數(shù)據(jù)將是一個(gè)給定的緩沖區(qū)的子集
也就是說蚯姆,它將會(huì)被引用到被追加的給定的緩沖區(qū)的字節(jié)
-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取給定的字節(jié)數(shù)椅寺,如果length為0,方法將無用蒋失,委托將不會(huì)被調(diào)用
-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取給定的字節(jié)數(shù),在給定的偏移開始,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的"data"參數(shù)
如果傳遞0或者0長度的數(shù)據(jù)桐玻,"data"參數(shù)篙挽,該方法將無用,委托將不會(huì)被調(diào)用
從socket讀取一行镊靴,使用"data"參數(shù)作為行的分隔符 (如HTTP的CRLF)
注意铣卡,此方法不是字符集链韭,因此,如果一個(gè)分隔符出現(xiàn)煮落,它自然可以作為進(jìn)行編碼的一部分敞峭,讀取將提前結(jié)束
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的“data”參數(shù),在給定的偏移量開始蝉仇,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)旋讹。
從socket讀取一行,使用"data"參數(shù)作為行的分隔符(如HTTP的CRLF)
-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;
將data寫入socket轿衔,當(dāng)完成的時(shí)候委托被調(diào)用
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
返回當(dāng)前讀或?qū)懙倪M(jìn)度沉迹,從0.0 到 1.0 或者 如果沒有讀/寫的時(shí)候返回Nan(使用isNan來檢查)
tag、done害驹、total如果不為空的話鞭呕,它們將會(huì)被填補(bǔ)
- (void)startTLS:(NSDictionary *)tlsSettings;
確保使用ssl/tls連接
這方法可被隨時(shí)調(diào)用,tls握手將會(huì)發(fā)生在所有懸而未決的讀/寫完成之后宛官。這緊跟著一個(gè)發(fā)送依賴 StartTLS消息的協(xié)議選項(xiàng)葫松,在排隊(duì)升級(jí)到TLS的同一時(shí)間,而不必等待寫入完成底洗。在這個(gè)方法被調(diào)用后,任何讀寫計(jì)劃 將會(huì)發(fā)生在安全鏈接
對(duì)于可能的keys和TLS設(shè)置的值是有據(jù)可查的
一些可能的keys是:
* - kCFStreamSSLLevel
* - kCFStreamSSLAllowsExpiredCertificates
* - kCFStreamSSLAllowsExpiredRoots
* - kCFStreamSSLAllowsAnyRoot
* - kCFStreamSSLValidatesCertificateChain
* - kCFStreamSSLPeerName
* - kCFStreamSSLCertificates
* - kCFStreamSSLIsServer
如果你傳遞空或者空字典腋么,將使用默認(rèn)的字典
默認(rèn)設(shè)置將檢查以確保由簽署可信的第三方證書機(jī)構(gòu)和沒有過期的遠(yuǎn)程連接的證書
然而,它不會(huì)驗(yàn)證證書上的名字枷恕,除非你給它一個(gè)名字党晋,通過kCFStreamSSLPeerName鍵去驗(yàn)證
這對(duì)安全的影響是重要的理解
想象一下你正試圖創(chuàng)建一個(gè)到MySecureServer.com的安全連接,但因?yàn)橐粋€(gè)被攻擊的DNS服務(wù)器徐块,所以你的socket被定向到MaliciousServer.com
如果你只是使用默認(rèn)設(shè)置未玻,MaliciousServer.com 有一個(gè)有效的證書
默認(rèn)設(shè)置將無法監(jiān)測(cè)到任何問題,因?yàn)樽C書是有效的
在這個(gè)特殊的情況下胡控,要妥善保護(hù)你的連接扳剿,應(yīng)設(shè)置kCFStreamSSLPeerName性質(zhì)為MySecureServer.com.
如果事前你不知道對(duì)等的名字的遠(yuǎn)程主機(jī)(例如,你不確認(rèn)它是domain.com" or "www.domain.com")昼激,那么你可以使用默認(rèn)設(shè)置來驗(yàn)證證書庇绽,然后在獲得驗(yàn)證的發(fā)行后使用X509Certificate類來驗(yàn)證,X509Certificate類的CocoaAsyncSocket開源項(xiàng)目的一部分
-(void)enablePrebuffering
對(duì)于處理readDataToData請(qǐng)求,數(shù)據(jù)是必須從socket以小增量的方式讀取出來的
性能通過允許AsyncSocket去一次性讀大塊的數(shù)據(jù)和存儲(chǔ)任何一個(gè)小的內(nèi)部緩沖區(qū)溢出的東西來大大提高
這被稱為預(yù)緩沖橙困,就好像一些數(shù)據(jù)在你要求它之前就可能被讀取出來
如果你經(jīng)常使用readDataToData瞧掺,使用預(yù)緩沖會(huì)有更好的性能,尤其是在iphone上
默認(rèn)的預(yù)緩沖狀態(tài)是由DEFAULT_PREBUFFERING 定義控制的凡傅,強(qiáng)烈建議設(shè)置其為yes
這方法存在一些預(yù)緩沖需要一些不可預(yù)見的原因被默認(rèn)禁用的情況辟狈,這時(shí),這種方法存在允許當(dāng)就緒時(shí),可輕松啟用預(yù)緩沖
-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;
當(dāng)你創(chuàng)建一個(gè)AsyncSocket哼转,它被添加到當(dāng)前線程runloop
對(duì)于手動(dòng)創(chuàng)建的socket明未,在線程上你打算使用它,它是最容易簡單的創(chuàng)建的線程上的socket
當(dāng)一個(gè)新的socket被接受壹蔓,委托方法 onSocket:wantsRunLoopForNewSocket 會(huì)被調(diào)用 允許你在一個(gè)單獨(dú)的線程上放置socket趟妥,這個(gè)工作最好結(jié)合在同一個(gè)線程池設(shè)計(jì)
如果,但是佣蓉,在一個(gè)單獨(dú)的線程上披摄,在之后的時(shí)間,你需要移動(dòng)一個(gè)socket偏螺,這個(gè)方法可以用來完成任務(wù)
此方法必須從 當(dāng)前運(yùn)行的 線程/runloop 的socket 調(diào)用
注意:此方法調(diào)用后行疏,所有進(jìn)一步的方法應(yīng)該從給定的runloop上調(diào)用這個(gè)對(duì)象
此外,所有委托調(diào)用將會(huì)發(fā)送到給定的runloop
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
- (BOOL)addRunLoopMode:(NSString *)runLoopMode;
- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;
允許你配置 socket 使用的 運(yùn)行循環(huán)模式
運(yùn)行循環(huán)模式設(shè)置默認(rèn)是NSRunLoopCommonModes
如果你想你的socket 在其他模式下繼續(xù)操作,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes
可接受的socket將自動(dòng) 繼承相同的運(yùn)行循環(huán)模式就像偵聽socket
注意:NSRunLoopCommonModes 定義在10.5,對(duì)于之前的版本可使用 kCFRunLoopCommonModes
-(NSArray *)runLoopModes
返回當(dāng)前正在運(yùn)行的循環(huán)模式的AsyncSocket實(shí)例板壮, run loop modes的默認(rèn)設(shè)置是NSDefaultRunLoopMode
-(NSData *)unreadData;
一個(gè)錯(cuò)誤的事件,在 onSocket:willDisconnectWithError: 將會(huì)被調(diào)用 去讀取留在socket上的任何數(shù)據(jù)
+ (NSData *)CRLFData; // 0x0D0A
各方法的解析
-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
發(fā)生錯(cuò)誤贞让,socket關(guān)閉,可以在call-back過程調(diào)用"unreadData"去取得socket的最后的數(shù)據(jù)字節(jié)柳譬,當(dāng)連接的時(shí)候喳张,該委托方法在 onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調(diào)用
-(void)onSocketDidDisconnect:(ASyncSocket *)sock;
當(dāng)socket由于或沒有錯(cuò)誤而斷開連接,如果你想要在斷開連接后release socket美澳,在此方法工作销部,而在onSocket:willDisconnectWithError 釋放則不安全
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
當(dāng)產(chǎn)生一個(gè)socket去處理連接時(shí)調(diào)用,此方法會(huì)返回 線程上的run-loop 的新的socket和其應(yīng)處理的委托制跟,如果省略舅桩,則使用[NSRunLoop cunrrentRunLoop]
-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;
-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;
當(dāng)socket連接正準(zhǔn)備讀和寫的時(shí)候調(diào)用,host屬性是一個(gè)IP地址雨膨,而不是一個(gè)DNS 名稱
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;
當(dāng)socket已完成所要求的數(shù)據(jù)讀入內(nèi)存時(shí)調(diào)用擂涛,如果有錯(cuò)誤則不調(diào)用
-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;
當(dāng)一個(gè)socket讀取數(shù)據(jù),但尚未完成讀操作的時(shí)候調(diào)用聊记,如果使用 readToData: or readToLength: 方法 會(huì)發(fā)生,可以被用來更新進(jìn)度條等東西
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
當(dāng)一個(gè)socket已完成請(qǐng)求數(shù)據(jù)的寫入時(shí)候調(diào)用
-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
當(dāng)一個(gè)socket寫入一些數(shù)據(jù)撒妈,但還沒有完成整個(gè)寫入時(shí)調(diào)用,它可以用來更新進(jìn)度條等東西
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length
使用讀操作已超時(shí)但還沒完成時(shí)調(diào)用排监,此方法允許隨意延遲超時(shí)狰右,如果返回一個(gè)正的時(shí)間間隔,讀取的超時(shí)將有一定量的擴(kuò)展舆床,如果不實(shí)現(xiàn)這個(gè)方法棋蚌,或會(huì)像往常一樣返回一個(gè)負(fù)的時(shí)間間隔,elapsed參數(shù)是 原超時(shí)的總和,加上先前通過這種方法添加的任何補(bǔ)充附鸽, length參數(shù)是 讀操作到目前為止已讀取的字節(jié)數(shù), 注意瞒瘸,如果返回正數(shù)的話坷备,這個(gè)方法可能被一個(gè)單獨(dú)的讀取多次調(diào)用
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;
如果一個(gè)寫操作已達(dá)到其超時(shí)但還沒完成時(shí)調(diào)用,同上
-(void)onSocketDidSecure:(AsyncSocket *)sock;
在socket成功完成ssl/tls協(xié)商時(shí)調(diào)用情臭,此方法除非你使用提供startTLS方法時(shí)候才調(diào)用省撑,
如果ssl/tls是無效的證書,socket將會(huì)立即關(guān)閉俯在,onSocket:willDisconnectWithError:代理方法竟會(huì)與特定的ssl錯(cuò)誤代碼一起調(diào)用
-(BOOL)canSafelySetDelegate
用來查看在改變它之前竟秫,是否帶有與當(dāng)前的委托有懸而未決的業(yè)務(wù)(讀/寫)。當(dāng)然跷乐,應(yīng)在安全連接或接受委托之前改變委托
一旦接收或連接方法之一被調(diào)用肥败,AsyncSocket實(shí)例會(huì)被鎖定,其他接收/連接方法在沒有先斷開socket不會(huì)被調(diào)用
如果嘗試失敗或超時(shí)愕提,這些方法要么返回NO 要么調(diào)用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect
當(dāng)傳入的連接被接受馒稍,AsyncSocket調(diào)用多個(gè)委托方法。這些方法按照時(shí)間順序排列:
1.onSocket:didAcceptNewSocket:
2.onSocket:wantsRunLoopForNewSocket:
3. onSocketWillConnect:
你的服務(wù)器的代碼將需要保留公認(rèn)的socket(如果要接受它)浅侨,最好的地方是要做到這一點(diǎn)可能在onSocket:didAcceptNewSocket:方法
在讀和寫流已經(jīng)為新接受的socket設(shè)置纽谒,onSocket:didConnectToHost:port 方法將在適當(dāng)?shù)倪\(yùn)行循環(huán)調(diào)用
多線程注意,如果要想通過實(shí)施onSocket:wantsRunLoopForNewSocket:如输,移動(dòng)另一個(gè)新接受的socket去到另一個(gè)循環(huán)的socket鼓黔。然后,應(yīng)該在調(diào)用讀和寫或者startTLS方法前不见,等待直到onSocket:didConnectToHost:port:方法澳化。否則讀和寫時(shí)間原定于不正確的runloop,混亂可能會(huì)隨之而來
-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;
告訴socket開始聽取和接受制定端口上的連接脖祈,當(dāng)一個(gè)連接到來的時(shí)候肆捕,AsyncSocket實(shí)例將調(diào)用各種委托方法,socket將聽取所有可用的接口(wifi,以太網(wǎng)等)
-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;
連接給定的主機(jī)和端口盖高,主機(jī)hostname可以是域名或者是Ip地址
-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;
連接到一個(gè)給定的地址慎陵,制定一個(gè)sockaddr結(jié)構(gòu)包裹住一個(gè)NSData對(duì)象,例如,NSData對(duì)象從NSNetService的地址方法返回,如果有一個(gè)現(xiàn)有的sockaddr結(jié)構(gòu)喻奥,可以將它轉(zhuǎn)換到一個(gè)NSData對(duì)象席纽,像這樣:
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;
立即斷開,任何未處理的讀或?qū)懚紝⒈粊G棄
如果socket還沒有斷開撞蚕,在這個(gè)方法返回之前润梯,onSocketDidDisconnect 委托方法將會(huì)被立即調(diào)用
注意推薦釋放AsyncSocket實(shí)例的方式:
[asyncSocket setDelegate:nil];
[asyncSocket disconnect];
[asyncSocket release];
-(void)disconnectAfterReading;
在已經(jīng)完成了所有懸而未決的讀取時(shí) 斷開,在調(diào)用之后,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入
- (NSString *)connectedHost;
- (UInt16)connectedPort;
- (NSString *)localHost;
- (UInt16)localPort;
返回本地和遠(yuǎn)程主機(jī)和端口給連接的socket,如果沒有連接會(huì)返回nil或0,主機(jī)將會(huì)是一個(gè)IP地址
-(NSData *)connectedAddress
-(NSData *)localAddresss
返回本地和遠(yuǎn)程的地址給連接的socket纺铭,指定一個(gè)socketaddr結(jié)構(gòu)包裹在一個(gè)NSData對(duì)象
readData和writeData方法不會(huì)是block(它們是異步的)
當(dāng)讀完成 onSocket:didReadData:withTag: 委托方法時(shí)調(diào)用
當(dāng)寫完成 onSocket:didWriteDataWithTag: 委托方法時(shí)調(diào)用
可以選擇任何讀/寫操作的超時(shí)設(shè)置(為了不超時(shí)寇钉,使用負(fù)時(shí)間間隔。)
如果讀/寫操作超時(shí)舶赔,相應(yīng)的 onSocket:shouldTimeout...委托方法被調(diào)用去選擇性地允許我們?nèi)パ娱L超時(shí)
超時(shí)后扫倡,onSocket:willDisconnectWithError: 方法被調(diào)用,緊接著是 onSocketDidDisconnect
tag是為了方便,可以使用它作為數(shù)組的索引竟纳、步數(shù)撵溃、state id 、指針等
-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;
讀取socket上第一次成為可用的字節(jié),如果timeout值是負(fù)數(shù)的锥累,讀操作將不使用timeout
- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;
讀取socket上第一次成為可用的字節(jié)
字節(jié)將被追加到給定的字節(jié)緩沖區(qū)缘挑,從給定的偏移量開始
如果需要,給定的緩沖區(qū)大小將會(huì)自動(dòng)增加
如果timeout值是負(fù)數(shù)的桶略,讀操作將不使用timeout
如果緩沖區(qū)為空语淘,socket會(huì)為我們創(chuàng)建一個(gè)緩沖區(qū)
如果bufferOffset是大于給定的緩沖區(qū)的長度,該方法將無用删性,委托將不會(huì)被調(diào)用
如果你傳遞一個(gè)緩沖區(qū)亏娜,當(dāng)AsyncSocket在使用它的時(shí)候你不能以任何方式改變它
完成之后,onSocket:didReadData:withTag 返回的數(shù)據(jù)將是一個(gè)給定的緩沖區(qū)的子集
也就是說蹬挺,它將會(huì)被引用到被追加的給定的緩沖區(qū)的字節(jié)
-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取給定的字節(jié)數(shù)维贺,如果length為0,方法將無用巴帮,委托將不會(huì)被調(diào)用
-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取給定的字節(jié)數(shù),在給定的偏移開始溯泣,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的"data"參數(shù)
如果傳遞0或者0長度的數(shù)據(jù),"data"參數(shù)榕茧,該方法將無用垃沦,委托將不會(huì)被調(diào)用
從socket讀取一行,使用"data"參數(shù)作為行的分隔符 (如HTTP的CRLF)
注意用押,此方法不是字符集肢簿,因此,如果一個(gè)分隔符出現(xiàn)蜻拨,它自然可以作為進(jìn)行編碼的一部分池充,讀取將提前結(jié)束
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的“data”參數(shù),在給定的偏移量開始缎讼,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)收夸。
從socket讀取一行,使用"data"參數(shù)作為行的分隔符(如HTTP的CRLF)
-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;
將data寫入socket血崭,當(dāng)完成的時(shí)候委托被調(diào)用
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
返回當(dāng)前讀或?qū)懙倪M(jìn)度卧惜,從0.0 到 1.0 或者 如果沒有讀/寫的時(shí)候返回Nan(使用isNan來檢查)
tag厘灼、done、total如果不為空的話咽瓷,它們將會(huì)被填補(bǔ)
- (void)startTLS:(NSDictionary *)tlsSettings;
確保使用ssl/tls連接
這方法可被隨時(shí)調(diào)用设凹,tls握手將會(huì)發(fā)生在所有懸而未決的讀/寫完成之后。這緊跟著一個(gè)發(fā)送依賴 StartTLS消息的協(xié)議選項(xiàng)茅姜,在排隊(duì)升級(jí)到TLS的同一時(shí)間围来,而不必等待寫入完成。在這個(gè)方法被調(diào)用后,任何讀寫計(jì)劃 將會(huì)發(fā)生在安全鏈接
對(duì)于可能的keys和TLS設(shè)置的值是有據(jù)可查的
一些可能的keys是:
* - kCFStreamSSLLevel
* - kCFStreamSSLAllowsExpiredCertificates
* - kCFStreamSSLAllowsExpiredRoots
* - kCFStreamSSLAllowsAnyRoot
* - kCFStreamSSLValidatesCertificateChain
* - kCFStreamSSLPeerName
* - kCFStreamSSLCertificates
* - kCFStreamSSLIsServer
如果你傳遞空或者空字典匈睁,將使用默認(rèn)的字典
默認(rèn)設(shè)置將檢查以確保由簽署可信的第三方證書機(jī)構(gòu)和沒有過期的遠(yuǎn)程連接的證書
然而,它不會(huì)驗(yàn)證證書上的名字桶错,除非你給它一個(gè)名字航唆,通過kCFStreamSSLPeerName鍵去驗(yàn)證
這對(duì)安全的影響是重要的理解
想象一下你正試圖創(chuàng)建一個(gè)到MySecureServer.com的安全連接,但因?yàn)橐粋€(gè)被攻擊的DNS服務(wù)器院刁,所以你的socket被定向到MaliciousServer.com
如果你只是使用默認(rèn)設(shè)置糯钙,MaliciousServer.com 有一個(gè)有效的證書
默認(rèn)設(shè)置將無法監(jiān)測(cè)到任何問題,因?yàn)樽C書是有效的
在這個(gè)特殊的情況下退腥,要妥善保護(hù)你的連接任岸,應(yīng)設(shè)置kCFStreamSSLPeerName性質(zhì)為MySecureServer.com.
如果事前你不知道對(duì)等的名字的遠(yuǎn)程主機(jī)(例如,你不確認(rèn)它是domain.com" or "www.domain.com")狡刘,那么你可以使用默認(rèn)設(shè)置來驗(yàn)證證書享潜,然后在獲得驗(yàn)證的發(fā)行后使用X509Certificate類來驗(yàn)證,X509Certificate類的CocoaAsyncSocket開源項(xiàng)目的一部分
-(void)enablePrebuffering
對(duì)于處理readDataToData請(qǐng)求,數(shù)據(jù)是必須從socket以小增量的方式讀取出來的
性能通過允許AsyncSocket去一次性讀大塊的數(shù)據(jù)和存儲(chǔ)任何一個(gè)小的內(nèi)部緩沖區(qū)溢出的東西來大大提高
這被稱為預(yù)緩沖嗅蔬,就好像一些數(shù)據(jù)在你要求它之前就可能被讀取出來
如果你經(jīng)常使用readDataToData剑按,使用預(yù)緩沖會(huì)有更好的性能,尤其是在iphone上
默認(rèn)的預(yù)緩沖狀態(tài)是由DEFAULT_PREBUFFERING 定義控制的澜术,強(qiáng)烈建議設(shè)置其為yes
這方法存在一些預(yù)緩沖需要一些不可預(yù)見的原因被默認(rèn)禁用的情況艺蝴,這時(shí),這種方法存在允許當(dāng)就緒時(shí)鸟废,可輕松啟用預(yù)緩沖
-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;
當(dāng)你創(chuàng)建一個(gè)AsyncSocket猜敢,它被添加到當(dāng)前線程runloop
對(duì)于手動(dòng)創(chuàng)建的socket,在線程上你打算使用它盒延,它是最容易簡單的創(chuàng)建的線程上的socket
當(dāng)一個(gè)新的socket被接受缩擂,委托方法 onSocket:wantsRunLoopForNewSocket 會(huì)被調(diào)用 允許你在一個(gè)單獨(dú)的線程上放置socket,這個(gè)工作最好結(jié)合在同一個(gè)線程池設(shè)計(jì)
如果兰英,但是撇叁,在一個(gè)單獨(dú)的線程上,在之后的時(shí)間畦贸,你需要移動(dòng)一個(gè)socket陨闹,這個(gè)方法可以用來完成任務(wù)
此方法必須從 當(dāng)前運(yùn)行的 線程/runloop 的socket 調(diào)用
注意:此方法調(diào)用后楞捂,所有進(jìn)一步的方法應(yīng)該從給定的runloop上調(diào)用這個(gè)對(duì)象
此外,所有委托調(diào)用將會(huì)發(fā)送到給定的runloop
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
- (BOOL)addRunLoopMode:(NSString *)runLoopMode;
- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;
允許你配置 socket 使用的 運(yùn)行循環(huán)模式
運(yùn)行循環(huán)模式設(shè)置默認(rèn)是NSRunLoopCommonModes
如果你想你的socket 在其他模式下繼續(xù)操作趋厉,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes
可接受的socket將自動(dòng) 繼承相同的運(yùn)行循環(huán)模式就像偵聽socket
注意:NSRunLoopCommonModes 定義在10.5寨闹,對(duì)于之前的版本可使用 kCFRunLoopCommonModes
-(NSArray *)runLoopModes
返回當(dāng)前正在運(yùn)行的循環(huán)模式的AsyncSocket實(shí)例, run loop modes的默認(rèn)設(shè)置是NSDefaultRunLoopMode
-(NSData *)unreadData;
一個(gè)錯(cuò)誤的事件君账,在 onSocket:willDisconnectWithError: 將會(huì)被調(diào)用 去讀取留在socket上的任何數(shù)據(jù)
+ (NSData *)CRLFData; // 0x0D0A
各方法的解析
-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
發(fā)生錯(cuò)誤繁堡,socket關(guān)閉,可以在call-back過程調(diào)用"unreadData"去取得socket的最后的數(shù)據(jù)字節(jié)乡数,當(dāng)連接的時(shí)候椭蹄,該委托方法在 onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調(diào)用
發(fā)生錯(cuò)誤,socket關(guān)閉净赴,可以在call-back過程調(diào)用"unreadData"去取得socket的最后的數(shù)據(jù)字節(jié)绳矩,當(dāng)連接的時(shí)候,該委托方法在 onSocket:didAcceptNewSocket: 或者 onSocket:didConnectToHost: 之前調(diào)用
-(void)onSocketDidDisconnect:(ASyncSocket *)sock;
當(dāng)socket由于或沒有錯(cuò)誤而斷開連接玖翅,如果你想要在斷開連接后release socket翼馆,在此方法工作,而在onSocket:willDisconnectWithError 釋放則不安全
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
當(dāng)產(chǎn)生一個(gè)socket去處理連接時(shí)調(diào)用金度,此方法會(huì)返回 線程上的run-loop 的新的socket和其應(yīng)處理的委托应媚,如果省略,則使用[NSRunLoop cunrrentRunLoop]
當(dāng)產(chǎn)生一個(gè)socket去處理連接時(shí)調(diào)用猜极,此方法會(huì)返回 線程上的run-loop 的新的socket和其應(yīng)處理的委托中姜,如果省略,則使用[NSRunLoop cunrrentRunLoop]
-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;
-(void)onSocket:(AsyncSocket *)sock didConnectToHost :(NSString *)host port:(UINt16)port;
當(dāng)socket連接正準(zhǔn)備讀和寫的時(shí)候調(diào)用跟伏,host屬性是一個(gè)IP地址扎筒,而不是一個(gè)DNS 名稱
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long) tag;
當(dāng)socket已完成所要求的數(shù)據(jù)讀入內(nèi)存時(shí)調(diào)用,如果有錯(cuò)誤則不調(diào)用
-(void)onSocket:(Asyncsocket *)sock didReadPartialDataOfLength:(NSUInteger)partiaLength tag:(long)tag;
當(dāng)一個(gè)socket讀取數(shù)據(jù)酬姆,但尚未完成讀操作的時(shí)候調(diào)用嗜桌,如果使用 readToData: or readToLength: 方法 會(huì)發(fā)生,可以被用來更新進(jìn)度條等東西
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
當(dāng)一個(gè)socket已完成請(qǐng)求數(shù)據(jù)的寫入時(shí)候調(diào)用
-(void)onSocket:(AsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag;
當(dāng)一個(gè)socket寫入一些數(shù)據(jù),但還沒有完成整個(gè)寫入時(shí)調(diào)用辞色,它可以用來更新進(jìn)度條等東西
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)exapsed bytesDone:(NSUInteger)length
使用讀操作已超時(shí)但還沒完成時(shí)調(diào)用骨宠,此方法允許隨意延遲超時(shí),如果返回一個(gè)正的時(shí)間間隔相满,讀取的超時(shí)將有一定量的擴(kuò)展层亿,如果不實(shí)現(xiàn)這個(gè)方法,或會(huì)像往常一樣返回一個(gè)負(fù)的時(shí)間間隔立美,elapsed參數(shù)是 原超時(shí)的總和匿又,加上先前通過這種方法添加的任何補(bǔ)充, length參數(shù)是 讀操作到目前為止已讀取的字節(jié)數(shù)建蹄, 注意碌更,如果返回正數(shù)的話裕偿,這個(gè)方法可能被一個(gè)單獨(dú)的讀取多次調(diào)用
-(NSTimeInterval)onSocket:(AsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length;
如果一個(gè)寫操作已達(dá)到其超時(shí)但還沒完成時(shí)調(diào)用,同上
-(void)onSocketDidSecure:(AsyncSocket *)sock;
在socket成功完成ssl/tls協(xié)商時(shí)調(diào)用痛单,此方法除非你使用提供startTLS方法時(shí)候才調(diào)用嘿棘,
如果ssl/tls是無效的證書,socket將會(huì)立即關(guān)閉旭绒,onSocket:willDisconnectWithError:代理方法竟會(huì)與特定的ssl錯(cuò)誤代碼一起調(diào)用
-(BOOL)canSafelySetDelegate
用來查看在改變它之前鸟妙,是否帶有與當(dāng)前的委托有懸而未決的業(yè)務(wù)(讀/寫)。當(dāng)然挥吵,應(yīng)在安全連接或接受委托之前改變委托
一旦接收或連接方法之一被調(diào)用重父,AsyncSocket實(shí)例會(huì)被鎖定,其他接收/連接方法在沒有先斷開socket不會(huì)被調(diào)用
如果嘗試失敗或超時(shí)忽匈,這些方法要么返回NO 要么調(diào)用 onSocket:willDisconnectWithError: 或 onSockedDidDisconnect
當(dāng)傳入的連接被接受坪郭,AsyncSocket調(diào)用多個(gè)委托方法。這些方法按照時(shí)間順序排列:
1.onSocket:didAcceptNewSocket:
2.onSocket:wantsRunLoopForNewSocket:
3. onSocketWillConnect:
你的服務(wù)器的代碼將需要保留公認(rèn)的socket(如果要接受它)脉幢,最好的地方是要做到這一點(diǎn)可能在onSocket:didAcceptNewSocket:方法
在讀和寫流已經(jīng)為新接受的socket設(shè)置,onSocket:didConnectToHost:port 方法將在適當(dāng)?shù)倪\(yùn)行循環(huán)調(diào)用
多線程注意嗦锐,如果要想通過實(shí)施onSocket:wantsRunLoopForNewSocket:嫌松,移動(dòng)另一個(gè)新接受的socket去到另一個(gè)循環(huán)的socket。然后奕污,應(yīng)該在調(diào)用讀和寫或者startTLS方法前萎羔,等待直到onSocket:didConnectToHost:port:方法。否則讀和寫時(shí)間原定于不正確的runloop碳默,混亂可能會(huì)隨之而來
-(BOOL)acceptOnPort:(UInit16)port error:(NSError **)errPtr;
告訴socket開始聽取和接受制定端口上的連接贾陷,當(dāng)一個(gè)連接到來的時(shí)候,AsyncSocket實(shí)例將調(diào)用各種委托方法嘱根,socket將聽取所有可用的接口(wifi,以太網(wǎng)等)
-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error :(NSError **)errPtr;
連接給定的主機(jī)和端口髓废,主機(jī)hostname可以是域名或者是Ip地址
-(BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError *)errPtr;
連接到一個(gè)給定的地址,制定一個(gè)sockaddr結(jié)構(gòu)包裹住一個(gè)NSData對(duì)象,例如该抒,NSData對(duì)象從NSNetService的地址方法返回,如果有一個(gè)現(xiàn)有的sockaddr結(jié)構(gòu)慌洪,可以將它轉(zhuǎn)換到一個(gè)NSData對(duì)象,像這樣:
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;
立即斷開凑保,任何未處理的讀或?qū)懚紝⒈粊G棄
如果socket還沒有斷開冈爹,在這個(gè)方法返回之前,onSocketDidDisconnect 委托方法將會(huì)被立即調(diào)用
注意推薦釋放AsyncSocket實(shí)例的方式:
[asyncSocket setDelegate:nil];
[asyncSocket disconnect];
[asyncSocket release];
-(void)disconnectAfterReading;
在已經(jīng)完成了所有懸而未決的讀取時(shí) 斷開,在調(diào)用之后欧引,讀取和寫入方法將無用,socket將斷開 即使仍有待寫入
- (NSString *)connectedHost;
- (UInt16)connectedPort;
- (NSString *)localHost;
- (UInt16)localPort;
返回本地和遠(yuǎn)程主機(jī)和端口給連接的socket频伤,如果沒有連接會(huì)返回nil或0,主機(jī)將會(huì)是一個(gè)IP地址
-(NSData *)connectedAddress
-(NSData *)localAddresss
返回本地和遠(yuǎn)程的地址給連接的socket,指定一個(gè)socketaddr結(jié)構(gòu)包裹在一個(gè)NSData對(duì)象
readData和writeData方法不會(huì)是block(它們是異步的)
當(dāng)讀完成 onSocket:didReadData:withTag: 委托方法時(shí)調(diào)用
當(dāng)寫完成 onSocket:didWriteDataWithTag: 委托方法時(shí)調(diào)用
可以選擇任何讀/寫操作的超時(shí)設(shè)置(為了不超時(shí)芝此,使用負(fù)時(shí)間間隔憋肖。)
如果讀/寫操作超時(shí)因痛,相應(yīng)的 onSocket:shouldTimeout...委托方法被調(diào)用去選擇性地允許我們?nèi)パ娱L超時(shí)
超時(shí)后,onSocket:willDisconnectWithError: 方法被調(diào)用,緊接著是 onSocketDidDisconnect
tag是為了方便瞬哼,可以使用它作為數(shù)組的索引婚肆、步數(shù)、state id 坐慰、指針等
-(void)readDataWithTimeout:(NSTimeInterval)tiemout tag:(long)tag;
讀取socket上第一次成為可用的字節(jié),如果timeout值是負(fù)數(shù)的较性,讀操作將不使用timeout
- (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInterger)offset tag:(long)tag;
讀取socket上第一次成為可用的字節(jié)
字節(jié)將被追加到給定的字節(jié)緩沖區(qū),從給定的偏移量開始
如果需要结胀,給定的緩沖區(qū)大小將會(huì)自動(dòng)增加
如果timeout值是負(fù)數(shù)的赞咙,讀操作將不使用timeout
如果緩沖區(qū)為空,socket會(huì)為我們創(chuàng)建一個(gè)緩沖區(qū)
如果bufferOffset是大于給定的緩沖區(qū)的長度糟港,該方法將無用攀操,委托將不會(huì)被調(diào)用
如果你傳遞一個(gè)緩沖區(qū),當(dāng)AsyncSocket在使用它的時(shí)候你不能以任何方式改變它
完成之后秸抚,onSocket:didReadData:withTag 返回的數(shù)據(jù)將是一個(gè)給定的緩沖區(qū)的子集
也就是說速和,它將會(huì)被引用到被追加的給定的緩沖區(qū)的字節(jié)
-(void)readDataToLength:(NSUInterger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取給定的字節(jié)數(shù),如果length為0剥汤,方法將無用颠放,委托將不會(huì)被調(diào)用
-(void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)tiemout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取給定的字節(jié)數(shù),在給定的偏移開始,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的"data"參數(shù)
如果傳遞0或者0長度的數(shù)據(jù)吭敢,"data"參數(shù)碰凶,該方法將無用,委托將不會(huì)被調(diào)用
從socket讀取一行鹿驼,使用"data"參數(shù)作為行的分隔符 (如HTTP的CRLF)
注意欲低,此方法不是字符集,因此畜晰,如果一個(gè)分隔符出現(xiàn)砾莱,它自然可以作為進(jìn)行編碼的一部分,讀取將提前結(jié)束
-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger) offset tag:(long)tag;
讀取字節(jié)直到(包括)傳入的作為分隔的“data”參數(shù)凄鼻,在給定的偏移量開始恤磷,字節(jié)將被追加到給定的字節(jié)緩沖區(qū)。
從socket讀取一行野宜,使用"data"參數(shù)作為行的分隔符(如HTTP的CRLF)
-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval) timeout tag:(long)tag;
將data寫入socket扫步,當(dāng)完成的時(shí)候委托被調(diào)用
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(NSUInteger *)done total:(NSUInteger *)total;
返回當(dāng)前讀或?qū)懙倪M(jìn)度,從0.0 到 1.0 或者 如果沒有讀/寫的時(shí)候返回Nan(使用isNan來檢查)
tag匈子、done河胎、total如果不為空的話,它們將會(huì)被填補(bǔ)
- (void)startTLS:(NSDictionary *)tlsSettings;
確保使用ssl/tls連接
這方法可被隨時(shí)調(diào)用虎敦,tls握手將會(huì)發(fā)生在所有懸而未決的讀/寫完成之后游岳。這緊跟著一個(gè)發(fā)送依賴 StartTLS消息的協(xié)議選項(xiàng)政敢,在排隊(duì)升級(jí)到TLS的同一時(shí)間,而不必等待寫入完成胚迫。在這個(gè)方法被調(diào)用后,任何讀寫計(jì)劃 將會(huì)發(fā)生在安全鏈接
對(duì)于可能的keys和TLS設(shè)置的值是有據(jù)可查的
一些可能的keys是:
* - kCFStreamSSLLevel
* - kCFStreamSSLAllowsExpiredCertificates
* - kCFStreamSSLAllowsExpiredRoots
* - kCFStreamSSLAllowsAnyRoot
* - kCFStreamSSLValidatesCertificateChain
* - kCFStreamSSLPeerName
* - kCFStreamSSLCertificates
* - kCFStreamSSLIsServer
如果你傳遞空或者空字典喷户,將使用默認(rèn)的字典
默認(rèn)設(shè)置將檢查以確保由簽署可信的第三方證書機(jī)構(gòu)和沒有過期的遠(yuǎn)程連接的證書
然而,它不會(huì)驗(yàn)證證書上的名字访锻,除非你給它一個(gè)名字褪尝,通過kCFStreamSSLPeerName鍵去驗(yàn)證
這對(duì)安全的影響是重要的理解
想象一下你正試圖創(chuàng)建一個(gè)到MySecureServer.com的安全連接,但因?yàn)橐粋€(gè)被攻擊的DNS服務(wù)器期犬,所以你的socket被定向到MaliciousServer.com
如果你只是使用默認(rèn)設(shè)置河哑,MaliciousServer.com 有一個(gè)有效的證書
默認(rèn)設(shè)置將無法監(jiān)測(cè)到任何問題,因?yàn)樽C書是有效的
在這個(gè)特殊的情況下龟虎,要妥善保護(hù)你的連接璃谨,應(yīng)設(shè)置kCFStreamSSLPeerName性質(zhì)為MySecureServer.com.
如果事前你不知道對(duì)等的名字的遠(yuǎn)程主機(jī)(例如,你不確認(rèn)它是domain.com" or "www.domain.com")鲤妥,那么你可以使用默認(rèn)設(shè)置來驗(yàn)證證書佳吞,然后在獲得驗(yàn)證的發(fā)行后使用X509Certificate類來驗(yàn)證,X509Certificate類的CocoaAsyncSocket開源項(xiàng)目的一部分
-(void)enablePrebuffering
對(duì)于處理readDataToData請(qǐng)求,數(shù)據(jù)是必須從socket以小增量的方式讀取出來的
性能通過允許AsyncSocket去一次性讀大塊的數(shù)據(jù)和存儲(chǔ)任何一個(gè)小的內(nèi)部緩沖區(qū)溢出的東西來大大提高
這被稱為預(yù)緩沖棉安,就好像一些數(shù)據(jù)在你要求它之前就可能被讀取出來
如果你經(jīng)常使用readDataToData底扳,使用預(yù)緩沖會(huì)有更好的性能,尤其是在iphone上
默認(rèn)的預(yù)緩沖狀態(tài)是由DEFAULT_PREBUFFERING 定義控制的垂券,強(qiáng)烈建議設(shè)置其為yes
這方法存在一些預(yù)緩沖需要一些不可預(yù)見的原因被默認(rèn)禁用的情況,這時(shí)羡滑,這種方法存在允許當(dāng)就緒時(shí)菇爪,可輕松啟用預(yù)緩沖
-(BOOL)moveToRunLoop:(NSRunLoop *)runLoop;
當(dāng)你創(chuàng)建一個(gè)AsyncSocket,它被添加到當(dāng)前線程runloop
對(duì)于手動(dòng)創(chuàng)建的socket柒昏,在線程上你打算使用它凳宙,它是最容易簡單的創(chuàng)建的線程上的socket
當(dāng)一個(gè)新的socket被接受,委托方法 onSocket:wantsRunLoopForNewSocket 會(huì)被調(diào)用 允許你在一個(gè)單獨(dú)的線程上放置socket职祷,這個(gè)工作最好結(jié)合在同一個(gè)線程池設(shè)計(jì)
如果氏涩,但是,在一個(gè)單獨(dú)的線程上有梆,在之后的時(shí)間是尖,你需要移動(dòng)一個(gè)socket,這個(gè)方法可以用來完成任務(wù)
此方法必須從 當(dāng)前運(yùn)行的 線程/runloop 的socket 調(diào)用
注意:此方法調(diào)用后泥耀,所有進(jìn)一步的方法應(yīng)該從給定的runloop上調(diào)用這個(gè)對(duì)象
此外饺汹,所有委托調(diào)用將會(huì)發(fā)送到給定的runloop
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
- (BOOL)addRunLoopMode:(NSString *)runLoopMode;
- (BOOL)removeRunLoopMode:(NSString *)runLoopMode;
允許你配置 socket 使用的 運(yùn)行循環(huán)模式
運(yùn)行循環(huán)模式設(shè)置默認(rèn)是NSRunLoopCommonModes
如果你想你的socket 在其他模式下繼續(xù)操作,你可能需要添加模式 NSModalPanelRunLoopMode 或者 NSEventTrackingRunLoopMode ,或者你可能只想使用 NSRunLoopCommonModes
可接受的socket將自動(dòng) 繼承相同的運(yùn)行循環(huán)模式就像偵聽socket
注意:NSRunLoopCommonModes 定義在10.5痰催,對(duì)于之前的版本可使用 kCFRunLoopCommonModes
-(NSArray *)runLoopModes
返回當(dāng)前正在運(yùn)行的循環(huán)模式的AsyncSocket實(shí)例兜辞, run loop modes的默認(rèn)設(shè)置是NSDefaultRunLoopMode
-(NSData *)unreadData;
一個(gè)錯(cuò)誤的事件迎瞧,在 onSocket:willDisconnectWithError: 將會(huì)被調(diào)用 去讀取留在socket上的任何數(shù)據(jù)
+ (NSData *)CRLFData; // 0x0D0A