个人实现的一个IM的SDK
基于TCP协议的socket实现,因为基于报文交互,所以服务端也是个人实现
具体见https://github.com/dda135/IM
以下纯属个人看法,可能有一些瑕疵,仅做记录
1.一个可用的IM项目必须基于能够自主研发,否则一旦出现问题很可能无法进行修复和扩展
2.TCP和UDP的选择,从使用上面来说,个人更加倾向于TCP,因为协议本身能够保证报文传递的有序性而不需要程序上面对于报文有额外的操作,当然UDP的好处就是快、开销小,这样对于服务器来说可能会好一点
3.报文协议的设计考量:
(1)因为存在ping操作,也就是检查连接是否正常或者服务端是否正常的行为,这种行为是很频繁的,所以说报文的大小会直接影响到用户流量的消耗大小,所以说报文大小能够越小越好
(2)因为报文要小,那么基于字节肯定是最好的选择,这样子传统的文本协议Json、Xml这些可能就不是比较好的选择
(3)基于字节设计,现成的方案就是基于Protocol Buffer协议来做,问题就是会带来大量的方法数
(4)完全自定义,这种需要非常合理的设计,因为还设计到扩展等问题,需要足够成熟才行,难度较大
4.设计流程
(1)连接操作:IM开始必须要去尝试建立连接,要想建立一个连接至少要有以下几步操作:
(1.1)客户端发给服务端一个请求建立连接的报文,然后开始计时,到时就认为连接建立失败
(1.2)服务端收到了请求报文,然后发送一个响应报文给客户端,用于告诉客户端服务端同意建立连接
(1.3)客户端收到报文之后,客户端认为连接建立
一个问题,服务端什么时候认为连接建立:
如果类似TCP三次握手的设计,那么应该在(1.3)步之后要求客户端再发送一个报文,然后服务端接收到报文之后才认为连接建立
同样服务端也可以在(1.2)步完成之后就认为连接建立
如果采用第一种做法,那么假设最后一个报文丢失,那么就是出于客户端认为连接建立,而服务端没有建立,这样的话在下一个ping操作或者发送报文操作之前,客户端都会处于假连接状态,也就是看上去明明登录了,但是却一直收不到消息
如果采用第二种做法,假设客户端没有收到(1.3)这个报文,那么客户端会等待连接定时器超时进行重连,服务端认为客户端在线,然后推送消息的时候会直接失败,此时应当在服务端进行离线消息存储操作
(2)ping操作:当连接建立之后,客户端要开始进行不断的触摸操作来确保当前服务端可用(避免NAT导致无效连接,同样也可以在当前服务端节点繁忙后去尝试更换其他节点)
(2.1)客户端发给服务端一个ping请求报文,然后开始计时,到时就认为ping失败,尝试与其他节点建立连接
(2.2)服务端如果认为连接仍然健壮,发给客户端一个ping成功报文
(2.3)客户端收到报文,ping成功
(3)发送数据操作:当连接维持中,客户端向服务端发送数据,可以是图片或者文本数据,总之都是字节流数据
模拟一个用户A发消息给用户B的操作
(3.1)客户端A发送给服务端数据报文,报文类型为客户端发送消息给服务端,并且开启计时器,计时器超时就认为发送失败
(3.2)服务端收到报文,然后解析数据,根据数据中的接收者信息去匹配当前连接中的socket,先进行离线消息的存储,然后组装数据,向B客户端发送数据报文,报文类型为服务端发送消息给客户端
(3.3)客户端B收到消息,然后发送报文给服务端,报文类型为客户端收到服务端消息,接着根据数据做业务操作
(3.4)服务端收到客户端B发过来的报文,将离线消息删除,然后发送报文给客户端A
(3.5)客户端A收到服务端的报文,认为发送成功,进行业务操作
上面是我目前用的一种方式,流程中一共传递了4个报文,但是能够看出,在消息是否接收这一点是有一点问题的,客户端和服务端实际上是用过两个报文来进行数据接收确认的
比方说在(3.3)完成之后,此时在客户端B认为消息接收成功,但是后续(3.4)和(3.5)报文丢失,此时在server和客户端A认为消息发送失败,此时server会有一份离线消息,并且ClientA是认为发送失败的,此时会要求客户端B在处理消息的时候需要进行本地去重操作。