XMPP eJabberd 粘包处理

DavidAlphaFox · 发布于 2017年09月23日 · 318 次阅读
84794b

什么是粘包

因为UDP的协议特性,粘包现象并不会出现在UDP当中。因此粘包一般都出现在TCP当中,不过并不是使用TCP进行数据传输就会产生粘包。

TCP长链接

客户端和服务器建立起一个TCP连接后,进行多次数据交换而不断开,必要的时候会插入和数据无关的心跳包来保持TCP连接不被中间路由强制断开。

TCP短链接

客户端与服务器没进行一次数据交换就建立一次连接,完成数据交换后立刻断开连接。

粘包的产生

从上面可以看出,只有是TCP长连接存在的时候,才会发生粘包,因为TCP是流式传输的。在应用层面上虽然可以划分出一个个数据包,但是TCP传输层会将数据包整合一起发送或缓存着被应用一次性取出。简单的说就是:

1.发送方需要等到缓存区被填充满了之后,再发送,因此造成了粘包现象。 2.接收方不能快速的处理缓存区中的包,造成多个包堆积在缓存区中,因此造成了粘包现象。

如何解决粘包

最常见的方式就是,在每次发送数据包之前,先定义一个数据包长度。在接收数据包的时候先读取数据包长度,再读取整个数据包。进一步去完成业务操作。

eJabberd是如何处理粘包的

作为即时通讯,eJabberd在和普通的客户端进行通讯的时候底层技术也使用的是TCP长连接(Web做客户端还有websocket和BOSH这两个技术)。因此eJabberd也会出现粘包这一现象,这里就介绍下eJabberd是如何处理粘包的。

ejabberd_receiver模块

ejabberd_receiver可以说是eJabberd的XML输入流最主要的部分,它负责管理了socket,输入流的流控和XML的解析器。在每次收到数据后,ejabberd_receiver都会更新流量控制器,防止一个客户端过快的发送数据,而给ejabberd_c2s造成过大的负担。

在ejabberd_receiver进程被创建之后,会立刻初始化一个exml_stream的解析器。因为XMPP是基于TCP长连接的XML流,而XMPP又规定了XML流上传输的数据是通过XML节这种结构化格式封装的,所以这里XML的节就相当于数据包前方的长度字段了。简单的说:读取了一个完整的XML节,就是一个数据包,剩下的数据就是后续的数据包。

exml项目

exml_event

exml_event是一个对libexpat进行封装的NIF,可以进行高效率的XML解析。exml_event会将xml数据解析成xml_element_start,xml_element_end和xml_cdata这三个标签。如果传入的数据没有被完全被解析,会保存在libexpat的parser的上下文当中。

exml_stream

exml_stream会将数据交给exml_event进行解析,会对解析出来的events(上面提到的xml_element_start,xml_element_end和xml_cdata)进行转换。将它们转化成xmlstreamstart,xmlstreamend和xmlel的Erlang的record。在exml_stream的上下文当中,会将没有闭合的xml节保存起来,直到整个XML节闭合后才会交还给ejabberd_receiver。

总结

eJabberd使用XML节闭合的特性,作为数据包的边界来解决粘包问题。同时使用libexpat的parser来缓存没有解析完的原始数据,使用exml项目中的exml_stream来缓存解析成功,但是没有闭合的XML节。从而保证了XMPP流中的XMPP节的完整性和TCP长链接的粘包问题。

暂无回复。
需要 登录/注册 后方可回复, 如果你还没有账号请点击这里 注册