IM

http://www.cocoachina.com/ios/20190425/26875.html

轉(zhuǎn)載自:

iOS websocket接入??CoderSJun

接觸WebSocket

最近公司的項(xiàng)目中有一個(gè)功能 需要服務(wù)器主動推數(shù)據(jù)到APP欲侮。

考慮到普通的HTTP 通信方式只能由客戶端主動拉取灵寺,服務(wù)器不能主動推給客戶端 。然后就想出的2種解決方案字支。

1.和后臺溝通了一下 他們那里使用的是WebSocket 浩嫌,所以就使用WebSocket讓我們app端和服務(wù)器建立長連接钠糊。這樣就可以事實(shí)接受他發(fā)過來的消息

2.使用推送垫言,也可以實(shí)現(xiàn)接收后臺發(fā)過來的一些消息

最后還是選擇了WebSocket杉女,找到了facebook的 SocketRocket 框架瞻讽。下面是接入過程中的一些記錄

WebSocket

WebSocket 是 HTML5 一種新的協(xié)議。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工通信熏挎,能更好的節(jié)省服務(wù)器資源和帶寬并達(dá)到實(shí)時(shí)通訊速勇,它建立在 TCP 之上,同 HTTP 一樣通過 TCP 來傳輸數(shù)據(jù)坎拐,但是它和 HTTP 最大不同是:

WebSocket 是一種雙向通信協(xié)議烦磁,在建立連接后,WebSocket 服務(wù)器和 Browser/Client Agent 都能主動的向?qū)Ψ桨l(fā)送或接收數(shù)據(jù)哼勇,就像 Socket 一樣都伪;

WebSocket 需要類似 TCP 的客戶端和服務(wù)器端通過握手連接,連接成功后才能相互通信积担。

具體在這兒WebSocket 是什么原理陨晶?為什么可以實(shí)現(xiàn)持久連接?

用法

我使用的是pod管理庫 所以在podfile中加入

pod 'SocketRocket'

在使用命令行工具cd到當(dāng)前工程 安裝

pod install

如果是copy的工程中的 SocketRocket庫的github地址:SocketRocket

導(dǎo)入庫到工程中以后首先封裝一個(gè)SocketRocketUtility單例

SocketRocketUtility.m文件中的寫法如下:

#import"SocketRocketUtility.h"#import<SocketRocket.h>NSString*constkNeedPayOrderNote =@"kNeedPayOrderNote";//發(fā)送的通知名稱@interfaceSocketRocketUtility(){int_index;NSTimer* heartBeat;NSTimeIntervalreConnectTime;}@property(nonatomic,strong) SRWebSocket *socket;@end@implementationSocketRocketUtility+ (SocketRocketUtility *)instance {staticSocketRocketUtility *Instance =nil;staticdispatch_once_tpredicate;dispatch_once(&predicate, ^{? ? ? ? Instance = [[SocketRocketUtility alloc] init];? ? });returnInstance;}//開啟連接-(void)SRWebSocketOpenWithURLString:(NSString*)urlString {if(self.socket) {return;? ? }if(!urlString) {return;? ? }//SRWebSocketUrlString 就是websocket的地址 寫入自己后臺的地址self.socket = [[SRWebSocket alloc] initWithURLRequest:? ? ? ? ? ? ? ? ? [NSURLRequestrequestWithURL:[NSURLURLWithString:urlString]]];self.socket.delegate =self;//SRWebSocketDelegate 協(xié)議[self.socket open];//開始連接}//關(guān)閉連接- (void)SRWebSocketClose {if(self.socket){? ? ? ? [self.socket close];self.socket =nil;//斷開連接時(shí)銷毀心跳[selfdestoryHeartBeat];? ? }}#pragma mark - socket delegate- (void)webSocketDidOpen:(SRWebSocket *)webSocket {NSLog(@"連接成功帝璧,可以與服務(wù)器交流了,同時(shí)需要開啟心跳");//每次正常連接的時(shí)候清零重連時(shí)間reConnectTime =0;//開啟心跳 心跳是發(fā)送pong的消息 我這里根據(jù)后臺的要求發(fā)送data給后臺[selfinitHeartBeat];? ? [[NSNotificationCenterdefaultCenter] postNotificationName:kWebSocketDidOpenNote object:nil];}- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError*)error {NSLog(@"連接失敗珍逸,這里可以實(shí)現(xiàn)掉線自動重連,要注意以下幾點(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)了。)";? ? _socket =nil;//連接失敗就重連[selfreConnect];}- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString*)reason wasClean:(BOOL)wasClean {NSLog(@"被關(guān)閉連接嗤军,code:%ld,reason:%@,wasClean:%d",code,reason,wasClean);//斷開連接 同時(shí)銷毀心跳[selfSRWebSocketClose];}/*

該函數(shù)是接收服務(wù)器發(fā)送的pong消息注盈,其中最后一個(gè)是接受pong消息的,

在這里就要提一下心跳包叙赚,一般情況下建立長連接都會建立一個(gè)心跳包老客,

用于每隔一段時(shí)間通知一次服務(wù)端僚饭,客戶端還是在線,這個(gè)心跳包其實(shí)就是一個(gè)ping消息胧砰,

我的理解就是建立一個(gè)定時(shí)器鳍鸵,每隔十秒或者十五秒向服務(wù)端發(fā)送一個(gè)ping消息,這個(gè)消息可是是空的

*/-(void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData*)pongPayload{NSString*reply = [[NSStringalloc] initWithData:pongPayload encoding:NSUTF8StringEncoding];NSLog(@"reply===%@",reply);}- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message? {//收到服務(wù)器發(fā)過來的數(shù)據(jù) 這里的數(shù)據(jù)可以和后臺約定一個(gè)格式 我約定的就是一個(gè)字符串 收到以后發(fā)送通知到外層 根據(jù)類型 實(shí)現(xiàn)不同的操作NSLog(@"%@",message);? ? ? ? [[NSNotificationCenterdefaultCenter] postNotificationName:kNeedPayOrderNote object:message];}#pragma mark - methods//重連機(jī)制- (void)reConnect{? ? [selfSRWebSocketClose];//超過一分鐘就不再重連 所以只會重連5次 2^5 = 64if(reConnectTime >64) {return;? ? }? ? ? dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(reConnectTime *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{self.socket =nil;? ? ? ? [selfSRWebSocketOpen];NSLog(@"重連");? ? });//重連時(shí)間2的指數(shù)級增長if(reConnectTime ==0) {? ? ? ? reConnectTime =2;? ? }else{? ? ? ? reConnectTime *=2;? ? }}//初始化心跳- (void)initHeartBeat{? ? dispatch_main_async_safe(^{? ? ? ? [selfdestoryHeartBeat];? ? ? ? __weaktypeof(self) weakSelf =self;//心跳設(shè)置為3分鐘尉间,NAT超時(shí)一般為5分鐘heartBeat = [NSTimerscheduledTimerWithTimeInterval:3*60repeats:YESblock:^(NSTimer* _Nonnull timer) {NSLog(@"heart");//和服務(wù)端約定好發(fā)送什么作為心跳標(biāo)識偿乖,盡可能的減小心跳包大小[weakSelf sendData:@"heart"];? ? ? ? }];? ? ? ? [[NSRunLoopcurrentRunLoop]addTimer:heartBeat forMode:NSRunLoopCommonModes];? ? })}//取消心跳- (void)destoryHeartBeat{? ? dispatch_main_async_safe(^{if(heartBeat) {? ? ? ? ? ? [heartBeat invalidate];? ? ? ? ? ? heartBeat =nil;? ? ? ? }? ? })}//pingPong機(jī)制- (void)ping{? ? [self.socket sendPing:nil];}#define WeakSelf(ws) __weak __typeof(&*self)weakSelf = self- (void)sendData:(id)data {? ? WeakSelf(ws);dispatch_queue_tqueue =? dispatch_queue_create("zy",NULL);dispatch_async(queue, ^{if(weakSelf.socket !=nil) {// 只有 SR_OPEN 開啟狀態(tài)才能調(diào) send 方法,不然要崩if(weakSelf.socket.readyState == SR_OPEN) {? ? ? ? ? ? ? ? [weakSelf.socket send:data];// 發(fā)送數(shù)據(jù)}elseif(weakSelf.socket.readyState == SR_CONNECTING) {NSLog(@"正在連接中哲嘲,重連后其他方法會去自動同步數(shù)據(jù)");// 每隔2秒檢測一次 socket.readyState 狀態(tài)贪薪,檢測 10 次左右// 只要有一次狀態(tài)是 SR_OPEN 的就調(diào)用 [ws.socket send:data] 發(fā)送數(shù)據(jù)// 如果 10 次都還是沒連上的,那這個(gè)發(fā)送請求就丟失了眠副,這種情況是服務(wù)器的問題了画切,小概率的[selfreConnect];? ? ? ? ? ? ? ? ? ? ? ? ? ? }elseif(weakSelf.socket.readyState == SR_CLOSING || weakSelf.socket.readyState == SR_CLOSED) {// websocket 斷開了,調(diào)用 reConnect 方法重連[selfreConnect];? ? ? ? ? ? }? ? ? ? }else{NSLog(@"沒網(wǎng)絡(luò)侦啸,發(fā)送失敗槽唾,一旦斷網(wǎng) socket 會被我設(shè)置 nil 的");? ? ? ? }? ? });}-(void)dealloc{? ? [[NSNotificationCenterdefaultCenter] removeObserver:self];}

然后在需要開啟socket的地方調(diào)用

[[SocketRocketUtility instance] SRWebSocketOpenWithURLString:@"寫入自己后臺的地址"];

在需要斷開連接的時(shí)候調(diào)用

[[SocketRocketUtility instance] SRWebSocketClose];

使用這個(gè)框架最后一個(gè)很重要的 需要注意的一點(diǎn)

這個(gè)框架給我們封裝的webscoket在調(diào)用它的sendPing senddata方法之前丧枪,一定要判斷當(dāng)前scoket是否連接光涂,如果不是連接狀態(tài),程序則會crash拧烦。

結(jié)語

這里簡單的實(shí)現(xiàn)了連接和收發(fā)數(shù)據(jù) 后續(xù)看項(xiàng)目需求在加上后續(xù)的改進(jìn) 希望能夠幫助第一次寫的iOSer 忘闻。 希望有更好的方法的童鞋可以有進(jìn)一步的交流? : )

4月10日 更新:

/// Creates and returns a new NSTimer object initialized with the specified block object and schedules it on the current run loop in the default mode./// - parameter:? ti? ? The number of seconds between firings of the timer. If seconds is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead/// - parameter:? repeats? If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires./// - parameter:? block? The execution body of the timer; the timer itself is passed as the parameter to this block when executed to aid in avoiding cyclical references+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void(^)(NSTimer*timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

上面發(fā)送心跳包的方法是iOS10才可以用的 其他版本會崩潰? 要適配版本 要選擇 這個(gè)方法

+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullableid)userInfo repeats:(BOOL)yesOrNo;

8月10日 更新demo地址

demo地址

可以下載下來看看哦 :)

demo中的后臺地址未設(shè)置? 所以很多同學(xué)直接運(yùn)行就報(bào)錯了 設(shè)置一個(gè)自己后臺的地址就ok了 :)

作者:CoderSJun

鏈接:http://www.reibang.com/p/821b777555d3

來源:簡書

簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處恋博。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末齐佳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子债沮,更是在濱河造成了極大的恐慌炼吴,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疫衩,死亡現(xiàn)場離奇詭異硅蹦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)闷煤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門童芹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鲤拿,你說我怎么就攤上這事假褪。” “怎么了近顷?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵生音,是天一觀的道長宁否。 經(jīng)常有香客問我,道長久锥,這世上最難降的妖魔是什么家淤? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮瑟由,結(jié)果婚禮上絮重,老公的妹妹穿的比我還像新娘。我一直安慰自己歹苦,他們只是感情好青伤,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著殴瘦,像睡著了一般狠角。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚪腋,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天丰歌,我揣著相機(jī)與錄音,去河邊找鬼屉凯。 笑死立帖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悠砚。 我是一名探鬼主播晓勇,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼灌旧!你這毒婦竟也來了绑咱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤枢泰,失蹤者是張志新(化名)和其女友劉穎描融,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體衡蚂,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窿克,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了讳窟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片让歼。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖丽啡,靈堂內(nèi)的尸體忽然破棺而出谋右,到底是詐尸還是另有隱情,我是刑警寧澤补箍,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布改执,位于F島的核電站啸蜜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏辈挂。R本人自食惡果不足惜衬横,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望终蒂。 院中可真熱鬧蜂林,春花似錦、人聲如沸拇泣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽霉翔。三九已至睁蕾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間债朵,已是汗流浹背子眶。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留序芦,地道東北人臭杰。 一個(gè)月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像芝加,于是被迫代替她去往敵國和親硅卢。 傳聞我的和親對象是個(gè)殘疾皇子射窒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 除了微信和QQ還有如下選擇: BigAnt(大螞蟻) 最近更新時(shí)間:2017-04-14 應(yīng)用場景:局域網(wǎng)藏杖、私有云...
    James_紀(jì)閱讀 506評論 0 0
  • 夜微涼蝌麸,尺素香。星月半央艾疟,淡了素妝来吩。 暗思量,自難忘蔽莱。涕淚兩行弟疆,濕了華裳。 --題記 窗外的夜盗冷,是靜謐的涼怠苔。微風(fēng)習(xí)...
    c528a9d867cc閱讀 246評論 0 1
  • 《誤入泥途》 --黃心喜 92年農(nóng)歷八月,離中秋節(jié)還有十多天仪糖。一個(gè)靦腆的17歲少年人柑司,風(fēng)塵仆仆的他從農(nóng)村逐...
    心喜_121f閱讀 389評論 0 3
  • 這已經(jīng)是第二次以阿澈女伴的身份出席宴會迫肖,也是我第二次登上“浠哲島”,上次登島時(shí)島上靜得只能聽到鳥鳴聲和海浪聲攒驰,而這...
    某小沁閱讀 402評論 0 0
  • 挑戰(zhàn)365天|第11天 文/依依思言 連著兩天夜班蟆湖,錯過了兩次精彩的直播。 昨晚玻粪,當(dāng)我十一點(diǎn)多下班回家隅津,打開回放,...
    依依思言閱讀 421評論 9 12