WebScoket建立即時通訊聊天室--SocketRocket的使用

前言

在新公司入職的兩個月時間里學習到了不少新的知識。其中聊天室就是近期在研究公司代碼時學習碰到的一個技術點。其實在上一家公司也接觸過做即時通訊的需求,但之前由于工期比較緊所以就選擇了用環信(一個比較主流的第三方即時通訊sdk)來實現。但其中實現的原理沒有深究,學到的東西并不多。因此借著這個機會拜讀一下同事的代碼,寫下這篇學習筆記補充一下即時通訊這一塊漏掉的知識。

實現方式

iOS 不用第三方sdk實現即時通訊的主流方法主要有4種:1、基于Scoket原生:代表框架 CocoaAsyncSocket。2、基于WebScoket:代表框架 SocketRocket。3、基于MQTT:代表框架 MQTTKit。4、基于XMPP:代表框架 XMPPFramework。這四種方式都各有利弊。我們公司選擇的實現方式是基于WebSocket實現的,因此我先從這個方式入手,其他3種方式待以后探究。

什么是WebSocket?

我們在客戶端開發的過程中,相信我們遇到最多的網絡協議是HTTP協議。WebSocket 和HTTP 一樣是網絡協議的一種。那么我們已經有強大的HTTP協議了為什么還需要另外一種網絡協議呢?那是因為HTTP協議有一個很大的弊端--通訊只能有客戶端發起。客戶端發起的request 和服務器下發的respond 是一一對應的。在HTTP協議下如果客戶端有連續的狀態變化,客戶端想要獲取就比較麻煩。我們只能依靠輪詢機制(每隔一段時間向服務器請求一次,了解服務器最新的數據)來獲取。但是輪詢不但耗費性能,而且也并非真正意義上的實現即時性。這就導致HTTP 協議并不適合用于即時通訊。而WebSocket就是解決這一問題而發明的。WebSocket借用了HTTP的協議來完成一次握手,在建立連接后,WebSocket 服務器和 Browser/Client Agent 都能主動的向對方發送或接收數據了。

如何在iOS項目中使用WebSocket

在上面介紹即時通訊的實現方式時提到,WebSocket的代表框架是SocketRocket。團隊的項目也是用SocketRocket來實現聊天室功能的。因此我也來順帶了解一下SocketRocket這個框架。SocketRocket是Facebook開源的一個用于 iOS, macOS and tvOS客戶端的websocket框架。SocketRocket是對WebSocket的封裝。

1、集成

集成SocketRocket方法非常簡單,用cocoapods 在podfile中加入 pod 'SocketRocket',執行pod install指令就可以完成集成。

2、實現聊天室功能需要做哪些事情?

那我們在一次建立一個即時聊天的過程中,我們需要做哪些事情呢?①、首先我們需要建立連接 ②、遵守并指定代理 ③、打開連接加載請求 ④、關閉連接 ⑤、發送消息 ⑥、通過代理方法來獲取接收到的消息

-(void)SRWebSocketOpen{

? ? ? ? ? ? //如果是同一個url return

? ? if (self.socket) {

? ? ? ? ? ? ? ? return;

? ? ? ? }

? ? self.socket = [[SRWebSocket alloc] initWithURLRequest: ? ? ? ? ? ? ? ? ? [NSURLRequest requestWithURL:[NSURL URLWithString:@"ws:xxxxxxxxxxx"]]];//這里填寫你服務器的地址

? ? self.socket.delegate = self;? //SRWebSocketDelegate 協議

? ? ? ? ? ? [self.socket open];? ? //開始連接

}

①、建立連接 ②、遵守并指定代理 ③、打開連接加載請求

在SRWebSocketOpen方法里,我們先創建一個SRWebSocket對象,并設置了SocketRocket的回調代理。在完成以上兩個操作后,調用[_socket Open];開始建立連接。



-(void)SRWebSocketClose{

? ? ? ? if (self.socket){

? ? ? ? ? ? ? ? [self.socket close];

? ? ? ? ? ? ? ? self.socket = nil;

? ? ? ? ? ? ? ? //斷開連接時銷毀心跳

? ? ? ? ? ? ? ? [self destoryHeartBeat];

? ? ? ? }

}

④、關閉連接:

在SRWebSocketClose方法里,通過調用[_socket close]; 關閉連接并銷毀心跳。等一下,什么是心跳?這部分內容在下一個章節說明,暫時先可以理解為檢測連接是否正常的一個機制。


- (void)sendData:(id)data {

? ? ? ? [self.socket send:data] ;

}

⑤、發送消息

- (void)sendData:(id)data 方法中通過調用[_socket send:data];方法發送消息。這個data可以是一個UTF8的字符串或者NSData對象。


- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message? {

? ? if (webSocket == self.socket) {

? ? ? ? ? ? ? ? NSLog(@"接收到后臺下發的信息,在這解析。");

? ? ? ? }

}

⑥、通過代理方法來獲取接收到的消息

當接收到信息時,會通過- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message代理方法回調。


按照常規,實現以上方法就已經實現了即時聊天的基本功能。但是,在現實的使用過程中,往往會出現一些特殊的情況需要處理。例如:1、在建立了鏈接后,如何確保客戶端和服務端的之間的鏈接有效可用?2、雖然WebSocket通過握手建立了鏈接,但是在鏈接過程中可能遇到因為網絡不好等的原因導致的連接中斷情況,鏈接斷了如何處理?3、在即時聊天項目中通常還要實現APSN即時推送功能,那服務器如何判斷什么時候通過WebSocket發送消息,什么時候走APNs離線推送呢?

解決以上三個問題,就涉及到了三個webSocket的機制

1、心跳機制

用NSTimer每隔固定時間向服務器發一個心跳包,以此來告訴服務器,這個客戶端還活著。事實上這是為了保持長連接,至于這個包的內容,是沒有什么特別規定的,不過一般都是很小的包,或者只包含包頭的一個空包。

//初始化心跳

- (void)initHeartBeat {

? ? ? dispatch_main_async_safe(^{

? ? ? ? ? ? ? ? ? ? [self destoryHeartBeat]; ? ? ? ? //心跳設置為3分鐘,NAT超時一般為5分鐘

? ? ? ? ? ? ? ? ? ? heartBeat = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(sentheart) userInfo:nil repeats:YES]; ? ? ? ? //和服務端約定好發送什么作為心跳標識,盡可能的減小心跳包大小

? ? ? ? ? ? ? ? ? ? [[NSRunLoop currentRunLoop]addTimer:heartBeat forMode:NSRunLoopCommonModes];

? ? ? ? })

}

//取消心跳

- (void)destoryHeartBeat {

? ? dispatch_main_async_safe(^{

? ? ? ? ? ? ? ? if (heartBeat) {

? ? ? ? ? ? ? ? ? ? ? ? if ([heartBeat respondsToSelector:@selector(isValid)]){

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if ([heartBeat isValid]){

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [heartBeat invalidate];

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? heartBeat = nil;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? }

? ? })

}

-(void)sentheart{

? ? ? ? ? ? //發送心跳 和后臺可以約定發送什么內容? 一般可以調用ping? 這里根據后臺的要求 發送了data給他

? ? ? ? ? ? [self sendData:@"heart"];

}

2、重連機制

重連機制比較好理解,值得注意的是當重連到一定次數仍然失敗后要提示用戶網絡存在問題,就沒必要再繼續重連了。

- (void)reConnect {

? ? ? ? ? ? [self SRWebSocketClose];

? ? ? ? ? ? ? ? if (reConnectTime > 50) {

? ? ? ? ? ? ? ? // 重連50次都失敗

? ? ? ? ? ? ? ? reConnectTime = 0;

? ? ? ? ? ? ? ? // 在這里彈出提示告知用戶網絡不好

? ? ? ? ? ? ? ? ? ? ? ? return;

? ? ? ? ? ? }

? ? ? ? ? dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

? ? ? ? ? ? ? ? ? ? self.socket = nil;

? ? ? ? ? ? ? ? ? ? [self SRWebSocketOpen];

? ? ? ? ? ? NSLog(@"重連");

? ? ? ? ? ? });

? ? ? ? ? ? ? ? ? ? reConnectTime ++;

? ? }

3、pingpong機制

當服務端發出一個Ping,客戶端沒有在約定的時間內返回響應的ack,則認為客戶端已經不在線,這時我們Server端會主動斷開Scoket連接,并且改由APNS推送的方式發送消息。

//pingPong

- (void)ping{

? ? if (self.socket.readyState == SR_OPEN) {

? ? ? ? ? ? ? ? [self.socket sendPing:nil];

? ? ? ? }

}

//sendPing的時候,如果網絡通的話,則會收到回調,但是必須保證ScoketOpen,否則會crash

- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload {

? ? ? ? NSLog(@"收到pong回調");

}

至此,用SocketRocket建立一個聊天室所需的基本方法大概都列了一下。當然,中間仍有許多代碼邏輯需要處理。這些內容會在仔細研讀后繼續整理出來。

寫在最后

這篇文章只是我自己的學習筆記。第一次寫簡書,有點語無倫次,不好意思。文中的代碼內容大多都是參考公司項目及網上的一些簡書作者的文章。非常感謝這些博主及公司同事的無私分享。最后附上這些博主的原文。

iOS--SocketRocket框架的使用及測試服務器的搭建

socketRocket 封裝,添加重連機制,block回調

iOS即時通訊,從入門到“放棄”?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370