[iOS] 接入WebSocket記錄 + 一些個(gè)人經(jīng)驗(yàn)

閑扯

WebSocket 以前沒用過,之前寫過一篇博客是基于原生socket的(查看)比較復(fù)雜,慎入。今天另外一個(gè)APP需要接websocket了,然后便找到了facebook的 SocketRocket 框架,然后用了一天時(shí)間接上了,完成了掉線自動(dòng)重連,自動(dòng)重登錄,心跳等等功能,用法比原生socket簡單(原生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用法簡單。

用法

用 SocketRocket 框架,記住幾個(gè)代理方法就好了,很簡單。
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)  f(x)=10, (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語言的都知道,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ù)

            } else if (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)長,我就寫個(gè)邏輯在這里好了

            } else if (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和電量。

8.終于接完websocket了,下班回家壓壓驚。我第一次用,其實(shí)不難,就是考慮的情況比較多,整個(gè)邏輯有點(diǎn)多,主要代碼就是上面那些了,其他不重要的代碼我就不復(fù)制粘貼上來了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容