TCP协议:现代网络数据传输的守护者——十大核心机制赋能高效可靠通信
一、先谈UDP协议
1.1、UDP协议端格式
16位UDP长度,表示整个数据报(UDP首部+UDP数据)的最大长度,源端口号和目的端口号占16个二进制位(2字节),范围0-65535
如果校验和出错,则会直接丢弃
UDP的特点:
• 无连接:不需要与对端建立连接,知道对端的端口号和IP即可直接传输
• 不可靠:没有确认应答、超时重传机制,即使因网络问题无法发送的对方,UDP协议也不会给应用层任何错误信息
• 面向数据报:不能灵活的控制读写数据的次数和数量
UDP使用注意事项
UDP协议首部有一个16位的最大长度,也就是UDP能传输的数据最大长度是64K(首部+数据),但在当今互联网环境下,64K是一个非常小的数据
如果我们想要传输的数据超过64K,就要在应用层手动的分包,多次发送,在接收端手动拼装
二、TCP协议
TCP全称为 "传输控制协议(Transmission Control Protocol"), 即要对数据的传输进行⼀个详细的控制
TCP协议端格式:
• 源/目的端口:表示数据从哪个进程发送,哪个进程要接收
• 32位序号/确认号:分别表示数据段的第一个字节的编号,用于保证数据按序到达和接收方期望的下一个字节序号,即已接收数据最高序号加1
• 4位数据偏移(等同报头长度):表示该TCP首部有多少个32位bit(4字节),所以TCP首部最大长度为15*4 = 60
• 6位标志位:
URG:紧急指针是否有效
ACK:确认号是否有效
PSH:催促接收端应用程序立刻从TCP缓存区将数据读走
RST:携带该标识的称为复位报文段:对方要求重新建立连接
SYN:携带该标识的称为同步报文段:请求重新建立连接
FIN:携带该标识的称为结束报文段:通知对端,本端将关闭
• 16位窗口大小:表示接收方当前可用缓冲区空间(单位:字节),接收方通过该字段告知发送方最多可发送多少未确认数据。
• 16位校验和:发送端填充,CRC校验,不通过则认为数据有问题,此处校验和:TCP首部 + TCP数据
• 16位紧急指针:表示哪些部分数据是紧急数据
• 40字节头部选项:可变长扩展区域,用于增强协议功能并适应复杂网络需求。
TCP协议的十大核心机制共同构建了其可靠传输与高效通信的基础架构,下面我们将对各机制详细讲解
三、TCP协议的十大核心机制
3.1、确认应答(可靠机制)
发送方每发送一个数据段,接收方需返回 ACK 报文确认接收成功。通过序号(32位)和确认号(32位)标识数据位置,解决网络“后发先至”导致的乱序问题
在传输数据的过程中,TCP将每个字节的数据都进行了编号,即为序列号
下图为TCP的载荷部分,因为TCP面向字节流,一个TCP载荷由多个字节构成,编写时每个字节都要分配一个编号,且连续递增
每一个ACK都带有对应的确认序列号,ACK确认序号隐含表示此前所有数据均已正确接收,如ACK=2001意味着1-2000字节数据全部到达
发送方传输数据时,序号字段填写的是载荷部分第一个字节的序号,真正的数据在载荷,如此一来,接收方就可以根据序号对数据进行排序~
后发先至问题
网络波动在我们生活中很正常,当然在传输过程中也可能遇到一系列情况
TCP在接收方会有“接收缓冲区” (内存,操作系统内核中),先把通过网卡读到的数据放到缓冲区里,后续代码里调用read方法从缓冲区里来读
3.2、超时重传(可靠机制)
超时重传就是针对丢包的情况做出处理~~
在一段时间后,A未收到B返回的应答,那么A 会再次向B发送一个一样的数据包
那么,我们如何确定超时的时间呢?
TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间
• Linux中(BSDUnix和Windows也是如此),超时以500ms为⼀个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍.
• 如果重发⼀次之后,仍然得不到应答,等待2*500ms后再进行重传.
• 如果仍然得不到应答,等待 4*500ms 进行重传.依次类推,以指数形式递增.
• 累计到⼀定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接
3.3、连接管理(可靠机制)
⚽ 建立连接的意义:
• 投石问路,确认当前通信路径是否畅通.
• 协商参数,通信双方共同确认⼀些通信中的必备参数数值
正常通信流程:
3.1、三次握手
以上来回三次通信即“三次握手”
目的:
• 初步探索网络的通信链路是否通畅(可靠传输的前提条件)
• 验证双方的发送能力和接收能力是否正常
• 协商通信过程的关键信息(序号从几开始,为了避免相邻的两次通信,序号是不从1开始的,而且两次连接传输的序号差别还很大)
3.2、四次挥手
3.4、滑动窗口(效率机制)
上文我们谈到数据传输过程,每发送一个数据包,都要等一个ACK响应,这种方法确实保证了TCP传输数据的可靠性,但是也是要付出代价滴~,在数据量较大且往返时间长的情况下,这种方法的效率低,性能差
这样一发一收的方式性能较低,那么我们是否可以一次发多个,一次收多个ACK应答,进行批量传输,发到一定量之后,再等待
• 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值,上图的窗口大小就是4000个字节 (四个段)
• 发送前四个段的时候,不需要等待任何ACK,直接发送
• 收到第⼀个ACK后,滑动窗口向后移动,继续发送第五个段的数据,依次类推
• 操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答,只有确认应答过的数据,才能从缓冲区删掉
• 窗口越大,则网络的吞吐率就越高
丢包情况一、数据到达,返回的ACK丢失
丢包情况二、数据包丢失
当序号为 1001-2000 的数据包丢失,发送端会一直收到 1001 这样的 ACK,若发送端连续收到 3 次这样重复的确认应答,就会把相应的数据重发一遍,此时接收端收到再次发送的数据后,就会直接返回 7001 的ACK应答 ,因为 2001-7000 的数据已被接收,放入到接收缓冲区中,这种机制被称为"高速重发控制"(也叫"快重传")
超时重传:传输的数据少,没有构成滑动窗口批量传输的形式
快速重传:数据量大,形成了滑动窗口
3.5、流量控制(效率机制)
针对滑动窗口机制,窗口越大,效率越高,但是不能 “无限大” ~~,接收端处理数据的速度是有限的,如果发的太快,接收端的缓冲区就会堆满,后面来的数据包就会丢包,继而引发丢包重传等一系列连锁反应
因此TCP支持根据接收端的处理能力,来决定发送端的发送速度,这个机制就叫做流量控制(Flow Control)
• 接收端将自己可以接收的缓冲区大小放入TCP首部中的"窗口大小"字段,通过ACK端通知发送端
• 窗口大小字段越大,说明网络的吞吐量越高
• 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端
• 发送端接受到这个窗口之后,就会减慢自己的发送速度;
• 如果接收端缓冲区满了,就会将窗口置为0,这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端
接收端就是把窗口大小的信息存放到TCP首部中16位的一个窗口字段,滑动窗口的大小是动态变化的
零窗口探测:当窗口为0时,发送方定期发送1字节探测包,直至接收方恢复可用窗口
16位数字最大表示65535,那么TCP窗口最大就是65535字节嘛,实际上,TCP首部40字节的选项中还包含一个窗口扩大因子M,实际窗口大小就是窗口字段的值左移M位
3.6、拥塞控制(效率机制)
刚谈到流量控制是依据接收方处理能力进行限制的,而拥塞控制则是依据传输链路的转发能力,进行限制的
网络上有大量计算机可能当前的网络状态已经比较拥堵,在不清楚当前网络状态下贸然发送大量数据,很可能会引发问题,故TCP引入慢启动机制,先发少量的数据,摸清当前的网络状态,再决定按照多大的速度进行传输数据

• 此处引入一个概念程为拥塞窗口
• 发送开始的时候,定义拥塞窗口大小为1
• 每次收到⼀个ACK应答,拥塞窗口加1
• 每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口
像上面这样的拥塞窗口增长速度,是指数级别的,"慢启动"只是指初使时慢,但是增长速度非常快
• 为了不增长的那么快,因此不能使拥塞窗口单纯的加倍
• 此处引入一个叫做慢启动的阈值
• 当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长
• 当TCP开始启动的时候,慢启动阈值等于窗口最大值
• 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1
少量的丢包,我们触发超时重传,大量的丢包,我们就认为网络堵塞,大概流程如下:
3.7、延时应答(效率机制)
在前几个TCP机制中,接收方都是在收到数据包时,立即返回ACK应答,特别是在滑动窗口处,我们每次应答还需返回窗口大小,倘若我们多等一会再返回ACK应答,此时接受方的缓冲区内容就会一直被读取,等待的这一段时间后,我们再做出ACK应答,返回的窗口大小就可能比之前的大了,但要注意,不是所有包都可以延时应答
• 数量限制:每隔N个包就应答⼀次;
• 时间限制:超过最大延迟时间就应答⼀次;
3.8、捎带应答(效率机制)
捎带应答是在TCP延时应答的基础上引入的,通俗来说就是,返回业务数据的时候,顺便把上次的ACK也带过去
没有延时应答时,返回ACK的时机和返回响应的时机是不同的时机,引入延时应答,ACK的返回可以往后延长一段时间,返回响应的时候就可以把ACK顺便带回去,提高效率~~
数据与ACK合并:在双向通信中,业务数据报文可携带对反向数据的ACK,降低网络负载。例如,服务器响应请求时同时确认客户端数据
3.9、面向字节流与粘包处理(数据处理)
TCP协议是通过字节流的方式传输,在应用层很容易混淆包与包的界限,从而无法区分哪到哪是一个完整的应用层数据包
此问题在TCP层次上无解,需要在应用层解决,定义好应用层协议,明确包之间的边界~
1、约定包与包之间的分隔符(包的结束标记,如TCP回显服务器中的“ ”)
2、约定包的长度
而UDP则不存在粘包问题,因为UDP传输的是一整个数据报
3.10、异常处理(可靠性保障)
进程终止:进程终止会释放文件描述符,仍然可以发送FIN,和正常关闭没有什么区别.
机器重启:和进程终止的情况相同.
机器掉电/网线断开:接收端认为连接还在,⼀旦接收端有写入操作,接收端发现连接已经不在了,就会进行reset,即使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在,如果对方不在,也会把连接释放
复位报文: