前言(认识TCP/UDP报文格式)

| 字段名称 | 位数 | 说明 |
|---|---|---|
| 源端口(Source Port) | 16 位 | 发送方端口号 |
| 目的端口(Destination Port) | 16 位 | 接收方端口号 |
| UDP 长度(Length) | 16 位 | UDP 头 + 数据总长度 |
| 校验和(Checksum) | 16 位 | 检测错误 |
| 数据(Data) | 可变 | 实际传输内容 |

| 字段名称 | 位数 | 说明 |
|---|---|---|
| 源端口(Source Port) | 16 位 | 发送方端口号 |
| 目的端口(Destination Port) | 16 位 | 接收方端口号 |
| 序列号(Sequence Number) | 32 位 | 用于分片排序、可靠传输 |
| 确认号(Acknowledgment Number) | 32 位 | 用于确认收到的数据 |
| 首部长度(Header Length) | 4 位 | TCP 头部长度 |
| 保留字段(Reserved) | 6 位 | 保留未用 |
| 标志位(Flags) | 6 位 | URG、ACK、PSH、RST、SYN、FIN |
| 窗口大小(Window Size) | 16 位 | 流量控制 |
| 校验和(Checksum) | 16 位 | 错误检测 |
| 紧急指针(Urgent Pointer) | 16 位 | URG 数据偏移 |
| 选项(Options) | 可变 | 可选字段,如 MSS、窗口扩大等 |
| 数据(Data) | 可变 | 实际传输内容 |
1.TCP/IP基础概念
1.1TCP是什么?
TCP(Transmission Control Protocol,传输控制协议)是一种 面向连接、可靠、有序 的传输协议。
✅ 核心特点:
面向连接:通信前必须先"握手"建立连接(三次握手),结束后要"挥手"断开(四次挥手)。
可靠传输:确保数据不丢失、不重复、按顺序到达。如果丢包,会自动重传。
流量控制 & 拥塞控制:防止发送太快导致接收方或网络崩溃。
字节流服务:应用层发的数据被看作连续的字节流,没有消息边界。
📌 举个例子:
当你用浏览器打开一个网页(比如百度),背后就是通过 TCP 下载网页内容------必须保证每一张图片、每一行文字都完整无误地传到你电脑上。
💡 常见用途:
网页浏览(HTTP/HTTPS)
电子邮件(SMTP、IMAP)
文件传输(FTP)
数据库连接
✅ 适合对"准确性"要求高的场景。
1.2TCP的报文格式及说明
1.3UDP 是什么?
UDP(User Datagram Protocol,用户数据报协议)是一种 无连接、不可靠、高效 的传输协议。
✅ 核心特点:
无连接:不需要建立连接,直接发送数据。
不可靠:不保证数据一定到达,也不重传、不排序。
低延迟、高效率:头部只有 8 字节,处理快,适合实时通信。
保留消息边界:每次发送就是一个独立的数据包("数据报")。
📌 举个例子:
你在玩《王者荣耀》时,你的操作(比如移动、释放技能)通过 UDP 发送给服务器。即使偶尔丢掉一个操作包,游戏也能继续,但若用 TCP 等待重传,就会明显卡顿。
💡 常见用途:
视频会议(Zoom、腾讯会议)
在线游戏
直播(如抖音直播、B站直播)
DNS 域名查询
物联网(IoT)设备通信
✅ 适合对"速度"和"实时性"要求高、能容忍少量丢包的场景。
1.4UDP的报文格式及说明
2.TCP和UDP的核心区别
| 对比项 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接(建立连接后传输) | 无连接(直接发包) |
| 可靠性 | 可靠传输:确认应答、重传机制 | 不可靠传输:不保证送达 |
| 顺序性 | 保证数据包按顺序到达 | 不保证顺序 |
| 传输速度 | 相对较慢(处理机制多) | 非常快(协议更轻量) |
| 传输模式 | 字节流(Stream) | 数据报(Datagram) |
| 头部开销 | 较大(至少 20 字节) | 很小(8 字节) |
| 流量控制 | 支持(滑动窗口) | 不支持 |
| 拥塞控制 | 支持(慢启动、拥塞避免等) | 不支持 |
| 错误检测 | 有校验+重传 | 仅校验,无重传 |
| 适用场景 | 文件传输、HTTP、邮件、SSH 等可靠性高的应用 | 视频直播、语音通话、DNS、实时游戏等实时性要求高的应用 |
| 是否有握手/挥手 | 有:三次握手、四次挥手 | 无:直接发送 |
| 数据边界 | 无边界(连续字节流) | 有边界(按数据包发送) |
3. TCP 的关键机制(重点)
3.1三次握手(Three-Way Handshake)
🔁 握手过程
TCP 是面向连接的协议,通信前必须建立连接。三次握手确保双方都能正常收发数据。
| 步骤 | 发送方 → 接收方 | 标志位 | 说明 |
|---|---|---|---|
| 1 | 客户端 → 服务端 | SYN=1, seq=x | 客户端发起连接请求,随机初始序号 x |
| 2 | 服务端 → 客户端 | SYN=1, ACK=1, seq=y, ack=x+1 | 服务端确认客户端请求,并发送自己的初始序号 y |
| 3 | 客户端 → 服务端 | ACK=1, seq=x+1, ack=y+1 | 客户端确认服务端的 SYN,连接建立完成 |
✅ 此后双方进入 ESTABLISHED 状态,可正常传输数据。
❓ 为什么需要"三次"?两次行不行?
核心原因:防止历史重复连接请求造成资源浪费或错误连接。
假设只有两次握手:
- 客户端发送 SYN,但因网络延迟很久才到达服务端;
- 此时客户端早已超时重发或放弃;
- 服务端收到"过期"的 SYN,误以为是新连接,直接分配资源并回复 ACK;
- 但客户端不会响应(因为它没发起新连接),导致服务端白白占用资源。
三次握手解决了这个问题:
- 第三次 ACK 由客户端发出,表明它确实想建立连接;
- 如果是旧 SYN,客户端不会发第三次 ACK,服务端就不会真正建立连接。
✅ 三次握手能双向确认双方的发送和接收能力都正常,且避免历史连接干扰。
3.2四次挥手(Four-Way Wavehand / Connection Termination)
TCP 连接是全双工的(双方可同时收发),所以关闭需要各自独立关闭方向。
🔁 挥手过程
| 步骤 | 发送方 → 接收方 | 标志位 | 说明 |
|---|---|---|---|
| 1 | 主动关闭方 → 被动方 | FIN=1, seq=u | 主动方(如客户端)数据发送完,请求关闭 |
| 2 | 被动方 → 主动方 | ACK=1, seq=v, ack=u+1 | 被动方确认 FIN,但可能还有数据要发 |
| 3 | 被动方 → 主动方 | FIN=1, seq=w, ack=u+1 | 被动方数据也发完,请求关闭 |
| 4 | 主动方 → 被动方 | ACK=1, seq=u+1, ack=w+1 | 主动方确认,连接完全关闭 |
📌 注意:步骤 2 和 3 不能合并(除非被动方恰好没有数据要发),所以通常是四次。
⏳ 为什么有 TIME_WAIT 状态?
主动关闭方在发送最后一个 ACK 后,会进入 TIME_WAIT 状态,持续 2×MSL(Maximum Segment Lifetime,通常 30~120 秒)。
目的有两个:
- 确保最后一个 ACK 能到达对方
- 如果 ACK 丢失,被动方会重发 FIN;
- TIME_WAIT 期间可再次响应 ACK,避免对方无法关闭。
- 防止旧连接的数据包干扰新连接
- 网络中可能还有旧连接的"残余数据包";
- 等待 2×MSL(足够让所有旧包过期),再允许相同四元组(IP+端口)建立新连接。
⚠️ 大量短连接可能导致服务器 TIME_WAIT 过多,占用端口资源(可通过 SO_REUSEADDR 优化)。
3.3滑动窗口(Sliding Window)------ 实现流量控制
🎯 目标:防止发送方发得太快,导致接收方缓冲区溢出。
🔧 工作原理:
- 接收方在每个 ACK 报文中携带 窗口大小(Window Size) 字段,告诉发送方"我还能接收多少字节"。
- 发送方根据这个窗口动态调整可发送的数据量。
- 窗口"滑动":随着数据被确认,窗口向前移动,释放新空间。
示例:
- 接收方缓冲区 4KB,已用 1KB → 告诉发送方窗口 = 3KB;
- 发送方最多发 3KB 数据,直到收到新的 ACK 更新窗口。
✅ 滑动窗口实现了端到端的流量控制(Flow Control),保护接收方不被压垮。
3.4拥塞控制(Congestion Control)
目标:防止网络过载(不只是接收方,而是整个路径上的路由器/链路)。
TCP 使用 四个核心算法:
1. 慢启动(Slow Start)
- 初始拥塞窗口(cwnd)很小(如 1 MSS);
- 每收到一个 ACK,cwnd += 1 MSS → 指数增长(1→2→4→8...);
- 直到达到 慢启动阈值(ssthresh),转为拥塞避免。
💡 快速探测网络容量,但避免一开始就发太多。
2. 拥塞避免(Congestion Avoidance)
- cwnd 线性增长:每 RTT 增加 1 MSS(即每个 ACK 增加 1/cwnd);
- 更温和地探测网络上限。
3. 快速重传(Fast Retransmit) - 当发送方收到 3 个重复 ACK(表示某个包丢失,后续包已到);
- 不等超时,立即重传丢失的包;
- 避免长时间等待 RTO(Retransmission Timeout)。
4.快速恢复(Fast Recovery) - 在快速重传后进入;
- 将 ssthresh 设为当前 cwnd 的一半;
- cwnd = ssthresh + 3(补偿已收到的重复 ACK 数量);
- 继续按拥塞避免方式增长,不回到慢启动。
🔄 整体策略:
慢启动 → 拥塞避免 →(丢包)→ 快速重传 + 快速恢复 → 拥塞避免
3.5超时重传(Retransmission Timeout, RTO)
🕒 触发条件:
- 发送数据后,在 RTO 时间内未收到 ACK;
- 则认为包丢失,重传该数据段。
⏱️ RTO 如何计算?
-
基于 RTT(Round-Trip Time) 动态估算;
-
使用 Jacobson 算法(考虑 RTT 变化和抖动);
公式简化版:
cpp
RTO = SRTT + 4 × RTTVAR
其中 SRTT 是平滑 RTT,RTTVAR 是 RTT 方差。
⚠️ 超时重传 vs 快速重传:
| 机制 | 触发条件 | 速度 | 效率 |
|---|---|---|---|
| 超时重传 | RTO 超时 | 慢(秒级) | 低(网络空闲) |
| 快速重传 | 收到 3 个重复 ACK | 快(毫秒级) | 高 |
✅ 现代 TCP 优先使用快速重传,超时重传是最后兜底。
3.6 TCP 连接建立(三次握手)与关闭(四次挥手)状态变化表和说明表
✅ TCP 连接建立(三次握手)与关闭(四次挥手)状态变化表
| 阶段 | 步骤 | 客户端(主动方)状态 | 含义 | 服务端(被动方)状态 | 含义 |
|---|---|---|---|---|---|
| 连接建立 | 初始 | CLOSED | 无连接 | CLOSED | 无连接 |
| (三次握手) | 1. 客户端发 SYN | SYN_SENT | 已发送 SYN,等待服务端确认 | LISTEN | 监听连接请求 |
| 2. 服务端回 SYN+ACK | SYN_SENT | 等待服务端 ACK | SYN_RECEIVED | 已收到 SYN 并回 ACK,等待客户端最终确认 | |
| 3. 客户端发 ACK | ESTABLISHED | 连接已建立,可收发数据 | ESTABLISHED | 连接已建立,可收发数据 | |
| 数据传输 | --- | ESTABLISHED | 正常通信中 | ESTABLISHED | 正常通信中 |
| 连接关闭 | 1. 客户端发 FIN | FIN_WAIT_1 | 已发送 FIN,等待 ACK | ESTABLISHED → CLOSE_WAIT | 收到 FIN,进入半关闭状态(仍可发数据) |
| (四次挥手) | 2. 服务端回 ACK | FIN_WAIT_2 | 已收到 ACK,等待服务端 FIN | CLOSE_WAIT | 应用层需调用 close() 才能发 FIN |
| 3. 服务端发 FIN | FIN_WAIT_2 | 等待服务端 FIN | LAST_ACK | 已发送 FIN,等待客户端确认 ACK | |
| 4. 客户端发 ACK | TIME_WAIT | 等待 2×MSL 后彻底关闭 | CLOSED | 连接完全关闭 | |
| 超时后 | CLOSED | 连接彻底释放 | --- | --- |
🔍 关键状态详解
| 状态 | 说明 |
|---|---|
| LISTEN | 服务端等待客户端连接请求(调用 listen() 后) |
| SYN_SENT | 客户端已发送 SYN,等待服务端响应 |
| SYN_RECEIVED | 服务端收到 SYN 并回复 SYN+ACK,等待客户端 ACK(短暂中间态) |
| ESTABLISHED | 连接已建立,双方可正常收发数据 |
| FIN_WAIT_1 | 主动关闭方已发送 FIN,等待对方 ACK |
| FIN_WAIT_2 | 主动关闭方已收到 ACK,等待对方 FIN(此时不能再发送数据,但可接收) |
| CLOSE_WAIT | 被动关闭方收到 FIN,通知应用层关闭连接,等待本地应用调用 close() |
| LAST_ACK | 被动关闭方已发送 FIN,等待最后 ACK,收到后关闭 |
| TIME_WAIT | 主动关闭方确保最后一个 ACK 被接收,并防止旧连接影响新连接(等待 2×MSL) |
| CLOSED | 无连接或连接已完全释放 |
💡 补充说明
- ** 谁先发 FIN,谁就是主动关闭方(通常是客户端,但服务端也可主动关闭)。**
- ** TIME_WAIT 只出现在主动关闭方。**
- 如果服务端主动关闭,则角色互换,状态变化对称。
- 大量 TIME_WAIT 是正常现象,但过多可能耗尽端口(可通过调整内核参数优化)。
3.7 总结图(逻辑流程)
cpp
连接建立: 三次握手
↓
数据传输: 滑动窗口(流量控制)
↓
拥塞控制(慢启动 → 拥塞避免)
↘ 丢包? → 快速重传 + 快速恢复
↘ 超时? → 超时重传 + 慢启动重启
↓
连接关闭: 四次挥手 + TIME_WAIT
4. UDP 的核心优势
| 优势 | 说明 |
|---|---|
| 头部结构简单 | 仅 8 字节 固定头部(TCP 至少 20 字节),开销极小 |
| 无连接 | 无需握手/挥手,直接发送数据,延迟极低 |
| 无重传/确认机制 | 不因丢包而阻塞或重传,避免卡顿 |
| 保留消息边界 | 每次 send() 对应一个独立数据报,应用层易处理 |
| 支持广播/多播 | 可同时向多个主机发送(TCP 仅支持单播) |
⚠️ 注意:这些"优势"本质上是牺牲可靠性换来的效率,适用于能容忍少量丢包的场景。
5. 实际应用场景对比
| 协议类型 | 常见应用场景 | 示例/说明 |
|---|---|---|
| TCP | Web 通信 | HTTP / HTTPS(网页加载、API 请求) |
| 远程登录 | SSH(安全远程管理服务器) | |
| 文件传输 | FTP(可靠传输文件) | |
| 邮件服务 | SMTP / IMAP / POP3(邮件发送与收取) | |
| 其他需要可靠性保障的通信 | 数据必须可靠、有序,不丢不重 | |
| UDP | 域名解析 | DNS(快速查询,无需建立连接) |
| 地址分配 | DHCP(广播式发现与响应) | |
| 实时视频/音频传输 | RTSP、实时直播(低延迟比可靠性更重要) | |
| 游戏通信 | FPS、MOBA(快速同步位置、状态) | |
| 其他实时性要求高的应用 | 即使丢包也要优先保证低延迟 |
6.如何选择 TCP 或 UDP
| 需求/场景 | 推荐协议 | 原因说明 |
|---|---|---|
| 需要可靠传输 | TCP | 有重传、确认、丢包恢复 |
| 需要实时性 | UDP | 无握手、无阻塞,延迟极低 |
| 需要保证消息顺序 | TCP | 内部按序号保证严格的包顺序 |
| 要极低延迟 | UDP | 不确认、不重传,发送即走 |
| 允许少量丢包 | UDP | 丢包不阻塞,适用音视频/游戏 |
| 不能接受丢包 | TCP | 保证可靠、有序 |