本文要讨论的IM通信协议是指应用层通信的“语言”,而不是传输层协议(如TCP、UDP)。IM通信协议的制定是IM开发的起点,也是贯穿设计、开发、运行和维护的核心。通信协议的设计直
本文要讨论的IM通信协议是指应用层通信的“语言”,而不是传输层协议(如TCP、UDP)。IM通信协议的制定是IM开发的起点,也是贯穿设计、开发、运行和维护的核心。通信协议的设计直接影响到用户体验(数据流量、功耗、通信速度)、兼容性(新老版本无缝融合)、可扩展性(后期版本如何升级)等等,是一项基础性的、极其重要的工作。
本文将通过理论与实践相结合的方式,详细解释一个典型IM的通信协议设计的各个方面。
本文原作者为58同城网沈剑:58同城技术委员会主席,高级架构师,优秀讲师。原百度hi团队成员,负责58同城im系统架构设计。
所谓“协议”,就是双方共同遵守的规则,比如离婚协议、停战协议等。
协议有三个元素:语法、语义和时间:
(1)语法:即数据与控制信息的结构或格式(2)语义:即需要发出何种控制信息,完成何种动作以及做出何种响应(3)时序:即事件实现顺序的详细说明
典型的IM通信协议设计分为三层:应用层、安全层和传输层。
即时消息应用层协议设计
应用层协议有三种常见类型:文本协议、二进制协议和流式XML协议。
文本协议是指“接近人类书面语言表达”的通信传输协议。典型的协议是http协议。http协议看起来像这样:
1234GET / HTTP/1.1User-Agent: curlHost: musicml.netAccept: */*
文本协议的特征是:
a. 可读性好,便于调试b. 扩展性也好(通过key:value扩展)c. 解析效率一般(一行一行读入,按照冒号分割,解析key和value)d. 对二进制的支持不好 ,比如语音/视频
在IM中,MSN使用文本协议。
2.二元协议
二进制协议是指二进制协议,典型的是ip协议。下面是ip协议的一个示例:
二进制协议一般有定长头和可扩展的变长头,每个字段都有固定的含义。例如,IP协议的前四位表示协议版本号(详见TCP/IP详解第3章)。
二进制协议有一些特点:
a. 可读性差,难于调试b. 扩展性不好 ,如果要扩展字段,旧版协议就不兼容了,所以一般设计时会有一个Version字段c. 解析效率超高(几乎没有解析代价)d. 对二进制的支持不好 ,比如语音/视频
IM,QQ使用时间二进制协议。
xmpp,im的准标准协议,使用流XML,比如gtalk。学校里的这些IM都是基于xmpp的。我们来看一个xmpp协议的例子:
1234567 <message to = '[URL = mailto:Romeo @ example . net]Romeo @ example . net[/URL]' from = '[URL = mailto:Juliet @ example . com]Juliet @ example . com[/URL]' type = ' chat ' XML:lang = ' en ' >& ltbody & gt你在哪里,罗密欧?& lt/body >& lt/message >
从xml标签可以大致判断这是罗密欧发给朱丽叶的聊天信息。Xmpp协议可以实现跨域互操作。比如gtalk和学校里的用户聊天。只要在服务器端实现s2s服务(server to server),现在im基本没有互通需求,所以这个服务基本没有人实现。
XMPP协议有几个特点:
a.它是准标准协议,可以跨域互通b.XML的优点,可读性好,扩展性好c.解析代价超高(dom解析)d.有效数据传输率超低(大量的标签)
个人强烈不建议使用xmpp,尤其是无线im。如果要用,必须自己压缩,减少网络流量(用过xmpp的同学都知道,发一个登录包需要多少交互,浪费多少流量)。
让我们来看一个im协议的实际例子。常见的做法是:定长二进制包头,可扩展变长包体。包可以使用可扩展的协议,如文本和XML。包头负责传输和解析效率,与业务无关。包保证可伸缩性,与业务相关。
这是一个实际的16字节im二进制定长头:
123456789//sizeof(cs _ essay-header)= 16 struct cs _ essay-header { uint 32 _ t version;uint32 _ t magic _ numuint32 _ t cmduint32 _ t lenuint 8 _ t data[];}__attribute__((打包));
–a.前4个字节是版本;;
–b.接下来的四个字节是一个“幻数(magic_num)”,用来保证数据错位或者丢包。通常的做法是在报头中加入一些约定的特殊字符,在包的末尾加入一些约定的特殊字符。发送给您的协议,几个字节的位置,是0x 01020304。
–c.接下来是command(命令号),用于区分keepalive报文、业务报文、密钥交换报文等。
–d.len(数据包长度),它告诉服务器应该在多长时间内收到数据包。
这是一个实际的可扩展im变长包:
123456789 message CUserLoginReq {可选字符串username = 1;可选字符串passwd = 2;}message CUserLoginResp{可选uint 64 uid = 1;}
用的是google的Protobuf协议(玩过的都能看懂)。如您所见,登录请求包传输用户名和密码,登录响应包返回用户的uid。当然,除了Protobuf,还有xml、json、mcpack(你懂吗?)等等。
立场鲜明的推荐Protobuf。主要有几个原因:
a. 现成的解析库种类多,可以生成C++、Java、php等10余种语言代码(详见请点b. 自带压缩功能c. 在工业界已广泛应用d. google制造
IM安全层协议设计
im协议,消息的保密性很重要,谁都不希望自己的聊天内容被看到,所以安全层必不可少。
可选协议是TCP和UDP。现在IM传输层基本都用TCP。有了epoll等技术,多连接不是瓶颈,几十万个单机链接也没有问题。58同城在线单机连接现在好像是10w?(可能单机性能测试能达到一百万,一般在线跑到几十万)
关于QQ使用UDP的问题。我个人不知道QQ使用UPD作为传输层协议的初衷,但我的猜测是10多年前客户端10K的问题没有得到很好的解决,一台服务器无法支持1W的TCP连接。腾讯同时在线流量高,没办法只能用UDP,但是UDP不可靠,只能在UDP上实现TCP超时/重传/确认机制。
【/br/】关于QQ使用UDP协议,在讨论过程中,有同学提出了一个非常好的问题:“无线环境下,UDP比较好,可以独立于状态,而TCP不稳定,进出电梯就会断线,用户体验不好”。
实际上,“用户状态可以被设计成独立于连接状态”。如果你有兴趣,我可以写一篇文章以后和你聊天。传输层使用TCP,那么如何让在线状态独立于底层连接。