用Twisted做了一个简单的TCPServer。使用自己的模拟客户端进行测试无问题。但是客户设备出现一个奇葩问题。该设备使用一个国产的WiFi模块上传数据。但是出现将完整数据包截断分成两个TCP报文上传的现象。
这个可能是无法避免的事情。毕竟嵌入式系统里RAM总是有限的。
解决方法之一,是将datareceived()改成linereceived(),即每个数据包成为带结束符号的一行。但是这在二级制数据里面似乎还需要转义字符之类的。
解决方法之二:是在TCP连接中将前后两个TCP报文进行拼合。但是这个上下文存在哪里呢?是Factory里?好像不能够是全局变量。
首先,TCP协议是基于流的,所以你遇到的“出现将完整数据包截断分成两个TCP报文上传的现象”这个问题是很正常的,并不奇葩,“使用自己的模拟客户端进行测试无问题”的原因只是网络好。
TCP一端发送的数据是这样:


接收方收到的很可能是这样:
针对这个问题,则需要接收方对接收到的数据进行分割或者合并。
主要有以下几种方式:
1、use fixed length messages
使用固定长度的消息。比如每个长度4字节,那么接收的时候按每条4字节拆分就可以了。
2、use a fixed length header that indicates the length of the body
使用固定长度的Header,Header中指定Body的长度(字节数),将信息的内容放在Body中。例如Header中指定的Body长度是100字节,那么Header之后的100字节就是Body,也就是信息的内容,100字节的Body后面就是下一条信息的Header了。
3、using a delimiter; for example many text-based protocols append a newline (or CR LF pair) after every message
使用分隔符。例如许多文本内容的协议会在每条消息后面加上换行符(CR LF,即”\r\n”),也就是一行一条消息。当然也可以用其他特殊符号作为分隔符,例如逗号、分号等等。
针对按换行符分割的方式,可以使用LineOnlyReceiver的lineReceived,如果每条消息本身有可能包含换行符,那么这种方法就不行了。
所以一般比较好的解决方法是第二种:用一个固定字节数的Header前缀来指定Body的字节数,以此来分割消息,Twisted中使用的API是Int32StringReceiver:

最后,推荐几篇我写的相关博客:
TCP消息边界问题及按行分割消息:http://xxgblog.com/2014/08/21/mina-netty-twisted-2/
TCP消息固定大小的前缀(Header):http://xxgblog.com/2014/08/22/mina-netty-twisted-3/
定制自己的协议:http://xxgblog.com/2014/08/25/mina-netty-twisted-4/