Skip to content

TCP 协议 (Transmission Control Protocol)

一、TCP 概述

什么是 TCP

TCP (Transmission Control Protocol,传输控制协议):一种面向连接、可靠、基于字节流的传输层协议。

TCP 位于应用层和 IP 层之间:

应用层: HTTP、HTTPS、SSH、SMTP、MySQL
                ↓
传输层: TCP —— 可靠传输、端口复用、流量控制、拥塞控制
                ↓
网络层: IP —— 寻址和路由
                ↓
链路层: Ethernet / Wi-Fi

TCP 的核心特点

  • 面向连接:通信前需要建立连接
  • 可靠传输:确认、重传、校验、去重、排序
  • 全双工:双方可以同时发送和接收
  • 字节流:没有消息边界
  • 有序交付:应用按发送顺序读取数据
  • 流量控制:避免发送方压垮接收方
  • 拥塞控制:避免过多流量压垮网络
  • 一对一通信:一个 TCP 连接只连接两个端点

TCP 连接的标识

一个 TCP 连接通常由四元组唯一标识:

源 IP + 源端口 + 目的 IP + 目的端口

示例:

192.168.1.10:53000 → 203.0.113.20:443

同一服务器端口可以同时服务大量客户端,因为每个客户端的源 IP 或源端口不同。

常见 TCP 应用

协议/应用 默认端口 说明
HTTP 80 Web 明文传输
HTTPS 443 HTTP over TLS
SSH 22 安全远程登录
FTP 20/21 文件传输
SMTP 25/465/587 邮件发送
IMAP 143/993 邮件读取
MySQL 3306 数据库
Redis 6379 缓存数据库

二、TCP 报文格式

1. TCP 首部结构

0                   15                  31
+-------------------+-------------------+
| Source Port       | Destination Port  |
+---------------------------------------+
| Sequence Number                       |
+---------------------------------------+
| Acknowledgment Number                 |
+----+---+----------------+--------------+
|DOFF|Res| Flags          | Window Size  |
+-------------------------+--------------+
| Checksum          | Urgent Pointer    |
+---------------------------------------+
| Options (可选,0~40 字节)              |
+---------------------------------------+
| Application Data                      |
+---------------------------------------+

TCP 固定首部为 20 字节,包含选项时最长为 60 字节。

2. 字段说明

字段 长度 作用
Source Port 16 bit 源端口
Destination Port 16 bit 目的端口
Sequence Number 32 bit 本报文段第一个数据字节的序号
Acknowledgment Number 32 bit 期望收到的下一个字节序号
Data Offset 4 bit TCP 首部长度,单位为 4 字节
Flags 多个标志位 控制连接和数据传输
Window Size 16 bit 接收窗口大小
Checksum 16 bit 检查首部和数据是否损坏
Urgent Pointer 16 bit 紧急数据位置,较少使用
Options 可变 MSS、窗口缩放、SACK、时间戳等

3. TCP 标志位

标志 名称 作用
SYN Synchronize 建立连接、同步初始序列号
ACK Acknowledgment 确认号有效
FIN Finish 发送方没有更多数据
RST Reset 立即复位连接
PSH Push 提示尽快把数据交给应用
URG Urgent 紧急指针有效
ECE ECN Echo 显式拥塞通知
CWR Congestion Window Reduced 已降低拥塞窗口
NS Nonce Sum 历史 ECN 扩展,较少使用

4. TCP 校验和

TCP 校验和覆盖:

IP 伪首部 + TCP 首部 + TCP 数据

伪首部包含源 IP、目的 IP、协议号和 TCP 长度,可以发现部分错误投递。TCP 校验和在 IPv4 和 IPv6 中都必须使用。

抓包时看到本机发出的 TCP 校验和错误,不一定真的有问题,可能是网卡 Checksum Offload 在抓包点之后才计算校验和。


三、TCP 序列号与确认号

1. 字节序号

TCP 对传输的每个字节编号,而不是对报文段编号。

发送 1000 字节:
第一个报文段: Seq=1000, Len=600
第二个报文段: Seq=1600, Len=400

2. 累积确认

ACK=1600

表示:

序号 1599 及以前的字节已经连续收到
下一步期望收到序号 1600

TCP 默认使用累积确认,一个 ACK 可以确认此前连续收到的全部数据。

3. SYN 与 FIN 占用序列号

  • SYN 消耗一个序列号
  • FIN 消耗一个序列号
  • 纯 ACK 不消耗序列号
  • 数据长度按实际字节数推进序列号

4. 初始序列号

连接双方分别选择自己的 ISN (Initial Sequence Number):

客户端 ISN = x
服务器 ISN = y

ISN 应难以预测,以降低旧报文干扰和序列号猜测攻击风险。

5. 序列号回绕

序列号为 32 bit,达到 2^32 - 1 后会回绕。TCP 通过窗口范围、时间戳等机制区分当前数据和旧报文。


四、TCP 三次握手

1. 建立连接过程

客户端                                      服务器
  │                                            │
  │ SYN, Seq=x                                 │
  ├───────────────────────────────────────────→│
  │                                            │
  │ SYN+ACK, Seq=y, Ack=x+1                    │
  │←───────────────────────────────────────────┤
  │                                            │
  │ ACK, Seq=x+1, Ack=y+1                      │
  ├───────────────────────────────────────────→│
  │                                            │
  │              连接建立                      │

2. 每次握手的作用

步骤 报文 作用
第一次 SYN 客户端请求连接并发送自己的 ISN
第二次 SYN+ACK 服务器确认客户端 ISN,并发送自己的 ISN
第三次 ACK 客户端确认服务器 ISN

3. 为什么是三次握手

三次握手至少要确认:

  • 客户端能发送,服务器能接收
  • 服务器能发送,客户端能接收
  • 双方都知道对方已经收到自己的初始序列号
  • 本次连接不是网络中滞留的旧 SYN 报文

两次握手无法让服务器确认客户端已经收到服务器的 SYN 和初始序列号。

4. 握手可以携带数据吗

  • 普通 TCP 中,第三次握手 ACK 可以同时携带应用数据
  • SYN 中携带数据需要 TCP Fast Open (TFO) 等扩展
  • 是否启用和接受 TFO 取决于客户端、服务器和中间网络

5. 连接失败重传

SYN 没有得到响应时,客户端通常按递增间隔重传,达到系统重试次数后返回连接超时。

SYN → 等待 → 重传 SYN → 等待更久 → ... → 超时

具体次数和超时时间由操作系统配置决定。


五、TCP 四次挥手

1. 正常关闭过程

假设客户端主动关闭:

客户端                                      服务器
  │                                            │
  │ FIN, Seq=u                                 │
  ├───────────────────────────────────────────→│
  │                                            │
  │ ACK, Ack=u+1                               │
  │←───────────────────────────────────────────┤
  │                                            │
  │              服务器继续处理剩余数据        │
  │                                            │
  │ FIN, Seq=v                                 │
  │←───────────────────────────────────────────┤
  │                                            │
  │ ACK, Ack=v+1                               │
  ├───────────────────────────────────────────→│
  │                                            │

2. 为什么通常是四次

TCP 是全双工协议:

  • 收到对方 FIN,只表示对方不再发送数据
  • 本端仍可继续发送剩余数据
  • 两个方向需要分别关闭

如果接收方没有剩余数据,ACK 和 FIN 可以合并,因此抓包中也可能只看到三个关闭报文。

3. 半关闭

一方调用 shutdown(SHUT_WR) 后:

本端不再发送数据
但仍可以继续接收对方数据

这称为 TCP Half-Close,适合“请求发送完毕,但仍要读取完整响应”的场景。

4. RST 关闭

RST 表示异常或立即终止:

  • 访问没有监听的端口
  • 应用异常退出
  • 对不存在的连接收到报文
  • 使用 SO_LINGER 配置为立即复位
  • 中间防火墙主动拒绝连接

RST 不执行正常的双向关闭流程,未读取的数据可能丢失。


六、TCP 状态机

1. 常见状态

状态 含义
CLOSED 没有连接
LISTEN 服务器等待连接
SYN_SENT 已发送 SYN,等待 SYN+ACK
SYN_RECV 已收到 SYN 并回复 SYN+ACK
ESTABLISHED 连接已建立
FIN_WAIT_1 主动关闭方已发送 FIN
FIN_WAIT_2 已收到 FIN 的 ACK,等待对方 FIN
CLOSE_WAIT 被动关闭方收到 FIN,等待应用关闭
LAST_ACK 被动关闭方已发送 FIN,等待最终 ACK
CLOSING 双方几乎同时关闭
TIME_WAIT 主动关闭方等待旧报文过期

2. 客户端典型状态变化

CLOSED
  ↓ connect()
SYN_SENT
  ↓ 收到 SYN+ACK,发送 ACK
ESTABLISHED
  ↓ 主动 close(),发送 FIN
FIN_WAIT_1
  ↓ 收到 ACK
FIN_WAIT_2
  ↓ 收到 FIN,发送 ACK
TIME_WAIT
  ↓ 等待 2MSL
CLOSED

3. 服务器典型状态变化

CLOSED
  ↓ bind() + listen()
LISTEN
  ↓ 收到 SYN,发送 SYN+ACK
SYN_RECV
  ↓ 收到 ACK
ESTABLISHED
  ↓ 收到 FIN,发送 ACK
CLOSE_WAIT
  ↓ 应用 close(),发送 FIN
LAST_ACK
  ↓ 收到 ACK
CLOSED

4. TIME_WAIT

TIME_WAIT 通常由主动关闭连接的一方进入,持续约 2MSL。

作用:

  • 确保最后一个 ACK 丢失时还能重发
  • 让旧连接中的延迟报文在网络中消失
  • 避免相同四元组的新连接误收旧报文

大量短连接可能产生许多 TIME_WAIT。优先使用连接复用和连接池,而不是盲目缩短 TIME_WAIT。

5. CLOSE_WAIT

CLOSE_WAIT 表示:

对方已经关闭发送方向
本地应用尚未关闭 socket

大量长期存在的 CLOSE_WAIT 通常是应用没有正确执行 close(),应修复应用资源释放逻辑。


七、TCP 可靠传输

1. 可靠性的组成

TCP 通过以下机制实现可靠传输:

  • 序列号
  • 确认应答
  • 超时重传
  • 快速重传
  • 校验和
  • 接收端去重
  • 乱序重组
  • 滑动窗口

2. 超时重传

发送 Seq=1000, Len=500
        ↓
等待 ACK=1500
        ↓ 超过 RTO 仍未收到
重传 Seq=1000, Len=500

RTO (Retransmission Timeout) 根据测量到的 RTT 和波动动态计算,不应简单设置为固定 RTT。

3. RTT 测量

  • RTT:报文从发送到收到确认的往返时间
  • SRTT:平滑后的 RTT
  • RTTVAR:RTT 波动估计
  • RTO:依据 SRTT 和 RTTVAR 计算的重传超时

发生超时后,RTO 通常指数退避,防止持续重传加重拥塞。

4. 快速重传

当接收方发现一个缺口,但继续收到后续数据时,会重复确认当前期望序号:

收到:1000-1499
丢失:1500-1999
收到:2000-2499 → ACK=1500
收到:2500-2999 → ACK=1500
收到:3000-3499 → ACK=1500

经典 TCP 在收到多个重复 ACK 后,不等待 RTO 就重传疑似丢失的数据。

5. SACK

SACK (Selective Acknowledgment) 允许接收方告诉发送方哪些非连续区间已经收到:

ACK=1500
SACK:2000-3500 已收到

发送方只需补发缺失的 1500-1999,而不是重复发送后续全部数据。

6. 乱序与重复

  • 乱序数据可暂存在接收缓冲区
  • 缺失数据到达后按顺序交给应用
  • 重复报文根据序列号识别并丢弃
  • TCP 不会把同一个字节重复交给应用

7. TCP 不能保证什么

TCP 保证连接内字节流的可靠、有序交付,但不保证:

  • 对端应用已经处理数据
  • 数据已经写入磁盘
  • 一次 write() 对应一次 read()
  • 连接永远不会中断
  • 业务操作不会重复执行

需要应用层协议定义消息边界、业务确认、幂等和故障恢复。


八、滑动窗口与流量控制

1. 为什么需要窗口

如果每发送一个报文段都等待确认,链路利用率很低:

发送 1 段 → 等 ACK → 发送 1 段 → 等 ACK

滑动窗口允许发送方在未收到确认前连续发送多个报文段:

发送 1、2、3、4 → 接收累计 ACK → 窗口向前滑动

2. 接收窗口 rwnd

接收方通过 TCP Header 的 Window 字段通告可用缓冲空间:

rwnd = 接收缓冲区剩余空间

发送方未确认的数据量不能超过接收窗口,从而避免接收方缓冲区溢出。

3. 窗口缩放

TCP 原始窗口字段只有 16 bit,最大 65535 字节。握手时协商 Window Scale 后,可把窗口左移最多 14 位,支持高带宽、高延迟链路。

实际窗口 = Header Window × 2^scale

窗口缩放只能在 SYN 阶段协商。

4. 零窗口

接收缓冲区满时,接收方通告:

Window = 0

发送方暂停普通数据,并周期性发送 Zero Window Probe,确认窗口是否重新打开,避免窗口更新报文丢失后双方永久等待。

5. 糊涂窗口综合征

如果接收方频繁开放很小的窗口,会产生大量小报文。常见缓解机制:

  • 接收端 Clark 方案
  • 发送端 Nagle 算法
  • 合理设置缓冲区
  • 应用批量读写

九、TCP 拥塞控制

1. 流量控制与拥塞控制

机制 保护对象 核心变量
流量控制 接收主机 rwnd
拥塞控制 网络链路和中间设备 cwnd

发送方实际可发送窗口近似为:

send_window = min(rwnd, cwnd)

2. 拥塞窗口 cwnd

cwnd (Congestion Window) 是发送方根据网络状况维护的窗口。丢包、ECN 和延迟变化可作为拥塞信号。

3. 经典 Reno 阶段

阶段 行为
慢启动 cwnd 每个 RTT 近似翻倍
拥塞避免 cwnd 每个 RTT 近似线性增长
快速重传 重复 ACK 达到阈值后立即重传
快速恢复 降低 cwnd,但不完全回到初始状态

4. 慢启动

“慢”指从较小窗口开始,不代表线性增长:

cwnd: 1 → 2 → 4 → 8 → 16 ...

达到慢启动阈值 ssthresh 或检测到拥塞后,进入拥塞避免。

现代 TCP 的初始窗口通常大于 1 MSS,具体由实现和标准决定。

5. 丢包后的处理

经典行为:

  • RTO 超时:认为拥塞严重,大幅减小 cwnd
  • 重复 ACK / SACK:认为部分丢包,快速重传并适度减小 cwnd
  • ECN:无需等待丢包即可通知拥塞

具体增长和退让方式取决于拥塞控制算法。

6. 常见拥塞控制算法

算法 特点
Reno/NewReno 经典基于丢包的算法
CUBIC Linux 常用,适合高速长距离网络
BBR 基于带宽和 RTT 模型,不只依赖丢包
DCTCP 利用 ECN,常用于数据中心
# Linux 查看可用算法
sysctl net.ipv4.tcp_available_congestion_control

# 查看当前算法
sysctl net.ipv4.tcp_congestion_control

十、MSS、MTU 与分段

1. MTU

MTU (Maximum Transmission Unit):链路层一次可承载的最大网络层报文大小。普通以太网 MTU 常为 1500 字节。

2. MSS

MSS (Maximum Segment Size):TCP 报文段中最大数据部分,不包含 IP 和 TCP 首部。

典型 IPv4:

MTU 1500
- IPv4 首部 20
- TCP 首部 20
= MSS 1460

典型 IPv6:

MTU 1500
- IPv6 首部 40
- TCP 首部 20
= MSS 1440

存在 TCP/IP 选项、隧道或额外封装时,可用 MSS 可能更小。

3. MSS 协商

双方在 SYN 报文中通告自己愿意接收的 MSS:

客户端 SYN: MSS=1460
服务器 SYN+ACK: MSS=1440

MSS 是单方向的接收能力声明,双方值可以不同。

4. PMTUD

Path MTU Discovery 用于发现完整路径允许的最大 IP 包:

  • IPv4 依赖 DF 标志和 ICMP Fragmentation Needed
  • IPv6 路由器不分片,依赖 ICMPv6 Packet Too Big

如果网络错误丢弃这些 ICMP 报文,可能出现 PMTUD Black Hole:握手成功,但大数据传输卡住。

5. TSO/GSO/GRO

现代操作系统和网卡会使用卸载与聚合:

  • TSO/GSO:发送端把大块数据交给内核或网卡再分段
  • GRO/LRO:接收端把多个报文段聚合后交给协议栈

因此,在不同抓包位置看到的报文大小可能不同。


十一、TCP 选项

1. 常见选项

选项 作用 协商阶段
MSS 最大 TCP 数据段大小 SYN
Window Scale 扩大接收窗口 SYN
SACK Permitted 声明支持选择确认 SYN
SACK 报告已收到的数据区间 连接期间
Timestamps RTT 测量、旧报文保护 SYN 协商后使用
TCP Fast Open 减少重复连接的握手延迟 SYN/Cookie

2. 时间戳

TCP Timestamp 包含:

  • TSval:发送方时间戳值
  • TSecr:回显对方时间戳

用途:

  • 更精确地测量 RTT
  • PAWS 防止高速连接中的旧序列号报文被接受

3. TCP Fast Open

TFO 允许已获得 Cookie 的客户端在 SYN 中携带数据,减少一次往返延迟。

限制:

  • 首次连接仍需获取 Cookie
  • 中间设备兼容性可能影响使用
  • 应用必须考虑早期数据被重放的风险
  • 只适合幂等或可安全重试的请求

十二、TCP 字节流与粘包

1. TCP 没有消息边界

发送方:

write("hello")
write("world")

接收方可能读到:

"helloworld"

也可能分成:

"hel"
"lowor"
"ld"

这不是 TCP 错误,而是字节流协议的正常行为。

2. 应用层划分消息的方法

方法 示例
固定长度 每条消息固定 64 字节
分隔符 HTTP Header 使用 CRLF
长度前缀 前 4 字节表示消息长度
自描述格式 HTTP Chunked、部分序列化协议
连接关闭 HTTP/1.0 可用关闭表示消息结束

3. 正确读取方式

应用不能假设一次 recv() 就获得完整消息,应:

1. 把收到的字节加入缓冲区
2. 按协议规则判断完整消息
3. 取出一条或多条完整消息
4. 保留不完整尾部,等待下次读取

十三、Socket 与服务器工作流程

1. TCP 服务器

socket()
   ↓
bind()
   ↓
listen()
   ↓
accept()
   ↓
read()/write()
   ↓
close()

2. TCP 客户端

socket()
   ↓
connect()
   ↓
read()/write()
   ↓
close()

3. 监听 socket 与连接 socket

  • 监听 socket:处于 LISTEN,接收新连接
  • 连接 socket:由 accept() 返回,对应具体四元组
  • 一个监听 socket 可以派生很多连接 socket

4. 连接队列

服务器通常维护:

  • 尚未完成握手的 SYN 队列
  • 已完成握手、等待应用 accept() 的队列

队列已满、应用处理太慢或遭遇 SYN Flood 时,新连接可能超时或被拒绝。

5. 阻塞与非阻塞

模式 特点
阻塞 I/O 调用等待完成,编程简单
非阻塞 I/O 未就绪时立即返回,需要事件循环
I/O 多路复用 select/poll/epoll/kqueue 管理大量连接
异步 I/O 完成后通知应用,模型依平台而异

十四、TCP 性能相关机制

1. Nagle 算法

Nagle 算法减少大量小报文:

有未确认数据时,暂存新的小块数据
直到收到 ACK 或积累到一个 MSS

适合吞吐优先场景,但可能增加交互延迟。低延迟应用可按需启用 TCP_NODELAY,不应无条件关闭。

2. Delayed ACK

接收方可能短暂等待:

  • 看能否把 ACK 搭载在反向数据上
  • 看能否用一个 ACK 确认多个报文段

Delayed ACK 与 Nagle 组合不当可能造成额外延迟。

3. ACK Piggyback

全双工通信中,ACK 可以与反向发送的数据放在同一 TCP 报文段中,减少纯 ACK 数量。

4. 连接复用

减少反复握手和慢启动的方法:

  • HTTP Keep-Alive
  • 数据库连接池
  • RPC 长连接
  • HTTP/2 多路复用

连接池应设置:

  • 最大连接数
  • 空闲超时
  • 连接最大寿命
  • 健康检查
  • 获取连接超时

5. 带宽时延积

BDP = 带宽 × RTT

要充分利用高带宽、高延迟链路,发送窗口和接收窗口通常需要至少覆盖 BDP。

示例:

带宽 1 Gbit/s
RTT 100 ms
BDP ≈ 12.5 MB

窗口过小会导致链路无法跑满。


十五、TCP 保活与应用心跳

1. TCP Keepalive

空闲连接启用 Keepalive 后,内核会周期性探测对端是否仍可达。

Linux 常见参数:

sysctl net.ipv4.tcp_keepalive_time
sysctl net.ipv4.tcp_keepalive_intvl
sysctl net.ipv4.tcp_keepalive_probes

2. Keepalive 的限制

  • 默认探测间隔通常很长
  • 只能判断连接层面是否可达
  • 不能确认应用是否健康
  • 中间 NAT、防火墙可能更早清理空闲连接

3. 应用层心跳

应用心跳可以携带协议语义:

PING → PONG

适用于:

  • 快速发现应用无响应
  • 保持 NAT 映射
  • 测量业务级延迟
  • 触发故障切换

应避免所有连接在同一时刻发送心跳,可加入抖动并合理设置超时。


十六、TCP 安全

1. SYN Flood

攻击者发送大量 SYN,但不完成握手,使服务器半连接队列耗尽。

防护措施:

  • SYN Cookies
  • 增大合理的连接队列
  • 限速和防火墙清洗
  • 缩短异常半连接占用时间
  • 上游 DDoS 防护

2. SYN Cookies

服务器不立即保存完整半连接状态,而把必要信息编码到 SYN+ACK 的序列号中。客户端返回合法 ACK 后再创建连接状态。

优点:减轻 SYN 队列耗尽。

限制:部分 TCP 选项信息可能受实现限制,且它不能替代完整的流量清洗。

3. TCP RST 注入

如果攻击者能够猜中连接四元组和有效序列号范围,可能伪造 RST 中断连接。

防护:

  • 使用 TLS、IPsec 等保护上层数据或链路
  • 使用不可预测的初始序列号
  • 避免不可信网络中的明文管理协议
  • 采用符合标准的 RST 校验策略

4. 会话劫持

TCP 本身不加密、不认证应用数据。能够观察并注入流量的攻击者可能劫持明文连接。

应使用:

  • TLS / HTTPS
  • SSH
  • IPsec / WireGuard
  • 强身份认证和证书校验

5. 端口扫描

常见 SYN 扫描结果:

开放端口: SYN → SYN+ACK
关闭端口: SYN → RST
过滤端口: 无响应或 ICMP 错误

防护重点是减少不必要的监听服务、限制来源并持续更新服务,而不是仅隐藏端口。


十七、抓包分析

1. tcpdump

# 抓取某端口的 TCP 流量
sudo tcpdump -i any -n 'tcp port 443'

# 抓取指定主机
sudo tcpdump -i any -n 'tcp and host 192.0.2.10'

# 只看 SYN
sudo tcpdump -i any -n 'tcp[tcpflags] & tcp-syn != 0'

# 保存抓包
sudo tcpdump -i any -n tcp -w tcp.pcap

2. Wireshark 过滤器

tcp
tcp.port == 443
tcp.stream eq 0
tcp.flags.syn == 1
tcp.flags.reset == 1
tcp.analysis.retransmission
tcp.analysis.fast_retransmission
tcp.analysis.duplicate_ack
tcp.analysis.zero_window

3. 握手抓包

SYN       Seq=0  MSS=1460 SACK_PERM WS=7
SYN, ACK  Seq=0  Ack=1 MSS=1460 SACK_PERM WS=7
ACK       Seq=1  Ack=1

Wireshark 默认显示相对序列号,因此初始 SYN 常显示为 Seq=0。

4. 常见异常

抓包现象 可能原因
SYN 重传,无响应 路由、防火墙、服务未到达
立即收到 RST 端口未监听或主动拒绝
SYN+ACK 重传 客户端 ACK 未到服务器
大量重传 丢包、拥塞、链路质量差
Zero Window 接收应用读取太慢
Window Full 发送量达到接收窗口
Out-of-Order 多路径、丢包或抓包位置影响
握手成功,大包卡住 MTU/PMTUD、防火墙丢 ICMP

十八、常用排查命令

1. 查看连接

# Linux
ss -tan
ss -ltnp
ss -s

# 查看某个目的端口
ss -tan dst :443

# 查看 TCP 详细信息
ss -ti

2. 测试端口

nc -vz example.com 443
curl -v https://example.com/

3. 查看内核统计

nstat -az
netstat -s

重点关注:

  • 重传报文段
  • 连接失败
  • Listen 队列溢出
  • RST 数量
  • 超时数量

4. 查看进程和端口

ss -ltnp
lsof -nP -iTCP -sTCP:LISTEN

5. 路径和 MTU

tracepath example.com
ping -M do -s 1472 192.0.2.10   # Linux IPv4,测试 1500 MTU

十九、TCP 故障排查

1. 排查流程

1. DNS:域名是否解析正确
2. 路由:目标 IP 是否可达
3. 端口:服务是否监听正确地址和端口
4. 防火墙:客户端、服务器和中间设备是否放行
5. 握手:SYN、SYN+ACK、ACK 在哪里丢失
6. 数据:是否重传、零窗口、RST、MTU 黑洞
7. 应用:是否及时 accept/read/write/close
8. 性能:RTT、丢包、窗口、拥塞控制、连接池

2. Connection Refused

connect: Connection refused

通常表示目标主机返回 RST:

  • 端口未监听
  • 服务监听在错误地址
  • 防火墙使用 REJECT
  • 访问了错误 IP 或端口

3. Connection Timed Out

connect: Connection timed out

通常表示 SYN 或响应被静默丢弃:

  • 路由错误
  • 防火墙 DROP
  • 安全组未放行
  • 目标主机离线
  • 回程路由错误

4. Connection Reset

Connection reset by peer

表示收到 RST,可能原因:

  • 对端应用异常终止
  • 协议或请求不符合预期
  • 中间设备主动断开
  • 向已关闭连接继续发送
  • 应用未读取数据就强制关闭

5. 大量 TIME_WAIT

优先检查:

  • 是否每次请求都新建连接
  • 是否可使用 Keep-Alive 或连接池
  • 上游是否主动要求关闭
  • 负载是否确实需要这么多短连接

TIME_WAIT 本身是正常状态,不应只因数量大就修改内核参数。

6. 大量 CLOSE_WAIT

优先定位持有 socket 的进程和调用路径:

对端已经关闭 → 本地应用没有 close()

通常应修复应用代码,而不是调低系统超时。

7. 吞吐低

检查:

  • RTT 和丢包率
  • 接收/发送窗口
  • BDP 是否大于缓冲区
  • 拥塞控制算法
  • CPU、网卡和加密开销
  • 应用是否小块同步读写
  • 是否存在重传或 MTU 问题

8. 常见问题对照

问题 常见原因 重点检查
无法建立连接 路由、端口、防火墙 SYN 流向、监听状态
建连很慢 丢包、SYN 重传、DNS 抓包、RTT、解析时间
连接频繁断开 超时、RST、NAT 清理 Keepalive、应用日志
传输卡住 零窗口、MTU 黑洞 ss -ti、抓包
重传很多 拥塞、链路丢包 接口错误、路径质量
CLOSE_WAIT 多 应用未关闭连接 文件描述符、代码路径
TIME_WAIT 多 大量主动关闭短连接 连接池、Keep-Alive
偶发数据重复 应用重试非幂等操作 业务请求 ID、幂等设计

二十、TCP 与 UDP 对比

维度 TCP UDP
连接 面向连接 无连接
数据形式 字节流 数据报
可靠性 确认、重传、有序 不保证
消息边界 不保留 保留
流量控制 有 无
拥塞控制 有 协议/应用自行实现
首部 20~60 字节 固定 8 字节
广播/组播 不支持 支持
延迟 建连和控制开销较高 开销较低
典型应用 HTTP/1.1、HTTP/2、SSH、数据库 DNS、实时音视频、QUIC

选择原则:

  • 需要可靠、有序字节流:TCP
  • 需要数据报、组播、低开销或自定义可靠性:UDP
  • 不应只根据“速度快慢”选择,应根据应用语义和网络需求选择

二十一、核心要点速记

  • TCP = 面向连接、可靠、全双工、字节流协议
  • TCP 连接由源 IP、源端口、目的 IP、目的端口标识
  • TCP 固定首部 20 字节,最大 60 字节
  • 三次握手:SYN → SYN+ACK → ACK
  • 四次挥手:FIN → ACK → FIN → ACK
  • SYN 和 FIN 各消耗一个序列号,纯 ACK 不消耗
  • ACK 表示下一步期望收到的字节序号
  • TCP 默认使用累积确认
  • 超时重传依据动态 RTO,不是固定时间
  • SACK 允许只重传缺失区间
  • rwnd 用于流量控制,保护接收方
  • cwnd 用于拥塞控制,保护网络
  • 实际发送窗口约为 min(rwnd, cwnd)
  • 经典拥塞控制包括慢启动、拥塞避免、快速重传和快速恢复
  • MSS 是 TCP 数据大小,MTU 是链路可承载的 IP 包大小
  • 普通 IPv4 以太网常见 MSS = 1460
  • TCP 是字节流,不保留应用写入的消息边界
  • 应用必须自行设计定长、分隔符或长度前缀协议
  • TIME_WAIT 通常在主动关闭方,作用是处理旧报文和最终 ACK 重传
  • 大量 CLOSE_WAIT 通常表示应用没有正确关闭 socket
  • Nagle 减少小报文,TCP_NODELAY 可用于低延迟场景
  • TCP Keepalive 不等于应用健康检查
  • TCP 本身不加密,应使用 TLS、SSH、IPsec 等保护数据
  • Connection Refused 通常对应 RST
  • Connection Timed Out 通常对应报文被静默丢弃
  • 握手成功但大数据卡住,应检查 MTU、PMTUD 和 ICMP
  • ss -tan 查看连接,ss -ti 查看 TCP 详细状态
  • Wireshark 的 tcp.analysis.retransmission 可筛选重传