TCP 协议:连接管理、拥塞控制与可靠性

深入 TCP 协议的核心机制,包括三次握手、四次挥手、滑动窗口、拥塞控制算法与调优参数

TCP(Transmission Control Protocol)是互联网最核心的协议之一。它以面向连接、可靠传输、字节流、全双工的特性,支撑了绝大多数网络应用。深入理解 TCP,是成为优秀后端工程师的必经之路。

一、TCP 头部结构

1.1 报文头格式

 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data | Flags |U|A|P|R|S|F|                               |   |
| Offset| Reser |R|C|S|S|Y|I|            Window Size          |   |
|       | ved   |G|K|H|T|N|N|                               |   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options (Variable)                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             data                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Flags:URG ACK PSH RST SYN FIN
        ↓   ↓   ↓   ↓   ↓   ↓
       紧急 确认 推送 重置 同步 结束

1.2 关键字段说明

字段大小说明
Source/Dest Port各 16 bits标识发送/接收应用
Sequence Number32 bits本报文段数据的第一个字节序号
Ack Number32 bits期望收到的下一个字节序号
Window Size16 bits接收窗口大小(流量控制)
Data Offset4 bits头部长度(以 4 字节为单位)
Flags6 bits控制位(SYN、ACK、FIN 等)

二、三次握手与四次挥手

2.1 三次握手(建立连接)

客户端(状态)                      服务端(状态)
CLOSED                              LISTEN
  │                                     │
  │ ─────── SYN=1, seq=x ────────────→ │
  │  SYN_SENT                           │ SYN_RCVD
  │                                     │
  │ ←──── SYN=1, ACK=1, seq=y, ack=x+1 │
  │  ESTABLISHED                        │
  │                                     │
  │ ───── ACK=1, seq=x+1, ack=y+1 ───→ │
  │                                     │ ESTABLISHED
  │                                     │

为什么是三次而不是两次?
● 防止历史重复连接的初始化造成混乱
● 同步双方的初始序列号
● 确认双方的收发能力正常

2.2 四次挥手(关闭连接)

客户端                              服务端
ESTABLISHED                         ESTABLISHED
  │                                     │
  │ ───── FIN=1, seq=u ─────────────→ │
  │  FIN_WAIT_1                         │ CLOSE_WAIT
  │                                     │
  │ ←──── ACK=1, seq=v, ack=u+1 ──────│
  │  FIN_WAIT_2                         │
  │                                     │
  │ ←──── FIN=1, seq=w, ack=u+1 ──────│
  │  TIME_WAIT                          │ LAST_ACK
  │                                     │
  │ ───── ACK=1, seq=u+1, ack=w+1 ───→ │
  │                                     │ CLOSED
  │ (等待 2MSL 后关闭)                    │
  │  CLOSED                             │

TIME_WAIT 状态存在的意义:
● 确保最后一个 ACK 能到达对方(可重传)
● 等待 2MSL 让网络中残留的报文段全部消失
● 防止旧的连接报文段被新连接误接收

2.3 Java Socket 观察状态

// 获取 Socket 连接状态
Socket socket = new Socket("example.com", 80);
System.out.println("本地地址: " + socket.getLocalAddress());
System.out.println("本地端口: " + socket.getLocalPort());
System.out.println("远程地址: " + socket.getInetAddress());
System.out.println("远程端口: " + socket.getPort());
System.out.println("是否连接: " + socket.isConnected());
System.out.println("是否关闭: " + socket.isClosed());

// 查看系统 TCP 连接状态(Linux)
// cat /proc/net/tcp
// ss -tan | awk '{print $1}' | sort | uniq -c

三、状态机

                              +--------+
                    主动打开  | CLOSED |  被动打开
                   ┌────────→│        │←────────┐
                   │         +--------+         │
                   │              ↑             │
                   ↓              │             ↓
              +----------+   超时/关闭     +----------+
              │ SYN_SENT │←────────────→│  LISTEN  │
              +----------+              +----------+
                   │              SYN+ACK        │
              收到 SYN+ACK        收到 SYN        │
                   │──────────────────────────→│
                   ↓                                   ↓
              +──────────+              +──────────+
              │ESTABLISHED│←──────────→│SYN_RCVD  │
              │          │    三次握手完成    │          │
              +──────────+              +──────────+
                   │                            │
              收到 FIN(主动关闭)         ACK(被动关闭方确认)
                   │                            │
                   ↓                            ↓
              +──────────+              +──────────+
              │FIN_WAIT_1│              │CLOSE_WAIT│
              +──────────+              +──────────+
                   │                            │
              收到 ACK                           │
                   │                      应用层 close()
                   ↓                            ↓
              +──────────+              +──────────+
              │FIN_WAIT_2│              │ LAST_ACK │
              +──────────+              +──────────+
                   │收到 FIN                     │收到 ACK
                   │                            │
                   ↓                            ↓
              +──────────+              +────────+
              │ TIME_WAIT│              │ CLOSED |
              +──────────+              +────────+
                   │
              2MSL 超时
                   │
                   ↓
              +--------+
              │ CLOSED |
              +--------+

四、可靠传输机制

4.1 序列号与确认应答

发送方                              接收方
  │ ── seq=1, data="Hel" ─────────→ │
  │ ── seq=4, data="lo " ─────────→ │
  │ ── seq=8, data="Wor" ─────────→ │
  │                                   │
  │ ←──── ack=12 ────────────────── │  期望收到 seq=12
  │                                   │
  │ ── seq=12, data="ld!" ────────→ │

4.2 超时重传(RTO)

发送方                              接收方
  │ ── seq=1, data="A" ───────────→ │
  │        [等待 RTO 超时]            │
  │        [未收到 ACK]               │
  │ ── seq=1, data="A" ───────────→ │  重传
  │                                   │
  │ ←──── ack=2 ─────────────────── │

RTO 计算(Jacobson / Karn 算法):
SRTT = α * SRTT + (1-α) * RTT_sample    // 平滑 RTT
RTTVAR = β * RTTVAR + (1-β) * |SRTT - RTT_sample|
RTO = SRTT + 4 * RTTVAR

4.3 快速重传

发送方                              接收方
  │ ── seq=1 ─────────────────────→ │
  │ ── seq=2(丢失)────────────────→ │
  │ ── seq=3 ─────────────────────→ │
  │ ── seq=4 ─────────────────────→ │
  │                                   │
  │ ←──── ack=2 ─────────────────── │  (期望 seq=2)
  │ ── seq=5 ─────────────────────→ │
  │                                   │
  │ ←──── ack=2 ─────────────────── │  (重复 ACK #1)
  │ ── seq=6 ─────────────────────→ │
  │                                   │
  │ ←──── ack=2 ─────────────────── │  (重复 ACK #2)
  │                                   │
  │ ── seq=2 ─────────────────────→ │  (快速重传,不等 RTO)
  │                                   │
  │ ←──── ack=7 ─────────────────── │  (累积确认)

五、流量控制:滑动窗口

5.1 原理

发送方缓冲区:

| 已发送已确认 |  已发送未确认  |  可发送未发送  |  不可发送  |
|    <-1     |    2-5        |    6-9       |   10+     │
└────────────┴───────────────┴──────────────┴───────────┘
             ↑               ↑               ↑
           SND.UNA         SND.NXT         SND.UNA + SND.WND

接收方缓冲区:

|  已接收已确认  |    期望接收    |  未准备接收  |
|    <-1       │    2-6       │    7+     │
└──────────────┴──────────────┴───────────┘
               ↑              ↑
             RCV.NXT      RCV.NXT + RCV.WND

5.2 零窗口与窗口探测

接收方缓冲区满时,发送 Window=0 的 ACK:

发送方                              接收方
  │ ── seq=100, data ─────────────→ │
  │                                   │
  │ ←──── ack=101, win=0 ────────── │  缓冲区满!
  │                                   │
  │ ── seq=101, 1 byte (探测) ─────→ │  窗口探测
  │                                   │
  │ ←──── ack=101, win=0 ────────── │  仍然满
  │                                   │
  │ ... 持续探测 ...                  │
  │                                   │
  │ ←──── ack=101, win=4096 ─────── │  有空间了!
  │                                   │
  │ ── seq=101, data ─────────────→ │  恢复发送

六、拥塞控制

6.1 四个核心算法

算法阶段机制
慢启动连接初始拥塞窗口(cwnd)指数增长
拥塞避免慢启动阈值后cwnd 线性增长
快速重传收到 3 个 dup ACK立即重传,不等待 RTO
快速恢复快速重传后cwnd 减半 + 3,避免慢启动

6.2 状态变化图

连接建立
    │
    ▼
┌─────────┐    cwnd < ssthresh    ┌─────────┐
│ 慢启动   │ ◄─────────────────────│ 快速恢复 │
│(指数增长)│                       │         │
└────┬────┘                       └────┬────┘
     │ cwnd >= ssthresh                │ 拥塞事件
     ▼                                 │
┌─────────┐     ┌────────┐            │
│拥塞避免  │────→│ 拥塞   │────────────┘
│(线性增长)│     │ 超时   │
└─────────┘     └────────┘
                      │
                      ▼
                 ssthresh = cwnd / 2
                 cwnd = 1

6.3 现代拥塞控制算法

算法特点适用场景
Reno经典算法,快速恢复通用场景
CUBICLinux 默认,三次函数增长高 BDP 网络
BBRGoogle 开发,基于带宽和 RTT 估算高丢包率网络
Vegas基于延时检测拥塞低延迟网络
# Linux 查看当前拥塞控制算法
cat /sys/module/tcp_congestion_control/parameters/available
cat /proc/sys/net/ipv4/tcp_congestion_control

# 临时修改为 BBR
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr

七、TCP 调优参数

# /etc/sysctl.conf

# 连接队列大小
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# 端口复用与快速回收
net.ipv4.tcp_tw_reuse = 1
# WARNING: tcp_tw_recycle 已在 Linux 4.12 移除

# TCP Keepalive
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5

# 窗口缩放
net.ipv4.tcp_window_scaling = 1

# 缓冲区大小
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728

# BBR 拥塞控制
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq

# 生效
sudo sysctl -p
// Java Socket 调优
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.setReceiveBufferSize(128 * 1024);

Socket socket = serverSocket.accept();
socket.setTcpNoDelay(true);        // 禁用 Nagle 算法
socket.setSoTimeout(30000);        // 读超时
socket.setSendBufferSize(64 * 1024);
socket.setReceiveBufferSize(64 * 1024);
socket.setKeepAlive(true);          // TCP Keepalive
socket.setOOBInline(true);          // 接收紧急数据

八、常见问题与排查

现象可能原因排查方法
大量 TIME_WAIT短连接频繁`netstat -tan
大量 CLOSE_WAIT服务端未 close检查代码中 socket 关闭逻辑
SYN Flood 攻击恶意连接启用 SYN Cookies
连接耗时高DNS 解析慢/网络延迟tcpdump / Wireshark 抓包分析
吞吐量低窗口太小/丢包检查 ss -tin 看 cwnd、ssthresh

九、总结

机制目的实现方式
三次握手可靠建立连接SYN → SYN+ACK → ACK
四次挥手优雅关闭连接FIN → ACK → FIN → ACK
序列号有序交付、去重每字节编号
确认应答可靠传输累积 ACK
超时重传丢包恢复RTO 计算、快速重传
滑动窗口流量控制接收窗口通告
拥塞控制网络保护慢启动、拥塞避免、BBR

TCP 通过精心设计的机制,在不可靠的 IP 层之上构建了可靠的传输通道。理解这些机制,不仅是网络编程的基础,更是排查线上问题的利器。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「network」更多文章

  1. 网络安全:TLS/SSL、证书与加密通信
  2. 负载均衡算法与高可用架构
  3. DNS 系统与智能解析