UDP,TCP协议的格式与机制
目录
一:UDP
1.UDP协议端格式
2.UDP的特点
二:TCP
1.TCP协议段格式
2.确认应答机制
1.确认应答的原理
2.为什么有两个序号?——捎带应答
3.超时重传
4.快速重传
3.连接管理机制
1.三次握手
2.四次挥手
shutdown函数
COLSE_WAIT状态
TIME_WAIT状态
4.滑动窗口
1.滑动窗口是什么
2.拥塞控制
一:UDP
1.UDP协议端格式
目的端口号(Destination Port)
接收方主机收到 UDP 报文后,根据这个端口号,决定把数据交给哪个应用程序处理。 比如:DNS 常用 53、TFTP 常用 69、QUIC 常用 443 等。源端口号(Source Port)
标识发送方自己是哪个程序发出的数据。一般由操作系统随机分配,方便对方收到数据后,按这个端口原路回复给你。16 位 UDP 长度(UDP Length)
表示整个 UDP 数据报的总字节长度= UDP 固定头部(8 字节) + 数据部分 16 位,所以取值范围:8 ~ 65535 字节(64KB)16 位 UDP 校验和(UDP Checksum)
校验和不一致:认为传输出错,直接丢弃,不重传、不纠错
2.UDP的特点
- ⽆连接: 知道对端的IP和端⼝号就直接进⾏传输,不需要建⽴连接;
- 不可靠: 没有确认机制,没有重传机制;如果因为⽹络故障该段⽆法发到对⽅,UDP协议层也不会给 应⽤层返回任何错误信息;
- ⾯向数据报: 不能够灵活的控制读写数据的次数和数量;
二:TCP
1.TCP协议段格式
- 源/目的端口号:表⽰数据是从哪个进程来,到哪个进程去;
- 32位序号/32位确认号:序号和确认序号可以同时存在,序号保证数据有序交付,确认序号累计确认已接收的数据,触发发送方重传未确认数据
- 4位TCP报头长度[0 - 15],乘以一个单位(规定是4字节),等于报头的长度:[0 - 60]字节。
所以一个报头的长度范围是[20 -60]字节。
6位标志位
SYN(Synchronize):发起连接请求或响应,典型用于三次握手前两次;把携带SYN标识的称为同步报文段
ACK(Acknowledgment):确认已接收数据 / 控制信息,使确认序号生效;
FIN(Finish):发起正常关闭连接,释放发送方的发送权,核心用于四次挥手;称携带FIN标识的为结束报文段
RST(Reset):复位异常连接(强制关闭),处理连接超时、端口未监听、协议错误等场景;把携带RST标识的称为复位报文段
PSH(Push):推送数据,要求接收方立即递交给应用层(不缓存),适用于键盘输入、聊天消息等实时数据传输;
URG(Urgent):标识段中包含紧急数据,需优先处理,需配合 “紧急指针” 字段使用。紧急指针字段会指向紧急数据的最后一个字节的下一位
16 位窗口字段:自己的接收缓冲区还剩余多少可用空间
- 6位校验和:发送端填充,CRC校验.接收端校验不通过,则认为数据有问题.此处的检验和不光包含 TCP⾸部,也包含TCP数据部分.
- 16位紧急指针:标识哪部分数据是紧急数据;
2.确认应答机制
1.确认应答的原理
怎么保证我们发送的报文被对方收到了? ——对方收到报文后,返回确认应答,我们收到确认应答,就可以保证之前的数据成功被对方接收了
- TCP将每个字节的数据都进行了编号.即为32位序号(SEQ),TCP 报文段头部的 SEQ 字段,代表的是该报文段携带的第一个数据字节的序号。
- ISN
- TCP 连接刚建立时,双方各自随机选的第一个 “字节编号”,就是 ISN。
- 为了安全,ISN 是系统随机生成的
安全层面:防止攻击者轻易猜到序列号,实施 TCP 会话劫持、伪造恶意报文等攻击;
可靠性层面:避免历史连接的重复报文,被新建立的同四元组(源 IP、源端口、目的 IP、目的端口)连接错误接收,造成数据错乱。
- 双方各有一个 ISN
客户端第一次握手发的
seq = x→ 客户端的 ISN服务器第二次握手发的
seq = y→ 服务器的 ISN
TCP 的确认应答不是对单个报文段的确认,而是对连续字节流的累计确认,这是机制的核心设计。接收方回传的 ACK 报文中的确认号,有严格的定义:
确认号 = 接收方已经成功、连续接收的最后一个字节的序号 + 1
含义:「确认号 - 1」及之前的所有字节已全部成功接收,接下来期望收到的第一个字节的序号就是该确认号。
举个完整的基础示例:
- 发送方发送 SEQ=1001、数据长度 1000 字节的报文(覆盖 1001~2000)
- 接收方成功收到该报文,回传 ACK 报文,其中ACK 号 = 2001
- 发送方收到 ACK=2001,即可确认 1001~2000 的所有字节已成功交付,无需再关注这段数据。
累计确认的核心优势:
- 一个 ACK 可以确认连续的多个报文段,无需为每个报文单独回 ACK,大幅减少网络开销;
- 强制要求只有连续的字节被完整接收,才会推进确认号,从根本上解决乱序问题。
2.为什么有两个序号?——捎带应答
为了减少网络中的纯 ACK 报文数量,提升传输效率,TCP 规范定义了两个优化规则:
- 延迟 ACK:接收方不会对每个报文都立刻回 ACK,而是等待最多 200ms;若这段时间内有反向数据要发送给对端,就将 ACK 确认号捎带在反向数据报文的 TCP 头部中一起发送,无需单独发 ACK 包——既是对上一个接收报文的确认,又是一个发送报文,所以既有序号,又有确认序号。
- 强制立即 ACK:遇到乱序报文、重复报文、丢包后补传的报文等异常场景,接收方会立即发送 ACK,不触发延迟,让发送方尽快感知网络状态,及时调整传输策略。
3.超时重传
如果发送的报文丢失,或者ACK丢失怎么办呢?
- 发送方每发出一个报文段,就会启动一个重传计时器(RTO,Retransmission TimeOut),RTO 的超时时间基于网络往返时延(RTT)动态计算,适配网络波动。
- 若 RTO 超时后仍未收到对应 ACK,发送方判定该报文段丢失,立即重传该报文段,并重置计时器再次等待 ACK,直到收到对应 ACK 为止。
- 累计到⼀定的重传次数,TCP认为⽹络或者对端主机出现异常,强制关闭连接.
4.快速重传
连续收到 3 个重复 ACK,不等超时就立刻重传,速度更快。
3.连接管理机制
1.三次握手
三次握手的核心目的:让通信双方(客户端 & 服务器)互相确认:自己的发送 / 接收能力正常、对方的发送 / 接收能力正常,并同步双方的初始序列号(ISN)。
两次握手无法完成双向收发能力的全验证
为什么不是四次握手?
第二次握手中,服务器的 ACK(确认客户端请求)和 SYN(同步自身序列号)可以合并在一个报文中发送,没有必要拆分成两个独立报文
2.四次挥手
四次挥手的核心目的:双方各自独立关闭自己的发送通道,互相确认对方的关闭请求,确保所有数据都传输完毕,安全、完整地断开连接。
为什么是 4 次,不能是 3 次?
TCP 连接的两个传输方向是完全独立的。一方关闭自己的「发送通道」(发 FIN 报文),仅代表自己不再发送新数据,但依然可以接收对方发来的数据,直到对方也关闭发送通道。
shutdown函数
#include <sys/socket.h> int shutdown(int sockfd, int how);sockfd:TCP 连接的套接字文件描述符how:关闭方式
| 参数宏定义 | 数值 | 作用(TCP 通道控制) | 关键行为 |
|---|---|---|---|
SHUT_RD | 0 | 关闭读通道 | 无法再接收数据,内核丢弃缓冲区数据;发送通道正常 |
SHUT_WR | 1 | 关闭写通道 | 本方不再发送数据,主动发送 FIN 报文(触发四次挥手第一次挥手);仍可以接收对方数据 |
SHUT_RDWR | 2 | 关闭读写双通道 | 同时关闭读写,等价于先 SHUT_RD 再 SHUT_WR,发送 FIN |
COLSE_WAIT状态
server不关闭fd,client断开后就会一直处于CLOSE_WAIT状态,导致文件描述符一直被占用,内存泄漏等问题。
TIME_WAIT状态
TIME_WAIT 是TCP 主动关闭连接的一方,在发送完第四次挥手的 ACK 报文后,进入的一种等待状态
核心作用
① 保证第四次挥手的 ACK 能被对方收到
- 主动方发完 ACK 后不直接关闭,等待 2MSL(MSL:单个报文在网络中能存活的最大时间)
- 若服务端没收到 ACK,会超时重传 FIN
- 主动方在 TIME_WAIT 期间收到重传的 FIN,会重新发送 ACK
- 确保服务端能顺利从 LAST_ACK → CLOSED,释放资源
② 防止旧连接的延迟报文干扰新连接:网络中可能存在延迟、迷路的旧报文,如果主动方立刻关闭并新建相同四元组(源 IP + 端口、目的 IP + 端口)的连接,旧报文可能会被新连接错误接收,导致数据错乱
TIME_WAIT 期间不能重新bind相同端口,可以使用setsockopt()函数允许地址复用
4.滑动窗口
1.滑动窗口是什么
- 窗口大小指的是⽆需等待确认应答而可以继续发送数据的最大值.上图的窗口大小就是4000个字节(四个段)
- 发送前四个段的时候,不需要等待任何ACK,直接发送;
- 收到第⼀个ACK后,滑动窗口向后移动,继续发送第五个段的数据;依次类推;
- 操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答;只有确认应答过的数据,才能从缓冲区删掉;
- 窗口越⼤,则⽹络的吞吐率就越高;
滑动窗口把发送缓冲区分为了三部分:
a.已发送且被确认的部分(窗口外左侧)
b.可以直接发送的部分,包含已发送但未被确认的部分(窗口内)
c.不能发送的部分(窗口外右侧)
那么滑动窗口的大小该如何决定呢?
窗口大小 = min (拥塞窗口cwnd,接收窗口rwnd);
接收窗口rwnd大小为tcp报文中的窗口大小字段,下面介绍拥塞窗口cwnd
2.拥塞控制
拥塞窗口(cwnd)
发送方自己维护,代表网络能承受的最大发送量,单位通常是 MSS(一个报文最大数据量)。
慢启动门限 ssthresh
阈值,用来区分「慢启动阶段」和「拥塞避免阶段」
1. 慢启动
- 刚建立 TCP 连接时,不知道网络好坏,cwnd 初始很小(一般从 1 个 MSS 开始)。
- 规则:每收到 1 个 ACK,cwnd 翻倍(指数增长:1→2→4→8→16…)
- 目的:快速试探网络能承受多大流量,不一开始就猛发。
- 退出条件:cwnd 增长到 ≥ ssthresh,切换到拥塞避免。
2. 拥塞避免
- 不再指数暴涨,改为线性缓慢增长。
- 规则:每经过一个往返时间 RTT,cwnd + 1
- 目的:慢慢逼近网络极限,避免突然拥塞。
| 触发场景 | ssthresh 变化 | cwnd 变化 | 后续阶段 | 拥塞判定等级 |
|---|---|---|---|---|
| 超时重传 | 当前 cwnd/2 | 直接重置为初始值(1/10 个 MSS) | 重新进入慢启动 | 严重拥塞(链路大概率不通 / 大面积丢包) |
| 3 次重复 ACK 快速重传 | 当前cwnd/2 | 先设为新的 ssthresh,后续线性增长 | 进入快速恢复→拥塞避免 | 轻度拥塞(仅个别报文丢包,链路通畅) |
