一、TCP 报文段结构
复制代码
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 源端口号(16bit) | 目的端口号(16bit) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 序列号 Seq (32bit) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 确认号 Ack (32bit) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 首部长度(4) | 保留(6) |U|A|P|R|S|F| 窗口大小(16bit) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 校验和(16bit) | 紧急指针(16bit) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 选项字段(最多40字节) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 数 据 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
各字段详解:
| 字段 |
大小 |
说明 |
| 源/目的端口号 |
各16bit |
标识发送/接收方的应用程序 |
| 序列号(Seq) |
32bit |
本报文段第一个字节的序号,用于数据排序和重传 |
| 确认号(Ack) |
32bit |
期望收到的下一个字节序号(Ack=N 表示 N-1 之前全部收到) |
| 首部长度 |
4bit |
TCP 头部长度(单位:4字节),最小20字节,最大60字节 |
| URG |
1bit |
紧急位,URG=1 时紧急指针有效 |
| ACK |
1bit |
确认位,ACK=1 时确认号字段有效(握手后所有报文 ACK=1) |
| PSH |
1bit |
推送位,接收方应尽快将数据交给应用层 |
| RST |
1bit |
复位位,RST=1 表示连接出现严重错误,需重新建立 |
| SYN |
1bit |
同步位,SYN=1 用于建立连接(三次握手) |
| FIN |
1bit |
终止位,FIN=1 表示发送方数据发完,请求释放连接 |
| 窗口大小 |
16bit |
接收方告知发送方自己的接收缓冲区剩余空间(流量控制核心) |
| 校验和 |
16bit |
覆盖整个 TCP 报文段(含伪首部)的校验 |
| 紧急指针 |
16bit |
指出紧急数据的末尾位置(URG=1 时有效) |
二、TCP 三次握手详解
2.1 握手过程
复制代码
客户端(CLOSED) 服务端(LISTEN)
│ │
│────── SYN=1, Seq=x ──────────────→│ 服务端:SYN_RCVD
│ 第一次握手 │
│ │
│←──── SYN=1, ACK=1, Seq=y, Ack=x+1│ 客户端:ESTABLISHED
│ 第二次握手 │
│ │
│────── ACK=1, Seq=x+1, Ack=y+1 ───→│ 服务端:ESTABLISHED
│ 第三次握手 │
│ │
三次握手各阶段说明:
| 次数 |
发送方 |
关键标志位 |
含义 |
| 第一次 |
客户端 → 服务端 |
SYN=1, Seq=x |
客户端请求建立连接,发送初始序列号 x |
| 第二次 |
服务端 → 客户端 |
SYN=1, ACK=1, Seq=y, Ack=x+1 |
服务端同意连接,并发送自己的初始序列号 y,确认已收到 x |
| 第三次 |
客户端 → 服务端 |
ACK=1, Seq=x+1, Ack=y+1 |
客户端确认服务端的序列号 y |
2.2 为什么是三次而不是两次?
两次握手无法确认"客户端能接收服务端数据"这一能力
| 三次握手验证目标 |
说明 |
| 客户端发送能力 |
第一次握手(服务端收到 SYN)✅ |
| 服务端接收能力 |
第一次握手(服务端收到 SYN)✅ |
| 服务端发送能力 |
第二次握手(客户端收到 SYN-ACK)✅ |
| 客户端接收能力 |
第二次握手(客户端收到 SYN-ACK)✅ |
| 客户端确认服务端序列号 |
第三次握手才能验证 ✅ |
三、TCP 四次挥手详解
3.1 挥手过程
复制代码
主动关闭方(A) 被动关闭方(B)
│ │
│────── FIN=1, Seq=u ──────────────→│ B:CLOSE_WAIT
│ 第一次挥手(A不再发数据) │
│ │
│←───── ACK=1, Ack=u+1 ─────────────│ A:FIN_WAIT_2
│ 第二次挥手(B确认,可能继续发数据)
│ │
│ B 处理完剩余数据... │
│ │
│←───── FIN=1, ACK=1, Seq=v ────────│ B:LAST_ACK
│ 第三次挥手(B也不再发数据) │
│ │
│────── ACK=1, Ack=v+1 ────────────→│ B:CLOSED
│ 第四次挥手 │
│(A 等待 2MSL 后进入 CLOSED) │
3.2 TIME_WAIT 状态
| 属性 |
说明 |
| 出现位置 |
主动关闭方(A)在发送最后一个 ACK 后进入 TIME_WAIT |
| 等待时间 |
2MSL(MSL = 最大报文生存时间,一般 60秒,故等待 120秒) |
| 为什么等 |
1. 确保最后的 ACK 送达对端 (若丢失,B 会重发 FIN,A 可重发 ACK)2. 让本连接所有报文在网络中自然消亡,避免影响下一个同端口连接 |
| 实际影响 |
服务端重启后端口处于 TIME_WAIT 无法立即绑定 → 用 SO_REUSEADDR 解决 |
3.3 为什么挥手需要四次?
TCP 是全双工的,每个方向的数据流独立关闭。 A 说"我说完了",B 回答"好的",但 B 可能还有数据要发,等 B 也说完后再发 FIN,A 最后确认。第二次和第三次挥手不能合并(B 可能需要时间发完剩余数据)。
四、TCP 状态机
4.1 TCP 完整状态转换
| 状态 |
说明 |
出现在 |
| CLOSED |
初始/关闭状态 |
两端 |
| LISTEN |
监听中,等待连接请求 |
服务端(bind+listen后) |
| SYN_SENT |
已发送 SYN,等待对方确认 |
客户端(connect后) |
| SYN_RCVD |
已收到 SYN 并回复 SYN-ACK |
服务端(收到第一次握手后) |
| ESTABLISHED |
连接已建立,可以通信 |
两端(握手完成后) |
| FIN_WAIT_1 |
已发送 FIN,等待对方确认 |
主动关闭方 |
| FIN_WAIT_2 |
收到对方 ACK,等待对方 FIN |
主动关闭方 |
| TIME_WAIT |
等待 2MSL,确保对方收到 ACK |
主动关闭方(最后阶段) |
| CLOSE_WAIT |
收到 FIN,等待应用层关闭 |
被动关闭方 |
| LAST_ACK |
已发 FIN,等待最后的 ACK |
被动关闭方 |
| CLOSING |
双方同时关闭(极少见) |
两端 |
📌 netstat -an 可查看当前系统所有 TCP 连接状态
五、TCP 可靠传输机制
5.1 确认与重传
| 机制 |
说明 |
| 累积确认 |
Ack=N 表示 N 之前的所有数据已收到,不对每个包单独确认 |
| 超时重传 |
发送方启动定时器,超时未收到 ACK 则重发(RTO 动态计算) |
| 快速重传 |
收到 3 个重复 ACK 时立即重传,无需等待超时 |
5.2 流量控制 ------ 滑动窗口
复制代码
发送方 接收方
│ │
│ 发送窗口大小 = 接收方通告的窗口 │
│ │
已确认 | 已发待确认 | 可发未发 | 不可发
←─────────── 发送窗口 ───────────→
接收方缓冲区满 → 通告窗口=0 → 发送方停发 → 零窗口探测
- 核心:发送方不能发超过接收方缓冲区剩余空间的数据
窗口大小 字段(16bit)= 接收方当前可用缓冲区大小
5.3 拥塞控制
| 阶段 |
算法 |
说明 |
| 慢开始 |
指数增长 |
拥塞窗口从1开始,每个 RTT 翻倍 |
| 拥塞避免 |
线性增长 |
窗口超过阈值(ssthresh)后,每个 RTT +1 |
| 快速重传 |
立即重传 |
收到3个重复ACK,不等超时 |
| 快速恢复 |
减半恢复 |
快速重传后,ssthresh减半,从新ssthresh开始线性增长 |
六、IP 协议与路由
6.1 IP 协议核心特性
| 特性 |
说明 |
| 无连接 |
每个数据包独立路由,不需预先建立路径 |
| 不可靠 |
尽力而为投递,不保证到达、顺序、不重复 |
| 分片与重组 |
数据包超过链路 MTU 时分片,目的端重组 |
| TTL 生存时间 |
每经过一个路由器 TTL-1,TTL=0 时丢弃并回 ICMP Time Exceeded |
6.2 路由选择
复制代码
数据包到达路由器后的查表步骤:
1. 用目的 IP 与每条路由的子网掩码做 AND 运算
2. 结果与路由表中的网络地址比较
3. 找到匹配的路由 → 转发到对应接口/下一跳
4. 没有匹配 → 转发到默认路由(0.0.0.0/0)
5. 连默认路由都没有 → 丢弃,回 ICMP Destination Unreachable
6.3 ICMP 协议
| ICMP 报文类型 |
Type/Code |
说明 |
触发场景 |
| Echo Request/Reply |
8/0 和 0/0 |
ping 命令使用 |
检测网络连通性 |
| Destination Unreachable |
3/x |
目标不可达 |
端口未开放、路由不存在 |
| Time Exceeded |
11/0 |
TTL 超时 |
traceroute 使用此原理 |
| Redirect |
5/x |
路由重定向 |
通知主机更优路由 |
七、ARP 协议详解
7.1 ARP 工作原理
ARP(Address Resolution Protocol):在已知 IP 地址的情况下,获取对应的 MAC 地址
复制代码
主机A(知道目标IP,不知道目标MAC)
│
├── 查 ARP 缓存表(arp -a 查看)
│ │
│ 命中 ──→ 直接使用缓存的 MAC
│ │
│ 未命中
│ │
│ 发 ARP 广播请求
│ "谁的 IP 是 192.168.1.2?把你的 MAC 告诉我"
│ 目的 MAC = FF:FF:FF:FF:FF:FF(广播)
│ │
│ 目标主机B 单播回复
│ "我是 192.168.1.2,我的 MAC 是 00:1A:2B:3C:4D:5E"
│ │
│ 主机A 更新 ARP 缓存,完成通信
7.2 ARP 报文格式
| 字段 |
大小 |
说明 |
| 硬件类型 |
2字节 |
以太网 = 1 |
| 协议类型 |
2字节 |
IPv4 = 0x0800 |
| 硬件地址长度 |
1字节 |
MAC 地址 = 6 |
| 协议地址长度 |
1字节 |
IPv4 = 4 |
| 操作码 |
2字节 |
1=ARP请求,2=ARP回复;3=RARP请求,4=RARP回复 |
| 发送方 MAC |
6字节 |
发送者的硬件地址 |
| 发送方 IP |
4字节 |
发送者的 IP 地址 |
| 目标 MAC |
6字节 |
ARP 请求时全为0(未知) |
| 目标 IP |
4字节 |
要查询的目标 IP 地址 |
八、TFTP 协议概述
8.1 什么是 TFTP?
| 属性 |
内容 |
| 全称 |
Trivial File Transfer Protocol(简单文件传输协议) |
| 传输层 |
基于 UDP (端口 69) |
| 特点 |
简单轻量,无需认证,无目录浏览功能 |
| RFC |
RFC 1350 |
| 应用场景 |
网络设备固件升级、PXE 无盘启动、嵌入式系统引导 |
8.2 TFTP vs FTP 对比
| 对比项 |
TFTP |
FTP |
| 传输层协议 |
UDP(端口 69) |
TCP(端口 21/20) |
| 认证 |
无认证 |
用户名+密码 |
| 目录操作 |
不支持 |
支持(ls/cd 等) |
| 可靠性 |
自实现 stop-and-wait ARQ |
依赖 TCP |
| 文件大小 |
理论上不限(实际受 ACK 号限制,原始版本 ≤32MB) |
无限制 |
| 适用场景 |
网络启动、固件升级、嵌入式 |
通用文件传输 |
九、TFTP 报文格式
9.1 操作码(OpCode)
| OpCode |
值 |
报文类型 |
方向 |
| RRQ |
1 |
读请求(Read Request) |
客户端 → 服务端 |
| WRQ |
2 |
写请求(Write Request) |
客户端 → 服务端 |
| DATA |
3 |
数据报文 |
服务端↔客户端 |
| ACK |
4 |
确认报文 |
服务端↔客户端 |
| ERROR |
5 |
错误报文 |
服务端↔客户端 |
9.2 各类报文格式
① RRQ / WRQ 请求报文
复制代码
+--------+----------+---+----------+---+
| OpCode | Filename | 0 | Mode | 0 |
| 2字节 | 字符串 | 1 | 字符串 | 1 |
+--------+----------+---+----------+---+
| 字段 |
说明 |
| OpCode |
1=RRQ(读),2=WRQ(写) |
| Filename |
文件名(以 \0 结尾的字符串) |
| Mode |
传输模式:"octet"(二进制,最常用)或 "netascii"(文本) |
② DATA 数据报文
复制代码
+--------+---------+---------+
| OpCode | Block# | Data |
| 0x03 | 2字节 | 1~512字节|
+--------+---------+---------+
| 字段 |
说明 |
| OpCode |
3 |
| Block# |
块编号,从 1 开始,每次 +1,用于丢包重传和去重 |
| Data |
数据内容,固定大小 512 字节 ;若小于 512 字节,表示是最后一块(传输结束标志) |
⚠️ 判断文件传输结束:收到 Data 数据长度 < 512 字节(或为 0),说明是最后一块
③ ACK 确认报文
复制代码
+--------+---------+
| OpCode | Block# |
| 0x04 | 2字节 |
+--------+---------+
| 字段 |
说明 |
| OpCode |
4 |
| Block# |
与对应 DATA 报文的 Block# 一致;WRQ 请求的确认 ACK 中 Block#=0 |
④ ERROR 错误报文
复制代码
+--------+----------+-----------+---+
| OpCode | ErrorCode | ErrMsg | 0 |
| 0x05 | 2字节 | 字符串 | 1 |
+--------+----------+-----------+---+
错误码含义:
| ErrorCode |
含义 |
| 0 |
未定义错误 |
| 1 |
文件未找到(File not found) |
| 2 |
访问违规(Access violation) |
| 3 |
磁盘满或超出分配空间 |
| 4 |
非法操作 |
| 5 |
未知的传输 ID |
| 6 |
文件已存在(File already exists) |
| 7 |
无此用户 |
十、TFTP 传输流程
10.1 读文件流程(RRQ)
复制代码
客户端(随机端口) 服务端(初始 69 端口)
│ │
│──── RRQ(文件名、模式)────────→│ 端口 69
│ │(服务端换随机端口 TID)
│←─── DATA Block#1(512字节)────│ 随机端口 TID
│──── ACK Block#1 ─────────────→│
│ │
│←─── DATA Block#2(512字节)────│
│──── ACK Block#2 ─────────────→│
│ │
│←─── DATA Block#n(< 512字节)──│ ← 最后一块(标志结束)
│──── ACK Block#n ─────────────→│
│ │ 传输完成
⚠️ 服务端收到 RRQ 后,会换一个随机端口(TID,Transfer Identifier)与客户端通信,不再使用 69 端口!
10.2 写文件流程(WRQ)
复制代码
客户端(随机端口) 服务端(初始 69 端口)
│ │
│──── WRQ(文件名、模式)────────→│ 端口 69
│ │
│←─── ACK Block#0 ──────────────│ 随机端口 TID(Block#0 表示可以发送)
│──── DATA Block#1(512字节)────→│
│←─── ACK Block#1 ─────────────│
│ │
│──── DATA Block#n(< 512字节)──→│ ← 最后一块
│←─── ACK Block#n ─────────────│
│ │ 传输完成
10.3 超时与重传机制
TFTP 基于 stop-and-wait ARQ(停止等待协议),每次只发一个块,收到 ACK 再发下一块
| 机制 |
说明 |
| 超时重传 |
发送方等待 ACK,超时(通常 5 秒)未收到则重传上一个数据包 |
| 去重 |
接收方通过 Block# 检测重复包,重复 DATA 则重发对应 ACK,但不重复处理 |
| Sorcerer's Apprentice Bug |
若 ACK 延迟导致发送方重传,接收方回 ACK,触发级联重传 → 现代实现对最后一个 ACK 加延迟解决 |
十一、TFTP 编程实现
11.1 TFTP 客户端下载文件(读请求)
c
复制
编译: gcc -o tftp_client tftp_client.c 运行: ./tftp_client 192.168.1.100 test.txt ./test.txt
11.2 关键报文构造速查
c
复制
十二、总结速记卡
| 知识点 |
核心记忆 |
| TCP 报文头 |
最小 20 字节;SYN/ACK/FIN/RST 是最重要的标志位 |
| 三次握手 |
SYN → SYN+ACK → ACK;验证双方收发能力 |
| 四次挥手 |
FIN → ACK → FIN → ACK;全双工各自独立关闭 |
| TIME_WAIT |
主动关闭方等待 2MSL(约120秒) ;SO_REUSEADDR 解决端口占用 |
| Ack 确认号 |
Ack=N 表示 N-1 之前全部收到,期望收到 N |
| 窗口控制 |
窗口=0 → 停止发送 → 零窗口探测 |
| 拥塞控制 |
慢开始(指数)→ 拥塞避免(线性)→ 快重传+快恢复 |
| TTL |
每过一个路由器 -1,=0 丢弃并返回 ICMP 超时 |
| ARP |
IP→MAC;广播请求,单播回复;有缓存表 |
| TFTP 端口 |
服务端初始监听 69/UDP;收到请求后换随机端口(TID)通信 |
| TFTP 操作码 |
1=RRQ, 2=WRQ, 3=DATA, 4=ACK, 5=ERROR |
| TFTP 块大小 |
数据块固定 512 字节,最后一块 < 512(结束标志) |
| TFTP 可靠性 |
stop-and-wait ARQ:发一块→等ACK→发下一块 |
| TFTP vs FTP |
TFTP=UDP+无认证+简单;FTP=TCP+认证+目录操作 |