WebSocket
以前沒用過倒彰,之前寫過一篇博客是基于原生socket的(查看)比較復(fù)雜披摄,慎入褐捻。今天另外一個(gè)APP需要接websocket了,然后便找到了facebook的
SocketRocket
框架父腕,然后用了一天時(shí)間接上了弱匪,完成了掉線自動(dòng)重連,自動(dòng)重登錄璧亮,心跳等等功能萧诫,用法比原生socket簡(jiǎn)單(原生socket基于TCP/UDP協(xié)議)。
為什么用 WebSocket
因?yàn)锳PP里面有個(gè)聊天功能枝嘶,需要服務(wù)器主動(dòng)推數(shù)據(jù)到APP帘饶。HTTP
通信方式只能由客戶端主動(dòng)拉取,服務(wù)器不能主動(dòng)推給客戶端群扶,如果有實(shí)時(shí)的消息及刻,要立刻通知客戶端就麻煩了,要么客戶端每隔幾秒鐘發(fā)一次請(qǐng)求竞阐,看看有沒有新數(shù)據(jù)缴饭,這種方式想想都知道耗流量電量。還一種方式就是走TCP/UDP協(xié)議服務(wù)器主動(dòng)推給你骆莹,這種方式省流量颗搂。還有就是用websocket,websocket是h5里面的東西幕垦,h5我不太會(huì)丢氢,反正它比原生socket用法簡(jiǎn)單。
用法
用 SocketRocket 框架先改,記住幾個(gè)代理方法就好了疚察,很簡(jiǎn)單。
1.創(chuàng)建和設(shè)置代理對(duì)象
SRWebSocket?*socket?=?[[SRWebSocket?alloc]?initWithURLRequest:
[NSURLRequest?requestWithURL:[NSURL?URLWithString:@"http://ip地址:端口"]];
socket.delegate?=?self;????//?實(shí)現(xiàn)這個(gè)?SRWebSocketDelegate?協(xié)議啊
[socket?open];????//?open?就是直接連接了
2.連接成功會(huì)調(diào)用這個(gè)代理方法
-?(void)webSocketDidOpen:(SRWebSocket?*)webSocket?{
????NSLog(@"連接成功仇奶,可以立刻登錄你公司后臺(tái)的服務(wù)器了貌嫡,還有開啟心跳");
}
3.連接失敗會(huì)調(diào)用這個(gè)方法,看 NSLog 里面的東西
-?(void)webSocket:(SRWebSocket?*)webSocket?didFailWithError:(NSError?*)error?{
????NSLog(@"連接失敗,這里可以實(shí)現(xiàn)掉線自動(dòng)重連衅枫,要注意以下幾點(diǎn)");
????NSLog(@"1.判斷當(dāng)前網(wǎng)絡(luò)環(huán)境嫁艇,如果斷網(wǎng)了就不要連了,等待網(wǎng)絡(luò)到來弦撩,在發(fā)起重連");
????NSLog(@"2.判斷調(diào)用層是否需要連接步咪,例如用戶都沒在聊天界面,連接上去浪費(fèi)流量");
????NSLog(@"3.連接次數(shù)限制益楼,如果連接失敗了猾漫,重試10次左右就可以了,不然就死循環(huán)了感凤。
????或者每隔1悯周,2,4陪竿,8禽翼,10,10秒重連...f(x)?=?f(x-1)?*?2,?(x=5)");
}
4.連接關(guān)閉調(diào)用這個(gè)方法族跛,注意連接關(guān)閉不是連接斷開闰挡,關(guān)閉是 [socket close] 客戶端主動(dòng)關(guān)閉,斷開可能是斷網(wǎng)了礁哄,被動(dòng)斷開的长酗。
-?(void)webSocket:(SRWebSocket?*)webSocket?didCloseWithCode:(NSInteger)code?reason:(NSString?*)reason?wasClean:(BOOL)wasClean?{
????NSLog(@"連接斷開,清空socket對(duì)象桐绒,清空該清空的東西夺脾,還有關(guān)閉心跳!");
}
5.收到服務(wù)器發(fā)來的數(shù)據(jù)會(huì)調(diào)用這個(gè)方法
-?(void)webSocket:(SRWebSocket?*)webSocket?didReceiveMessage:(id)message??{
????NSLog(@"收到數(shù)據(jù)了茉继,注意?message?是?id?類型的咧叭,學(xué)過C語(yǔ)言的都知道,id?是?(void*)??
????????void*?就厲害了烁竭,二進(jìn)制數(shù)據(jù)都可以指著佳簸,不詳細(xì)解釋?void*?了");
????NSLog(@"我這后臺(tái)約定的?message?是?json?格式數(shù)據(jù)
????????收到數(shù)據(jù),就按格式解析吧颖变,然后把數(shù)據(jù)發(fā)給調(diào)用層");
}
6.向服務(wù)器發(fā)送數(shù)據(jù)
發(fā)送的時(shí)候可能斷網(wǎng),可能socket還在連接听想,要判斷一些情況腥刹,寫在下面了
發(fā)送邏輯是,我有一個(gè) socketQueue 的串行隊(duì)列汉买,發(fā)送請(qǐng)求會(huì)加到這個(gè)隊(duì)列里衔峰,然后一個(gè)一個(gè)發(fā)出去,如果掉線了,重連連上后繼續(xù)發(fā)送垫卤,對(duì)調(diào)用層透明威彰,調(diào)用層不需要知道網(wǎng)絡(luò)斷開了。
-?(void)sendData:(id)data?{
????WEAKSELF(ws);
????dispatch_async(self.socketQueue,?^{
????????if(ws.socket?!=?nil)?{
????????????//?只有?SR_OPEN?開啟狀態(tài)才能調(diào)?send?方法啊穴肘,不然要崩
????????????if(ws.socket.readyState?==?SR_OPEN)?{
????????????????[ws.socket?send:data];????//?發(fā)送數(shù)據(jù)
????????????}?elseif(ws.socket.readyState?==?SR_CONNECTING)?{
????????????????NSLog(@"正在連接中歇盼,重連后其他方法會(huì)去自動(dòng)同步數(shù)據(jù)");
????????????????//?每隔2秒檢測(cè)一次?socket.readyState?狀態(tài),檢測(cè)?10?次左右
????????????????//?只要有一次狀態(tài)是?SR_OPEN?的就調(diào)用?[ws.socket?send:data]?發(fā)送數(shù)據(jù)
????????????????//?如果?10?次都還是沒連上的评抚,那這個(gè)發(fā)送請(qǐng)求就丟失了豹缀,這種情況是服務(wù)器的問題了,小概率的
????????????????//?代碼有點(diǎn)長(zhǎng)慨代,我就寫個(gè)邏輯在這里好了
????????????}?elseif(ws.socket.readyState?==?SR_CLOSING?||?ws.socket.readyState?==?SR_CLOSED)?{
????????????????//?websocket?斷開了邢笙,調(diào)用?reConnect?方法重連
????????????????[ws?reConnect:^{
????????????????????NSLog(@"重連成功,繼續(xù)發(fā)送剛剛的數(shù)據(jù)");
????????????????????[ws.socket?send:data];
????????????????}];
????????????}
????????}?else{
????????????NSLog(@"沒網(wǎng)絡(luò)侍匙,發(fā)送失敗氮惯,一旦斷網(wǎng)?socket?會(huì)被我設(shè)置?nil?的");
????????????NSLog(@"其實(shí)最好是發(fā)送前判斷一下網(wǎng)絡(luò)狀態(tài)比較好,我寫的有點(diǎn)晦澀想暗,socket==nil來表示斷網(wǎng)");
????????}
????});
}
7.心跳機(jī)制
心跳機(jī)制就不難了妇汗,開個(gè)定時(shí)器,問下后臺(tái)要每隔多少秒發(fā)送一次心跳請(qǐng)求就好了江滨。然后注意铛纬,斷網(wǎng)了或者socket斷開的時(shí)候把心跳關(guān)一下,省資源唬滑,不然都斷網(wǎng)了告唆,還在循環(huán)發(fā)心跳,浪費(fèi)CPU和電量晶密。