TCP-粘包
TCP 协议是面向连接的、可靠的、基于字节流的传输层通信协议,应用层交给 TCP 协议的数据并不会以消息为单位向目的主机传输,这些数据在某些情况下会被组合成一个数据段发送给目标的主机。
问题原因
基于字节流的传输协议,会有控制算法 拆分,组合应用层协议的数据
应用层协议 没有定义 消息的边界 导致 接收方 无法 拼接数据
同时 UDP / 网络层IP协议 等不存在粘包问题的原因是:
UDP 对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这就涉及到包大小确定:
UDP包允许发送最大值 64K,理想大小(1500 / 548 内外网)需要应用层来手动处理数据问题,但是TCP不存在包大小限制。
IP协议 基于 数据包 拆分为 IP报文的传输与接收是有边界协议的;并且数据在TCP传输层就会考虑到 IP报文的大小限制,主动分段处理。
IP分片一般用在UDP
TCP下的延迟发包算法:Nagle
减少发包的方式提高TCP传输性能的算法,但是现在服务器配置一般默认关闭;
- TCP_NODELAY 延迟发小包,等待小包重组小包发送
- TCP_CORK 小包延迟200ms发送
解决方式
设置消息边界即可;有两种方案 基于长度的封包/解包,基于终结符的组包
HTTP 协议的消默认息边界就是基于长度实现的:在 header头中添加了 Content-Length 字段。当应用层协议解析到足够的字节数后,就能从中分离出完整的 HTTP 消息,无论发送方如何处理对应的数据包,我们都可以遵循这一规则完成 HTTP 消息的重组
HTTP 协议除了使用基于长度的方式实现边界,也会使用基于终结符的策略,当 HTTP 使用块传输(Chunked Transfer)机制时,HTTP 头中就不再包含 Content-Length 了,它会使用负载大小为 0 的 HTTP 消息作为终结符表示消息的边界。
自定义消息边界实现
基于消息长度的,应用层添加封包/解包逻辑
- 添加固定字节的头信息
1 |
|
- 大端/小端写入:网络字节序,一般是大端,CPU写入内存主流是小端