【网络】6.UDP和TCP原理

目录

[UDP 和 TCP 协议原理详解](#UDP 和 TCP 协议原理详解)

[一、UDP 协议](#一、UDP 协议)

[1. 通信识别](#1. 通信识别)

[2. UDP 报头结构](#2. UDP 报头结构)

[3. 报头分离与分用](#3. 报头分离与分用)

[4. UDP 特点](#4. UDP 特点)

[5. 缓冲区](#5. 缓冲区)

[6. 报文大小限制](#6. 报文大小限制)

二、报文在内核中的管理

[1. 读取与调度](#1. 读取与调度)

[2. 报文管理结构:struct sk_buff](#2. 报文管理结构:struct sk_buff)

[三、TCP 协议](#三、TCP 协议)

[1. 交付与分离](#1. 交付与分离)

[2. TCP 报头结构](#2. TCP 报头结构)

[3. 首部长度](#3. 首部长度)

[4. 报文大小与边界](#4. 报文大小与边界)

[5. 可靠性:应答机制](#5. 可靠性:应答机制)

[6. 应答规则](#6. 应答规则)

[7. 捎带应答](#7. 捎带应答)

[8. 流量控制](#8. 流量控制)

[9. 6 个标志位](#9. 6 个标志位)

三次握手

四次挥手

PSH(推送)

RST(重置)

URG(紧急指针)

[四、TCP 核心机制](#四、TCP 核心机制)

[1. 序列号](#1. 序列号)

[2. 重传机制](#2. 重传机制)

[3. 连接管理](#3. 连接管理)

[4. 调用过程](#4. 调用过程)

[5. 为什么要三次握手?](#5. 为什么要三次握手?)

[6. 为什么要四次挥手?](#6. 为什么要四次挥手?)

[7. 文件描述符泄漏](#7. 文件描述符泄漏)

[8. TIME_WAIT 状态](#8. TIME_WAIT 状态)

五、滑动窗口控制

[1. 窗口构成](#1. 窗口构成)

[2. 丢包问题](#2. 丢包问题)

[3. 重传区分](#3. 重传区分)

[4. 流量控制](#4. 流量控制)

[5. 拥塞控制](#5. 拥塞控制)

慢启动策略

拥塞窗口

[6. 延迟应答](#6. 延迟应答)

[六、TCP 异常处理](#六、TCP 异常处理)

[1. 进程终止](#1. 进程终止)

[2. 机器关机](#2. 机器关机)

[3. 网线断开](#3. 网线断开)

七、总结

[TCP 可靠性机制](#TCP 可靠性机制)

[TCP 效率机制](#TCP 效率机制)


UDP 和 TCP 协议原理详解

一、UDP 协议

1. 通信识别

TCP/IP 协议中使用五元组来唯一标识一个通信:

  • 源端口号

  • 源 IP 地址

  • 目标端口号

  • 目标 IP 地址

  • 协议号(TCP 或 UDP)

应用场景:如果客户端有 2 个画面(如视频聊天+文件传输),不需要分开发送,可以用不同的端口号来区分不同的请求。

2. UDP 报头结构

UDP 报头固定为 8 字节,包含 4 个字段,各占 16 位(2 字节):

  • 源端口

  • 目的端口

  • UDP 长度(整个 UDP 数据报的长度,包括报头和数据)

  • UDP 校验和

端口号设计为 16 位,是为了与内核协议栈中的端口表示保持一致。

3. 报头分离与分用

  • 分离 :由于 UDP 报头是定长(8 字节),可以轻松地将报头和有效载荷分离

  • 分用:利用目标端口号,内核将数据交给对应端口的应用程序

面向数据报

  • UDP 保持消息边界,每个报文都是独立的

  • 发送端 sendto 一次,接收端 recvfrom 一次,一一对应

  • 操作系统可以直接发送结构体对象(但要考虑字节序和对齐问题)

4. UDP 特点

特点 说明
无连接 不需要建立连接,直接发送数据
不可靠 不保证数据到达,不保证顺序
面向数据报 保持消息边界,一个报文一个整体

例如:一个报文有 100 字节,UDP 会直接发送 100 字节,不会拆分成 10 次 10 字节发送。

5. 缓冲区

发送缓冲区

  • UDP 没有真正意义上的发送缓冲区

  • 数据发送完成后就丢弃,无法重传

  • 这也是 UDP 效率高、无法处理丢包的原因

接收缓冲区

  • UDP 有接收缓冲区

  • 但不保证和发送时的数据顺序一致

  • 如果缓冲区满了,新到的数据会被直接丢弃

6. 报文大小限制

UDP 报文长度字段占 16 位,因此 UDP 数据报的最大长度为 2^16 = 64 KB(包括 8 字节报头)。

二、报文在内核中的管理

1. 读取与调度

应用层正在解析报文时,操作系统可以同时从网络读取新的报文。因为:

  • 接收消息和进程调度都是由时钟中断驱动的

  • 操作系统会不断进行调度,让这两个动作交替进行

2. 报文管理结构:struct sk_buff

内核使用 sk_buff 结构体管理网络报文,包含 4 个关键指针:

复制代码
head ──> [ 以太网头 │ IP头 │ TCP头 │ 数据 ] <── end
data ────────────────────────^
tail ──────────────────────────────^
  • head:指向缓冲区的开始

  • data:指向当前协议层的有效载荷开始位置

  • tail:指向当前协议层的有效载荷结束位置

  • end:指向缓冲区的结束

封装过程(发送)

  • 每增加一层协议,data 指针向上移动,留出空间给新的报头

  • 新的协议报头被填充到 data 之前的位置

解包过程(接收)

  • 每去掉一层协议,data 指针向下移动,跳过已处理的报头

三、TCP 协议

1. 交付与分离

  • 交付:使用源端口和目标端口,将数据交给对应的应用程序

  • 分离 :TCP 报头是定长(20 字节基本报头 + 可选选项),可以分离

2. TCP 报头结构

TCP 报头基本长度 20 字节,选项部分长度可变。

关键字段

字段 长度 说明
源端口 16位 发送方端口
目的端口 16位 接收方端口
序列号 32位 数据的第一个字节的序号
确认号 32位 期望收到的下一个字节的序号
首部长度 4位 表示 TCP 报头长度(单位:4 字节)
保留位 6位 保留将来使用
标志位 6位 URG、ACK、PSH、RST、SYN、FIN
窗口大小 16位 接收方剩余缓冲区大小,用于流量控制
校验和 16位 校验整个 TCP 报文段
紧急指针 16位 当 URG=1 时有效,指向紧急数据位置
选项 可变 如 MSS、窗口扩展因子、SACK 等

3. 首部长度

首部长度字段占 4 位,最大值是 15,但单位是 4 字节 ,因此最大可表示 15 × 4 = 60 字节

  • 基本报头 20 字节

  • 选项最多 40 字节

  • 取值范围:[20, 60] 字节

4. 报文大小与边界

TCP 是面向字节流的协议:

  • 报文可能被拆分或组合

  • 没有固定的报文大小

  • 报文是否完整需要上层协议来判断

边界问题

  • 报文和下一个报头不会粘在一起 (由 sk_buff 的指针区分)

  • 但多个报文的数据可能粘在一起(TCP 粘包问题),需要上层协议(如自定义协议头)来分离

5. 可靠性:应答机制

TCP 的可靠性由**确认应答(ACK)**保障。

示例

  • 服务器发送序号:200

  • 客户端应答序号:201(表示 200 号字节已收到,期待 201)

注意

  • 在收到 201 应答之前,服务器无法确保 200 已收到

  • 应答只对历史的消息完整性负责,不对最新消息负责

6. 应答规则

基本规则

  • 确认序号 = 发送序号 + 1

  • 收到某个序号的应答,表示该序号之前的所有数据都已收到

  • 下一次发送时,使用新的确认序号作为起始

特点

  • 允许少量序号丢失

  • 若 200 没收到应答,但紧接着收到了 301 的应答,则判定 200 已收到(虽然它可能确实丢了)

  • 乱序问题:TCP 不保证数据按顺序到达,但保证按顺序交付给上层

7. 捎带应答

为什么应答要使用新编号?因为可以实现捎带应答

  • 可以在应答的同时,顺便返回自己的消息

  • 应答放在报头中,消息放在报文中,互不冲突

类比:聊天时的回答 + 反问

8. 流量控制

TCP 使用 16 位窗口大小字段实现流量控制:

  • 该字段存储自己接收缓冲区的剩余大小

  • 发送方根据这个值调整发送速度

  • 目的:让发送速度更合理(太快减速,太慢加速),提升效率,减少丢包重传

9. 6 个标志位

为什么需要标志位?因为 TCP 收到的报文有不同的类型,需要不同的处理方式。

标志 名称 作用
SYN 同步标志 连接建立时使用,表示请求同步序列号
ACK 确认标志 表示确认号字段有效
FIN 结束标志 表示发送方数据已发送完毕,请求断开连接
PSH 推送标志 催促接收方尽快将数据交给应用层
RST 重置标志 重置连接,用于异常情况
URG 紧急标志 表示紧急指针字段有效
三次握手

通信前要先确认连接:

  1. 客户端 → 服务端SYN=1,seq=x(询问)

  2. 服务端 → 客户端SYN=1, ACK=1,seq=y,ack=x+1(应答+询问)

  3. 客户端 → 服务端ACK=1,seq=x+1,ack=y+1(应答)

特点

  • 前两次握手只能发送带有标志位的报头,不能携带数据

  • 但可以携带窗口大小等选项,协商接收能力

四次挥手

断开连接:

  1. 客户端 → 服务端FIN=1(询问断开)

  2. 服务端 → 客户端ACK=1(应答,表示收到断开请求)

  3. 服务端 → 客户端FIN=1(服务端数据发完,询问断开)

  4. 客户端 → 服务端ACK=1(应答确认)

PSH(推送)

当对方缓冲区满了,带 PSH 标志的报文可以催促客户端快点读取,腾出空间。

RST(重置)

三次握手不一定成功。如何确定成功?

  • 客户端视角:收到第 2 次握手,说明自己可以发、也可以收

  • 服务端视角:收到第 3 次握手,说明自己可以收(第 1 次)、也可以发(第 3 次确认了第 2 次)

异常情况

  • 如果第 1、2 次丢了:双方都认为错误,重新来过

  • 如果第 3 次丢了:客户端认为没问题,服务端认为有问题

  • 此时客户端会无脑给服务端发消息,服务端就要发送 RST 信号告诉客户端要重来

此外,通信过程中任何问题都可以发送 RST 重置连接。

URG(紧急指针)
  • 与 16 位紧急指针一起使用

  • URG=1 时,紧急指针才有效

  • 紧急数据可以插队优先读取(如卡顿时询问原因)

  • 紧急数据大小为 1 个字节,相当于状态码

四、TCP 核心机制

1. 序列号

TCP 对每个字节都进行了编号,可以理解为一个 char 数组,数组下标就是序列号。

每个 ACK 回答都包含确认序号,告诉发送者:

  • 已经收到哪些数据

  • 下一次从哪个序号开始发送

2. 重传机制

发送方没收到应答,可能有两种情况:

  1. 数据包丢了

  2. 应答包丢了

无论哪种原因,发送方都会等待一个时间间隔,如果收不到应答,就执行超时重传

时间间隔:由 TCP 动态计算,通常是 500ms 的整数倍。

重复报文处理

  • 如果是应答丢了,接收方会收到 2 个同样的数据包

  • 接收方可以根据序列号识别并丢弃重复包

序列号的作用

  • 确认应答

  • 按序到达

  • 确认丢弃

3. 连接管理

服务器可能有多个客户端连接,每个连接状态不同,需要管理。

管理结构:struct link 或类似的结构体。

注意:建立连接是有成本的(内存、时间)。

4. 调用过程

复制代码
connect()    // 执行三次握手(操作系统内核完成)
accept()     // 不参与握手,只将已建立的连接从队列中取出
write/read() // 只对缓冲区操作

关键理解

  • 三次握手在操作系统内核中自动完成

  • 即使没有调用 accept(),连接也能建立成功(只是没有被应用程序取走)

  • 这种设计实现了解耦,支持生产者消费者模型,可以应对忙闲不均

5. 为什么要三次握手?

  1. 验证双方全双工通信能力的最小次数

    • 一次握手:只能证明客户端能发

    • 二次握手:证明服务端能收能发,但无法证明客户端能收

    • 三次握手:证明双方都能收能发

  2. 本质上是 4 次,只是服务端通常将应答和自己的 SYN 请求合并在一个报文中(捎带),优化为 3 次

  3. 最小成本确认双方通信意愿

6. 为什么要四次挥手?

四次挥手是最小成本确认双方断开请求的方式:

  • A 确认数据已发完 → B 应答收到

  • B 确认数据已发完 → A 应答收到

半关闭

  • 可能出现客户端数据发完、服务端数据没发完的情况

  • 此时只执行了前两次挥手(客户端发 FIN,服务端 ACK)

  • 客户端关闭读端,但写端不能关闭,要继续接收消息

半关闭实现 :调用 shutdown() 接口,变为只读不写的半双工状态。

3 次 vs 4 次

  • 建立连接时,服务端对客户端服务,收到连接申请一定会同时发送自己的连接申请,因此 4 次可以优化为 3 次

  • 断开连接时,服务端和客户端基本不会同时退出,较少能优化为 3 次

  • 本质上都是 4 次,只是因为服务性质不同,表现出的次数不同

7. 文件描述符泄漏

假设客户端主动退出:

  • 客户端:发出 ACK 应答完成四次挥手

  • 服务端:接收到 ACK 应答完成四次挥手

问题:两个过程不同步。客户端发出 ACK 后需要等待一段时间才能完全退出。

查看连接状态:

复制代码
netstat -natp | grep 8081
  • 客户端:FIN_WAIT2 状态

  • 服务端:CLOSE_WAIT 状态

查看文件描述符:

复制代码
ls /proc/[进程号]/fd -l

可以看到 3、4 号文件描述符仍然被占用,造成文件描述符泄漏(文件描述符数量有限)。

8. TIME_WAIT 状态

先退出的一方会进入 TIME_WAIT 状态,等待 2 个 MSL(报文最大生存时间)再完全退出。

MSL(Maximum Segment Lifetime):报文在网络中的最大存活时间,通常为 30 秒、1 分钟或 2 分钟。

为什么需要 TIME_WAIT?

考虑一个场景:

  1. 客户端报文 A 卡在网络中

  2. 客户端超时重传,发送了 A',并收到了应答

  3. 双方都认为通信正常完成

  4. 客户端要退出

  5. 如果没有等待时间,下次连接握手时,服务端可能会莫名收到 A,导致新连接混乱

等待的作用

  • 让历史残存报文在网络中自然消亡

  • 防止影响新连接

端口复用

  • 处于 TIME_WAIT 状态的端口不能立即重用

  • 紧急情况下可以调用 setsockopt(SO_REUSEADDR) 复用端口

  • 此时如果收到序号不匹配的历史报文,可以丢弃,防止危害

五、滑动窗口控制

1. 窗口构成

将发送缓冲区看作一个 char 数组:

复制代码
[ 已确认 ][ 已发送待确认 ][ 可发送 ][ 不可发送 ]
          ↑              ↑        ↑
         left           window   end
  • left 指针:区分是否收到应答,左边是已确认的,右边是待确认的

  • 窗口大小:可以发送但未确认的数据量,由对方报头的窗口大小字段决定

  • end 指针:区分能否发送,右边是还不能发送的数据

如果对方窗口大小为 0,表示对方当前无法接收数据。

2. 丢包问题

假设 1000~2000 的数据没收到应答:

情况 1:收到了 3001 应答

  • 说明只是应答丢了,数据没丢

  • left 指针可以直接移动到 3000 位置

情况 2:一直收到 1001 应答(收到 3 次)

  • 说明数据包丢了

  • 触发快重传机制

  • 重传丢失的数据,直到收到 2001 应答,left 指针右移

由于确认信息是连续的,中间丢失的报头会迟到,变为最左边的确认。

3. 重传区分

重传类型 触发条件 作用
快重传 收到 3 次相同的 ACK 提高效率,快速恢复
超时重传 长时间收不到任何 ACK 兜底机制,处理网络问题

注意:TCP 发送的数据不能立即删除,要存储在滑动窗口中,直到收到确认。实际上,发出报文后就会开始计时,准备超时重传。

4. 流量控制

  • 第一、二次握手不能带数据,第三次可以携带

  • 第一次发送时,通过前两次握手报头中的窗口大小字段,可以知道对方的接收能力

  • 窗口大小虽然只有 16 位,但可以使用窗口扩展因子选项来增大

5. 拥塞控制

问题:丢 2 个包和丢 998 个包,原因不同:

  • 丢 2 个:可能只是个别包出错

  • 丢 998 个:大概率是网络拥塞

处理原则

  • 大面积丢包 → 判定为网络拥塞

  • 不能立即重传所有丢包(会加重拥塞)

  • 对于使用相同协议的主机,拥塞时的策略应该相同

慢启动策略

发送方维护一个拥塞窗口

  • 初始阶段:发送数据量按指数增长(1, 2, 4, 8...)

  • 达到阈值(ssthresh)后:改为线性增长

  • 目的:探测网络承载能力,避免一开始就发送大量数据造成拥塞

拥塞窗口

实际发送速度由两个因素共同决定:

复制代码
实际发送窗口 = min(对方窗口,拥塞窗口)
  • 对方窗口:接收方的接收能力

  • 拥塞窗口:网络的承载能力

拥塞窗口的变化

  • 网络好时:窗口不断增大(但受限于带宽,不会无限增大)

  • 网络不好时:窗口迅速减小,然后重新慢启动探测

理解

  • 网络好时,拥塞窗口的增大像"吹牛",因为无法证明它过大,此时实际限制是对方窗口

  • 网络不好时,拥塞窗口就知道"牛吹过了",迅速压下来,此时它才对发送速度有实际影响

6. 延迟应答

问题:64KB 缓冲区接收了 20KB,立即应答的话窗口值只能报 44KB。

优化:稍等片刻,应用层可能消费了一些数据,窗口值可能变为 54KB,这样发送方可以多发一些数据,提升效率。

策略

  • 隔 N 个包应答一次(通常 2 个包)

  • 超过最大延迟时间(通常 200ms)应答一次

因此,应答次数可能少于发送次数,但由于滑动窗口的存在,不影响可靠性。

六、TCP 异常处理

1. 进程终止

和正常关闭差不多:

  • 进程退出时,文件描述符自动关闭(引用计数变为 0)

  • 触发四次挥手

2. 机器关机

  • 关机会先停止进程,因此和进程终止类似

  • 会正常执行四次挥手

3. 网线断开

  • 没有机会执行四次挥手(不可达)

  • 对方发送数据时会发现网络不可达,最终关闭连接

  • 保活机制:TCP 会定时发送探测报文,检查连接是否存活

    • 如果长时间无响应,就释放连接

七、总结

TCP 可靠性机制

机制 作用
校验和 检测数据是否损坏
序列号 按序交付、去重
确认应答 确认数据已收到
超时重传 处理丢包
连接管理 三次握手、四次挥手
流量控制 防止接收方被淹没
拥塞控制 防止网络被淹没

TCP 效率机制

机制 作用
滑动窗口 批量发送,提高吞吐量
快重传 快速恢复丢包
延迟应答 提高窗口利用率
捎带应答 减少报文数量
相关推荐
干啥都是小小白2 小时前
2.创建你的第一个FreeRTOS任务(动态与静态)
stm32·单片机
芯联智造2 小时前
【stm32简单外设篇】- 震动传感器
c语言·stm32·单片机·嵌入式硬件
Hello_Embed2 小时前
LVGL 入门(八):标签控件 lv_label
前端·笔记·stm32·单片机·嵌入式
天天爱吃肉82183 小时前
新能源汽车电机台架测试功率分析仪问题梳理(理论+实操)
功能测试·嵌入式硬件·汽车
梁洪飞3 小时前
armv7a和uboot里面的重要概念
arm开发·嵌入式硬件·arm
海阔天空任鸟飞~12 小时前
蓝汛-BT897-添加按键提示音
c语言·单片机·蓝汛
yrx02030712 小时前
stm32单线串口(空闲中断+DMA接收+阻塞式发送)模式控制舵机
stm32·单片机·嵌入式硬件·单线串口
雾削木12 小时前
STM32HAL输入捕获定时器测量PWM频率和占空比
stm32·单片机·嵌入式硬件
FreakStudio12 小时前
一行命令搞定驱动安装!MicroPython 开发有了自己的 “PyPI”包管理平台!
python·stm32·单片机·嵌入式·arm·电子diy