项目中用websocket实现了一个简单的IM功能,来记录一下其中遇到的问题及解决方案。
首先要知道websocket是什么,参考https://www.cnblogs.com/fuqiang88/p/5956363.html
简单来说,websocket是个协议,用来在一个tcp链接中实现全双工通信的。
它的优点主要是节省服务端资源,具体可以参考上面链接中的内容。
iOS这边使用了Facebook的SocketRocket,然后自己又封装一层socketNetworking,提供block回调,这样一来如果将来要换掉SocketRocket也方便一点;然后再写一个socketService实现具体业务。
开发中遇到的问题:
1,首先是心跳包的机制问题
SocketRocket提供了sendPing和didReceivePong的方法,但是并没有自己发送ping,所以需要自己实现心跳机制。
可以参考微信开发团队的博客://www.greatytc.com/p/8175f51e662c
这是比较智能的方案了,最简单的就是弄个NSTimer,按固定的时间间隔sendPing,如果连续几次没有收到pong,就认为链接已经断开了,需要重连。
这里一般是用一个变量来记录sendPing的次数,如果收到pong,就重置为0,如果没有收到sendPing的次数大于阈值,就重连。
2,如果用了NSTimer,就要考虑runloop的问题,把NSTimer添加到主线程的runloop当然是可行的,但肯定不是最好的,毕竟定时器是比较消耗资源的。如果添加到子线程,就涉及到子线程保活的问题。
常规的做法是,仿照AFNetworking2.X的写法,创建一个子线程的单例,然后再这个子线程中开启runloop保活,再把NSTimer添加到这个runloop当中,SocketRocket也是用了类似的方法,创建了一个自己的线程作为socket的操作线程。具体可以看SocketRocket的源码。
关于线程保活的问题,可以参考:https://bestswifter.com/runloop-and-thread/
3,服务端的消息转发机制
简单来说用户A想发消息给B,要经历以下几步:
1)A要将消息发送到服务器S,
2)S回给A一个响应告诉A服务端已经收到了
3)S转发这个消息给B
4)B回给S一个响应告诉服务端B已经收到了
步骤1的时候消息状态为发送中,如果在指定的时间间隔内没有收到响应,即没有到步骤2,那就要将消息标记为发送失败;
步骤3的时候服务端也要做类似处理,不同的是服务端要判断B是否在线,如果不在线,要等B上线以后再发送,且发送失败要有重试机制。
4,SocketRocket的回调是在子线程中,如果要在回调中更新UI,主要要自己切回主线程。
5,websocket安全问题,参考:https://www.ibm.com/developerworks/cn/java/j-lo-websocket-cross-site/