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

前言

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

實現(xiàn)方式

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

什么是WebSocket?

我們在客戶端開發(fā)的過程中肾胯,相信我們遇到最多的網(wǎng)絡(luò)協(xié)議是HTTP協(xié)議竖席。WebSocket 和HTTP 一樣是網(wǎng)絡(luò)協(xié)議的一種。那么我們已經(jīng)有強大的HTTP協(xié)議了為什么還需要另外一種網(wǎng)絡(luò)協(xié)議呢敬肚?那是因為HTTP協(xié)議有一個很大的弊端--通訊只能有客戶端發(fā)起毕荐。客戶端發(fā)起的request 和服務(wù)器下發(fā)的respond 是一一對應(yīng)的艳馒。在HTTP協(xié)議下如果客戶端有連續(xù)的狀態(tài)變化憎亚,客戶端想要獲取就比較麻煩。我們只能依靠輪詢機制(每隔一段時間向服務(wù)器請求一次弄慰,了解服務(wù)器最新的數(shù)據(jù))來獲取第美。但是輪詢不但耗費性能,而且也并非真正意義上的實現(xiàn)即時性陆爽。這就導致HTTP 協(xié)議并不適合用于即時通訊什往。而WebSocket就是解決這一問題而發(fā)明的。WebSocket借用了HTTP的協(xié)議來完成一次握手慌闭,在建立連接后别威,WebSocket 服務(wù)器和 Browser/Client Agent 都能主動的向?qū)Ψ桨l(fā)送或接收數(shù)據(jù)了。

如何在iOS項目中使用WebSocket

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

1利花、集成

集成SocketRocket方法非常簡單,用cocoapods 在podfile中加入 pod 'SocketRocket'载佳,執(zhí)行pod install指令就可以完成集成炒事。

2、實現(xiàn)聊天室功能需要做哪些事情蔫慧?

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

-(void)SRWebSocketOpen{

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

? ? if (self.socket) {

? ? ? ? ? ? ? ? return;

? ? ? ? }

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

? ? self.socket.delegate = self;? //SRWebSocketDelegate 協(xié)議

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

}

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

在SRWebSocketOpen方法里,我們先創(chuàng)建一個SRWebSocket對象甩鳄,并設(shè)置了SocketRocket的回調(diào)代理逞度。在完成以上兩個操作后,調(diào)用[_socket Open];開始建立連接妙啃。



-(void)SRWebSocketClose{

? ? ? ? if (self.socket){

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

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

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

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

? ? ? ? }

}

④档泽、關(guān)閉連接:

在SRWebSocketClose方法里,通過調(diào)用[_socket close]; 關(guān)閉連接并銷毀心跳彬祖。等一下茁瘦,什么是心跳?這部分內(nèi)容在下一個章節(jié)說明储笑,暫時先可以理解為檢測連接是否正常的一個機制甜熔。


- (void)sendData:(id)data {

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

}

⑤、發(fā)送消息

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


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

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

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

? ? ? ? }

}

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

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


按照常規(guī)诵闭,實現(xiàn)以上方法就已經(jīng)實現(xiàn)了即時聊天的基本功能。但是澎嚣,在現(xiàn)實的使用過程中疏尿,往往會出現(xiàn)一些特殊的情況需要處理。例如:1易桃、在建立了鏈接后褥琐,如何確保客戶端和服務(wù)端的之間的鏈接有效可用晤郑?2敌呈、雖然WebSocket通過握手建立了鏈接贸宏,但是在鏈接過程中可能遇到因為網(wǎng)絡(luò)不好等的原因?qū)е碌倪B接中斷情況,鏈接斷了如何處理磕洪?3吭练、在即時聊天項目中通常還要實現(xiàn)APSN即時推送功能,那服務(wù)器如何判斷什么時候通過WebSocket發(fā)送消息褐鸥,什么時候走APNs離線推送呢线脚?

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

1叫榕、心跳機制

用NSTimer每隔固定時間向服務(wù)器發(fā)一個心跳包浑侥,以此來告訴服務(wù)器,這個客戶端還活著晰绎。事實上這是為了保持長連接寓落,至于這個包的內(nèi)容,是沒有什么特別規(guī)定的荞下,不過一般都是很小的包伶选,或者只包含包頭的一個空包。

//初始化心跳

- (void)initHeartBeat {

? ? ? dispatch_main_async_safe(^{

? ? ? ? ? ? ? ? ? ? [self destoryHeartBeat]; ? ? ? ? //心跳設(shè)置為3分鐘尖昏,NAT超時一般為5分鐘

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

? ? ? ? ? ? ? ? ? ? [[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{

? ? ? ? ? ? //發(fā)送心跳 和后臺可以約定發(fā)送什么內(nèi)容? 一般可以調(diào)用ping? 這里根據(jù)后臺的要求 發(fā)送了data給他

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

}

2、重連機制

重連機制比較好理解抽诉,值得注意的是當重連到一定次數(shù)仍然失敗后要提示用戶網(wǎng)絡(luò)存在問題陨簇,就沒必要再繼續(xù)重連了。

- (void)reConnect {

? ? ? ? ? ? [self SRWebSocketClose];

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

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

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

? ? ? ? ? ? ? ? // 在這里彈出提示告知用戶網(wǎng)絡(luò)不好

? ? ? ? ? ? ? ? ? ? ? ? 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機制

當服務(wù)端發(fā)出一個Ping河绽,客戶端沒有在約定的時間內(nèi)返回響應(yīng)的ack,則認為客戶端已經(jīng)不在線唉窃,這時我們Server端會主動斷開Scoket連接耙饰,并且改由APNS推送的方式發(fā)送消息。

//pingPong

- (void)ping{

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

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

? ? ? ? }

}

//sendPing的時候纹份,如果網(wǎng)絡(luò)通的話苟跪,則會收到回調(diào),但是必須保證ScoketOpen蔓涧,否則會crash

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

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

}

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

寫在最后

這篇文章只是我自己的學習筆記鳞陨。第一次寫簡書昨寞,有點語無倫次瞻惋,不好意思。文中的代碼內(nèi)容大多都是參考公司項目及網(wǎng)上的一些簡書作者的文章援岩。非常感謝這些博主及公司同事的無私分享歼狼。最后附上這些博主的原文。

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

socketRocket 封裝享怀,添加重連機制羽峰,block回調(diào)

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末梅屉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鳞贷,更是在濱河造成了極大的恐慌坯汤,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搀愧,死亡現(xiàn)場離奇詭異惰聂,居然都是意外死亡,警方通過查閱死者的電腦和手機咱筛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門搓幌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人迅箩,你說我怎么就攤上這事溉愁。” “怎么了沙热?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵叉钥,是天一觀的道長。 經(jīng)常有香客問我篙贸,道長投队,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任爵川,我火速辦了婚禮敷鸦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寝贡。我一直安慰自己扒披,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布圃泡。 她就那樣靜靜地躺著碟案,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颇蜡。 梳的紋絲不亂的頭發(fā)上价说,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天辆亏,我揣著相機與錄音,去河邊找鬼鳖目。 笑死扮叨,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的领迈。 我是一名探鬼主播彻磁,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼狸捅!你這毒婦竟也來了衷蜓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤薪贫,失蹤者是張志新(化名)和其女友劉穎恍箭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞧省,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡扯夭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鞍匾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片交洗。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖橡淑,靈堂內(nèi)的尸體忽然破棺而出构拳,到底是詐尸還是另有隱情,我是刑警寧澤梁棠,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布置森,位于F島的核電站,受9級特大地震影響符糊,放射性物質(zhì)發(fā)生泄漏凫海。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一男娄、第九天 我趴在偏房一處隱蔽的房頂上張望行贪。 院中可真熱鬧,春花似錦模闲、人聲如沸建瘫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽啰脚。三九已至,卻和暖如春实夹,著一層夾襖步出監(jiān)牢的瞬間橄浓,已是汗流浹背晾咪。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贮配,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓塞赂,卻偏偏與公主長得像泪勒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子宴猾,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355