UDP

udp主打的就是无连接,快,简单.
在这里插入图片描述

非常的简单,udp的主要应用场景就是对效率要求高,丢包概率极低.

TCP

在这里插入图片描述

1.tcp报文结构:所有机制的基础

32位序列号:标记数据的第一个字节,解决了一系列问题
32位确认序号:这个是收到最后一个字节的序号+1,这位后面的丢包重传有极大的帮助
16位窗口大小:流量控制的核心,接收方通知它告诉发送方[我还能收多少数据]
标志位:控制连接建立,数据传输,连接断开等流程
选项:可携带窗口扩展因子,时间戳等信息,解决16位窗口大小限制等问题

2.三次握手:建立可靠连接

核心逻辑:双向同步+确认双方接收能力
1.客户端 -> 服务器:SYN
客户端发送同步报文段(SYN = 1),携带初始序号,表示我要和你建立连接
2.服务器 -> 客户端:SYN+ACK
服务器回复同步+确认报文段(SYN=1,ACK=1),携带自己的初始序号,并确认客户端的SYN(ack = 客户端ISN+1)
3.客户端 -> 服务器:ACK
客户端回复确认报文(ACK=1),确认服务器的SYN(ack = 服务器ISN+1),连接正式建立

意义:
1.投石问路,初步的验证了网络通信的流畅
2.验证通信双方发送能力和接收能力是否正常
3.3次握手过程中,还可以完成一次参数协商

3.可靠传输机制:怎么保证数据不丢,不重,不乱

1.序号机制:解决乱序和重复

1.每个字节都有唯一的序列号,接收方按序号排序顺序,保证应用层读到的是有序的流
2.接收方通过序号去重:如果收到重复序号的数据,直接丢弃,不会让应用层重复处理

2.确认应答(ACK)

1.接收方收到数据后,回复ACK报文,确认序号 = 收到的最后一个子节序号+1
2.例如:收到1–1000字节的数据,回复ack序号1001,表示1001之前的数据都收到了

3.超时重传:丢包了怎么办

1.发送方发送数据后,启动计数器,若超时没收到ack,就重传数据
2.发送方无法区分数据丢失还是ack丢了,所以两种情况都会重传,由接收方的序号机制去重

触发规则
发送方发出数据包后,启动超时计时器
设定固定超时时间,到期依旧没收到对应 ACK 应答,判定报文丢失,立刻重传
流程演示
发送方依次发送报文 1、2、3、4
报文 2 在传输途中丢失
接收方只收到 1、3、4,只会回复确认号为 2 的 ACK
发送方迟迟等不到报文 2 的确认,计时器倒计时结束
重新把报文 2 发送出去

4.快速重传:不同等超时,提前重传

当发送方连续收到三个重复的ack,就知道这个数据丢了,立刻重传,不用等超时

  1. ACK 丢失,基本不会触发快速重传
    场景:数据正常送到对方,对方回复的 ACK 报文半路丢了
    接收端已经完整收下数据,数据本身没缺失
    发送端收不到这次 ACK,但后续收发还在继续
    不会产生重复 ACK,凑不齐 3 次重复应答,不会触发快速重传
    最坏情况只是暂时等待,最终只会兜底走超时重传,不会造成数据错乱、缺失
    举例
    发 1→收方收到,回 ACK2,ACK 中途丢失
    后续正常发 2、3,收方依次正常应答
    发送端后续能收到新 ACK,就能推进窗口,旧的丢失 ACK 不会造成业务问题
  2. 真正触发快速重传:有效数据丢失
    场景:中间某个数据包没送达,后续数据包正常抵达
    接收端一直缺对应序号的数据,每收到后续包,都会返回同一个确认号的 ACK
    累计收到 3 次重复 ACK,判定数据报文丢失
    立刻从发送缓存队列取出丢失数据包补发,补齐缺失数据,保证数据完整有序
    举例
    发 1、2、3、4,报文 2 丢失
    收方收到 1、3、4,反复回复 ACK=2
    凑够 3 次相同 ACK,触发快速重传补发报文 2

这两个重传里面有一个缓冲区队列

发送缓冲区队列
发送方发出去但还没收到 ACK 的数据,都会暂存在这个队列里
没确认的报文不会直接删掉,等待应答,超时就从队列取出重发
接收缓冲区队列
接收方收到的报文,不一定按顺序抵达,先放进队列排队,按序列号重新排序,排好序再交给上层应用
一、结合队列看超时重传流程
发送方把数据按序号排入发送队列,依次发出
发出后数据保留在队列,等待 ACK 确认
超时没收到应答,从队列取出对应报文重新发送
收到 ACK 后,才把队列里已确认的数据清除

简易图示
plaintext
发送队列:[报文1][报文2][报文3][报文4]
依次往外发送
报文2丢失,一直无ACK反馈
计时器超时
从队列取出【报文2】重新发送
收到ACK确认后,移除队列对应数据

二、接收端队列:报文乱序重排序
网络传输路径不同,会出现后发先到的乱序情况
依靠 TCP 序列号,在接收队列里自动整理顺序

举个实例
正常发送顺序:1 → 2 → 3 → 4
实际到达顺序:1 → 3 → 4 → 2
接收队列暂存所有报文,根据序号规整排序
最终排序后:1、2、3、4,再向上交付
接收队列暂存:
[1] 已就位
[3][4] 先抵达排队
[2] 最后到达

按序列号重排 → 1,2,3,4
在这里插入图片描述

4.滑动窗口:高效批量的传输核心

1.发送方维护一个发送窗口,可以一次性发送窗口内的所有数据,不用等每发一个就等ack
2.每收到一个ack,窗口就向前滑动一格,发送下一批数据
3.窗口大小 = 接收方窗口大小(流量控制)和拥塞窗口大小(拥塞控制)的最小值

5.流量控制:别把接收方撑爆

核心问题:发送方发太快,接收方处理不过来怎么办?
1.接收方每次回复ack时,在窗口大小字段填入自己缓冲区的剩余空间
2.发送方根据这个值,调整自己的发送窗口的大小,不能超过接收方的处理能力
3.特殊情况:窗口大小为0 -> 发送方暂停发送,只发送窗口探测包,询问接收方缓冲区是否恢复
窗口扩展:突破64kb限制
TCP 报文里的窗口大小是 16 位,理论最大 64KB。通过「窗口扩展因子」选项,实际窗口大小 = 16 位窗口值 << 扩展因子,可支持更大的窗口。

6.拥塞控制:别把网络"堵死"

核心问题:网络中间设备(路由器)缓存满了,丢包不是接收方的锅怎么办?
在这里插入图片描述
TCP 引入「拥塞窗口(cwnd)」,控制发送方的发送速度,避免网络拥塞。

  1. 慢启动(Slow Start)
    初始拥塞窗口很小,每收到一个 ACK,窗口大小指数增长(每次 ×2)。
    目的:刚建立连接时,谨慎探测网络容量,避免一开始就发太多数据导致拥塞。
  2. 拥塞避免(Congestion Avoidance)
    当拥塞窗口超过慢启动阈值(ssthresh)后,改为线性增长(每个 RTT+1),避免窗口暴涨导致拥塞。
  3. 快速重传 + 快速恢复(Fast Retransmit + Fast Recovery)
    收到 3 个重复 ACK 时,不把窗口重置为 1(旧版本 Tahoe 的做法),而是:
    把慢启动阈值设为当前窗口的一半。
    窗口大小设为新的 ssthresh,进入快速恢复阶段,避免直接降为慢启动导致性能暴跌。

7.四次挥手

核心问题:双向连接,需要双方都确认关闭
主动关闭方 → 被动关闭方:FIN
主动方发送 FIN,表示「我没数据要发了,准备关闭」。
被动关闭方 → 主动关闭方:ACK
被动方回复 ACK,确认收到 FIN,此时主动方进入 FIN_WAIT_2 状态,被动方可以继续发送剩余数据。
被动关闭方 → 主动关闭方:FIN
被动方发送 FIN,表示「我也没数据要发了,准备关闭」。
主动关闭方 → 被动关闭方:ACK
主动方回复 ACK,确认收到 FIN,被动方进入 CLOSED 状态。
主动方进入 TIME_WAIT 状态,等待 2MSL(最长报文寿命,通常 1 分钟)后才关闭。
为什么需要 TIME_WAIT?
确保被动方收到最后一个 ACK:如果 ACK 丢了,被动方会重传 FIN,主动方还能收到并回复 ACK。
防止旧连接的报文干扰新连接:等待足够长时间,让网络中旧连接的所有报文都消失。

8.延时应答 + 捎带应答

  1. 延时应答(Delayed ACK)
    规则:收到数据后不立即发 ACK,等待一段时间(默认 500ms)再回复。
    触发条件
    收到单个报文段,且当前无待发送数据;
    等待期间若收到第二个报文,立刻合并 ACK 回复。
    目的:减少纯 ACK 报文,降低网络开销。
    缺点:可能增加单向时延,极端情况引发糊涂窗口综合征。
  2. 捎带应答(Piggybacking ACK)
    规则:把 ACK 和己方待发送的数据报文合并,随数据一起发出。
    场景:双方双向同时传输数据(全双工)。
    目的:彻底消除单独 ACK,进一步节省带宽。

工作逻辑
接收方收到数据,先开启延时应答计时器,暂不发 ACK;
若计时器到期前,本机有上行数据要发送:
→ 直接把 ACK捎带在数据报文里发出(最优组合)。
若计时器到期,仍无上行数据:
→ 单独发送延时 ACK。
若等待期间收到第二个数据段:
→ 立即回复合并 ACK,不等延时计时。

9.面向字节流

产生的粘包问题
何为粘包?
本质:多个独立应用数据包,在 TCP 缓冲区被合并 / 拆分,接收方一次读到多个包、或半个包。
分两种情况:
粘包(合并):发送方连续发多段数据,接收方一次读出多段。
拆包(分包):单个大数据包,被 TCP 拆分,接收方分多次读完。

解决办法
1.固定长度包
每个数据包大小一致,接收方按固定字节读取。
缺点:空间浪费。
2.分隔符结尾
包尾加特殊分隔符(如 \n、\r\n、自定义符号),读到分隔符即代表一个包结束。
缺点:数据内容不能包含分隔符。
3.包头 + 数据(长度字段,最常用)
包头:固定 2/4 字节,存后续数据长度
数据:真实业务内容
流程:先读长度 → 再按长度读对应字节。
优点:通用、稳定、工业首选。
4.关闭 Nagle 算法
TCP_NODELAY,小包立即发送,只能缓解,不能彻底解决。

10.异常情况

1.进程崩溃
过程(以主动崩溃端 A,对端 B 为例)
A 进程异常退出 / 被杀死,操作系统内核接管 socket 资源。
内核发现该 socket 无进程持有,主动发起正常四次挥手:
A 内核发送 FIN 报文 → 告知 B:本方不再发数据
B 收到 FIN,回复 ACK,A 进入 TIME_WAIT,B 进入 CLOSE_WAIT
B 侧应用此时仍可正常向 A 发数据(TCP 半关闭状态)。
若 B 后续调用 read() 读数据:
缓冲区无新数据 + 已收到对端 FIN → read 返回 0(TCP 标准 EOF)。
若 B 继续调用 write() 向已关闭的连接发数据:
内核会回复 RST 复位报文
B 进程触发 SIGPIPE 信号,write 函数返回 -1,连接强制断开。
关键特点
链路正常、主机正常,只是应用进程没了。
对端能明确感知连接正常关闭(read=0),属于优雅断开。
不会残留僵死连接。
常见现象:客户端进程闪退,服务端立刻读到 EOF。

2.主机掉电
假设服务器突然断电,之前客户端发送的都有ack,突然一下没有ack了
客户端触发超时重传,然后客户端单方面释放连接,触发特殊的数据包rst

假设客户端突然断电,此时发送的数据突然没了,服务器会等待,会发送"心跳包",探测对方是不是在工作,如果没有心跳了说明挂了
,如果心跳是周期性的说明在正常工作.

四种状态补充

  1. LISTEN 监听状态
    归属:服务端独有状态
    含义:服务端端口已开启,静静等待客户端发起连接请求
    触发:服务端调用listen()函数后进入该状态
    行为:不主动发包,只接收客户端 SYN 握手报文,收到后响应握手
  2. ESTABLISHED 已建立连接状态
    含义:三次握手完成,双向通信通道就绪,可以正常收发业务数据
    两端都可处于这个状态
    行为:数据读写、ACK 应答、窗口控制、拥塞控制都在此状态下执行
    断开触发:任意一方主动发 FIN 报文,开始走向关闭流程
  3. CLOSE_WAIT 关闭等待状态
    归属:被动关闭方独有
    触发流程
    主动关闭方先发 FIN 报文,表示本方不再发数据
    被动方收到 FIN,立刻回复 ACK 确认
    被动方就进入 CLOSE_WAIT
    核心特点
    此时被动方还能继续往对方发送剩余数据
    等待应用程序执行关闭套接字操作
    若程序迟迟不关闭,会堆积大量 CLOSE_WAIT 连接,造成资源占用
  4. TIME_WAIT 时间等待状态
    归属:主动断开的一方独有
    触发流程
    双方都发送 FIN、互相确认完毕
    主动方最后发出收尾 ACK 报文后,进入 TIME_WAIT
    核心规则
    固定等待 2MSL 时长
    等待结束后,才彻底变为 CLOSED 关闭状态
    两大作用
    兜底重传:防止最后 ACK 丢失,对方重发 FIN 时,仍能正常回应
    隔绝旧报文:确保网络中本条连接残留数据包全部消散,不干扰新连接
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐