前言:
目前看官们对“java socketchannel粘包处理”大约比较珍视,大家都想要分析一些“java socketchannel粘包处理”的相关内容。那么小编在网络上搜集了一些关于“java socketchannel粘包处理””的相关资讯,希望同学们能喜欢,同学们快快来了解一下吧!什么是粘包问题?
因为TCP协议是流式协议所以数据包之间没有边界,有时会因为操作系统缓存机制、网络延迟等原因造成2次间隔时间较短、数据量较少的数据合并成一次发送。因此影响了数据的完整性。
粘包问题解决策略:
常见的解决方式是通过自定义协议厘清数据包之间的边界。
发送方:
1.发送数据包前先计算长度,再将int型长度数据转换成4字节的bytes型;
2.先发送4字节bytes型长度数据,再发送数据包。
接收方:
1.先接收4字节bytes型长度数据,将其转换成int型长度数据。
2.只接收指定长度的数据。
以上协议是服务端和终端双方均要遵守的自定义协议。这样就可以解决粘包问题。
区分消息和文件
通常而言消息大小设为1024字节,发送消息前要先发送消息的长度,然后再发消息。接收时也是要先接收长度,再接收消息。
发送文件时要先计算文件的大小,然后发送文件的长度,再发送文件。接收时要先接收文件的大小,socket文件收发缓存大小通常设为65535。注意:文件发送时发送方发送的数据包大小是固定的,但接收方每次收到的数据包大小不固定,每次接收后要用文件长度减去接收到的数据包长度,判断剩余未接收文件长度小于1才算完成。
stuct模块pack和unpack缺陷
处理粘包问题我查阅了很多资料,看到绝大多数人都是import struct,使用struct.pack和unpack来完成int数据与bytes相互转换的工作。但是我觉得struct模块的pack和unpack有2个缺陷:一是表示数值范围是-2147483648至2147483647,负值在计算数据长度完全用不上,会造成上传、下载文件大小不能超过2个G,unpack返回的是一个元组,还要对元组解包才能使用。所以我尝试自己写了一个pack和unpack在下面分享给大家。
自定义pack和unpack
def pack(n): if n >= 4294967296 or n < 0: raise ValueError('The value is out of range.') values = ((0b11111111000000000000000000000000, 24), (0b111111110000000000000000, 16), (0b1111111100000000, 8), (0b11111111, 0)) ret = b'' for i in values: x = (n & i[0]) >> i[1] x = x.to_bytes(length=1, byteorder="big") ret += x return ret
def unpack(numlist): if len(numlist) != 4: raise ValueError("The bytes length must be 4.") values = (24, 16, 8, 0) ret = 0 i = 0 while i < 4: n = numlist[i] ret += n << values[i] i += 1 return ret
代码说明:
自定义的pack函数表示范围是0-4294967295,上传、下载文件长度在4个g以内都不会报错。
在该函数中通过按位与配合位移算法以及python3内置函数to_bytes()来完成功能,不需要另外import。
自定义的unpack函数直接返回int型数值,不需要解包。在这个函数里全部是自定义的代码,没有引用任何函数也没有导包,通过位移运算完成bytes转换成int。