tcp前4字节消息长度_关于TCP粘包问题的终结(2020中文版)

知乎上关于这个概念的讨论在这里传送门

厘清概念

  1. 英文中没有“粘包”这个术语,应为国人误定义了一种现象。

2. TCP是处理字节流的传输协议,发送的是字节流,接收的是字节流。

3. 一般讨论这个问题的主要是PHP中的Swoole开发者和java中的netty开发者。

在 swoole 的开发文档中明确的写出了 “TCP 粘包问题” 。

在 ”Netty精粹之TCP粘包拆包问题“ 一文中大谈特谈这个问题。

之所以会有人搞不清楚问题的本质是因为对网络基础知识的缺乏,因此本文要从7层协议说起。

基础知识

我们常挂在嘴边的 TCP/IP 协议是 TCP 协议和 IP 协议两种协议的组合以及在其之上构建的应用协议的集合,因此也叫TCP/IP 协议簇。

IP 协议是网络层协议(3层),TCP是传输层协议(4层),HTTP是应用层协议(7层)。

我们看看 OSI 模型是哪7层:

d06dcbc1cf1febdbf3b894d77b59a3db.png

最底层的以太网协议(Ethernet)规定了电子信号如何组成数据包(packet)。

IP 协议定义了一套自己的地址规则,称为 IP 地址。它实现了路由功能,允许某个局域网的 A 主机,向另一个局域网的 B 主机发送消息。

TCP特点有如下几个:

  1. TCP是面向连接的运输层协议
  2. TCP连接只能有两个端点
  3. TCP提供可靠交付
  4. TCP提供全双工通信
  5. 面向字节流

包的大小

以太网数据包(packet)的大小是固定的,最初是1518字节,后来增加到1522字节。其中, 1500 字节是负载(payload),22字节是头信息(head)。这个1500就是网络设备设置的MTU大小了,因此正常来说MTU只能比1500小,更详细的资料参考“以太网帧大小到底多大?”。

IP 数据包在以太网数据包的负载里面,它也有自己的头信息,最少需要20字节,所以 IP 数据包的负载最多为1480字节。

33c38423e6efda26846f428b8743402d.png

TCP 数据包在 IP 数据包的负载里面。它的头信息最少也需要20字节,因此 TCP 数据包的最大负载是 1480 - 20 = 1460 字节。由于 IP 和 TCP 协议往往有额外的头信息,所以 TCP 负载实际为1400字节左右。

因此,发送一条有效内容1500字节的信息需要两个 TCP 数据包。

两个 TCP 数据包分别拆成1460字节+40字节,假设系统真的直接把这两个包发出去的话,则每个包还要额外发送的信息为20(TCP头)+20(IP头)+22(以太网头)。可见第二次发送存在极大的浪费。

0141bf2d35c4b3abd519c907960ef1d7.png

在Socket网络编程中,操作系统内核提供了send() recv()两个方法。

当应用程序调用Socket接口发送函数Send( ),发送1000个字节时,Send()立马返回,不阻塞(Non-Blocked)。试问此时这1000个字节到接收端了吗?不一定!TCP什么时候发、发送频率、发送间隔、包的尺寸大小不受应用程序的直接指挥,而是由自己的代码来进行控制。但无论如何,TCP还是有自己的服务承诺的:按照发送方的字节顺序将字节流按序提交给接收方应用程序

发送方的Send()可以按照自己的节奏向TCP供应商灌入数据,如同机关枪一样,一秒钟60发子弹。但TCP的发送节奏和机关枪的发射节奏不完全相同,一秒钟可能是40发,也有可能是80发。如果接收方按照一秒60次的频率(和发送方频率相同)接收,造成的后果就是,要么Receive()到的数据是空的,要么Receive()到的数据是1460字节,或者其它的字节数。

程序员就会有疑问了:明明是按照一个包1000字节发送的,为何接收方接到的包尺寸为1460字节?

实际上我们的问题不是TCP的粘包问题,而是如何在TCP协议基础上设计应用协议,并保证接收方可以按顺序读取并解析到发送方希望发送的数据大小。

方案一:在发送的TCP数据前标明该数据的长度,接收方如果没有接收完该长度的数据则继续等待,直到读完或者超时异常终止。

方案二:参考HTTP协议,以文本传输,其中content-length标示了数据长度,而且特殊字符rn可以标示换行或终止等

在以上方案外,还可以参考其他各种基于TCP协议的应用层协议,或者自创协议并实现。

参考资料

TCP 协议简介 - 阮一峰的网络日志

及各种文中链接

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值