基于WebSocket协议的iOS端即时聊天

好好很久没有在cnblogs上写博客,不过在这里写的最先的一篇博客的时间戳,真是时间久远啊,那时候还没毕业。不在cnblogs的期间,在github pages、简书上写过博客,github pages的markdown仍是不错的,不过百度不能检索到文章,也就是经过百度,永远没法导流搜索到个人文章(感动moving),简书感受更适合抒情鸡汤,可能我不太能融入那个用户群体。不过如今我回来了,那些在github pages上的文章,我暂时也不迁移了,毕竟人生原本就不完美,提醒本身不能有强迫症(心里默念三次)。续上上一篇博客的时间(2016-04-29)继续回到这里,中间的间隔两年多,甚至更长。这段空白时间的大概状况介绍完了~git

 

---------------------脏兮兮的分割线---------------------github

 

言归正传,最近由于公司产品的须要,计划在移动端开发即时聊天的通信功能。即时聊天的第三方SDK供应商也是很是多的,由于项目高度的自由定制性,数据隐私等方面的考虑,最终Server-Client端都由本身来实现,服务端采用worker man的PHP socket服务器架构。web

在长链接双向通讯上,选择的是WebSocket协议。开发主要负责iOS Client端的开发,按照开发第三方SDK的标准,将关键的部分封装起来,只留出必要的API供外部调用,将相关代码模块化,方便后期向公司其余项目中移植聊天模块。(不能本身坑本身,遇到移植的需求的可能性是很是大的,因此与其散漫的写代码,不如按照SDK的标准去作开发。)算法

 

WebSocket服务器

WebSocket 协议在2008年诞生,2011年成为国际标准。WebSocket 协议本质上是一个基于 TCP 的协议。是创建在 TCP 协议之上的全双工通信协议,与 HTTP 协议有着良好的兼容性。默认端口也是80和443,而且握手阶段采用 HTTP 协议,所以握手时不容易屏蔽,能经过各类 HTTP 代理服务器,因此服务器端的实现比较容易。协议标识符是ws,请求地址格式:ws://example.com:80/pathwebsocket

握手过程:markdown

为了创建一个 WebSocket 链接,客户端首先要向服务器发起一个 HTTP 请求,这个请求和一般的 HTTP 请求不一样,包含了一些附加头信息。以下所示:网络

客户端请求Header:架构

1 --- request header ---
2 GET /chat HTTP/1.1
3 Upgrade: websocket
4 Connection: Upgrade
5 Host: 127.0.0.1:8001
6 Origin: http://127.0.0.1:8001
7 Sec-WebSocket-Key: hj0eNqbhE/A0GkBXDRrYYw==
8 Sec-WebSocket-Version: 13

 

其中附加头信息"Upgrade: WebSocket"代表这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息,根据Sec-WebSocket-Key的字符串,经过sha1算法处理,将response信息(sec-Websocket-Accept字符串)返回给客户端,客户端能成功解码字符串,就和服务器端的 WebSocket链接就创建起来了。socket

服务器的Response:

1 HTTP/1.1 101 Switching Protocols
2 Content-Length: 0
3 Upgrade: websocket
4 Sec-Websocket-Accept: ZEs+c+VBk8Aj01+wJGN7Y15796g=
5 Server: TornadoServer/4.5.1
6 Connection: Upgrade
7 Date: Wed, 21 Jun 2017 03:29:14 GMT

双方就能够经过这个链接通道自由的传递信息,而且这个链接会持续存在直到客户端或者服务器端的某一方主动的关闭链接。

 

使用封装Websocket的SocketRocket(Objective-C)

上面是WebSocket握手链接通讯,而站在巨人的肩膀上,这里使用的是Github上facebook的SocketRocket项目,这是关于WebSocket的Objective-C的封装,提供简单的API,让开发者不用去跟底层协议打交道,而是关注于链路上的数据处理,逻辑层。关于SocketRocket的Features使用等,在Github上有详细介绍,使用起来也很是简单。须要注意SRWebSocketDelegate协议的相关方法:

 

 //当收到服务器的Message时调用,这里的message是id类型,能够是NSString,也能够是NSData。

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;

//当与服务器创建链接时调用

- (void)webSocketDidOpen:(SRWebSocket *)webSocket;   

//当发生未知错误的时调用,多是网络缘由等

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;

//当关闭WebSocket时调用

- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;

//接收到服务器的Pong时调用

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

//返回YES表示对messages进行转换,以NSString的形式发送,返回NO,表示跳过NSData->NSString的转换,直接以NSData来传递。默认YES

- (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket;

 

使用方式与以前学Java时使用的socket通信相似,大概流程以下所示:init websocket -> open ->  connected -> sendMsg -> handle server response -> close

具体的代码也是很是容易在网上找到的,就不大段的贴代码了。

上述的通道握手创建而且能与服务器简单通讯后,就要考虑各类状况的处理,包括断网,信号差等,就须要考虑断线重连,发送心跳包肯定是否与服务器保持着链接的状态。

这里心跳包的发送是定时执行的,使用NSTimer的方式。

 1 dispatch_main_async_safe(^{
 2         
 3         [self destoryHeartBeat];
 4         
 5         __weak typeof(self) weakSelf = self;
 6         //心跳设置为3分钟,NAT超时通常为5分钟
 7         _heartBeat = [NSTimer scheduledTimerWithTimeInterval:3 * 60 repeats:YES block:^(NSTimer * _Nonnull timer) {
 8             NSLog(@"heart");
 9             //和服务端约定好发送什么做为心跳标识,尽量的减少心跳包大小
10             [weakSelf sendHeartBeatMessage];
11         }];
12         [[NSRunLoop currentRunLoop]addTimer:_heartBeat forMode:NSRunLoopCommonModes];
13     })

错误断网等重连的实现:

 1 - (void)reConnect {
 2     
 3     [self stopSocket];
 4     
 5     if (_connectInterval < 2) {
 6         _connectInterval = 2;
 7     }else{
 8         _connectInterval = _connectInterval + 2;
 9     }
10     
11     // 断开链接后每过n+2秒后从新创建一次链接
12     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_connectInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
13         [self startSocket];
14     });
15 }

一个好的与服务器链接的Websocket模块须要细细的打磨,这里展现的都是很粗糙的模块,须要根据之后的需求,出现的问题进行不断的修正,才能有一个好用的Websocket模块。想到了一句话:细节决定成败。因此打磨好生活工做学习中的每个细节~

相关文章
相关标签/搜索