TCP
→ 返回 计算机网络
TCP(Transmission Control Protocol)提供面向连接、可靠、有序、全双工的字节流服务。HTTP/1.1、HTTPS、SSH、MySQL 等默认跑在 TCP 上。
与 UDP 对比见下文;基于 UDP 的 QUIC/HTTP/3 见 3。
与 UDP 对比(摘要)
| 特性 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接(三次握手) | 无连接 |
| 可靠性 | 可靠、有序 | 不可靠、可能丢包乱序 |
| 流量/拥塞控制 | 有 | 无 |
| 头部 | 20~60 字节 | 8 字节 |
| 典型场景 | Web、API、数据库、文件 | DNS、流媒体、游戏、QUIC |
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) │
├─────────────────────────────────────────────────────────────────┤
│ 序列号 Sequence Number(32bit) │
├─────────────────────────────────────────────────────────────────┤
│ 确认号 Acknowledgment Number(32bit) │
├─────────┬───┬───┬───┬───┬───┬───┬──────────────────────────────┤
│数据偏移 │保留│URG│ACK│PSH│RST│SYN│FIN│ 窗口大小(16bit) │
├─────────┴───┴───┴───┴───┴───┴───┴───┴──────────────────────────┤
│ 校验和(16bit) │ 紧急指针(16bit) │
├─────────────────────────────────────────────────────────────────┤
│ 选项(0~40 字节)+ 填充 │
└─────────────────────────────────────────────────────────────────┘
| 字段 / 标志 | 作用 |
|---|---|
| Seq | 本报文段第一个字节的序号 |
| Ack | 期望收到的下一字节(累计确认) |
| 窗口 | 接收方剩余缓冲区(流量控制) |
| SYN | 建立连接 |
| FIN | 关闭本方向发送 |
| RST | 异常复位(端口未监听、拒绝连接、半开检测失败等) |
| PSH | 尽快交给应用层(如交互式终端) |
| URG | 带紧急数据(少用) |
常见选项:MSS(最大段大小)、Window Scale(窗口扩大)、SACK(选择性确认)、Timestamps(RTT 测量与 PAWS)。
三次握手
客户端 服务端
│──── SYN(seq=x)────────────────────►│ [SYN_SENT]
│◄─── SYN+ACK(seq=y, ack=x+1)────────│ [SYN_RCVD]
│──── ACK(seq=x+1, ack=y+1)──────────►│ [ESTABLISHED]
- 客户端:
CLOSED → SYN_SENT → ESTABLISHED - 服务端:
LISTEN → SYN_RCVD → ESTABLISHED
为何三次: 确认双方收发能力;避免旧 SYN 被当成新连接(历史连接请求)。
SYN Flood: 大量 SYN 不回 ACK,占满半连接队列。防护:tcp_syncookies、限速、WAF。
监听队列:
| 队列 | 说明 |
|---|---|
| 半连接(SYN 队列) | SYN_RCVD,等待第三次 ACK |
| 全连接(Accept 队列) | 已完成握手,等待 accept() |
listen(backlog) 的 backlog 与内核 somaxconn、tcp_max_syn_backlog 共同决定上限;高并发服务需一并调优。
TCP Fast Open(TFO): 在 SYN 中携带 Cookie 与少量数据,重复访问域名时可减少 1 RTT(需客户端、服务端、中间设备支持)。
四次挥手与半关闭
主动方 ──── FIN ────► 被动方 「我不再发送」
被动方 ──── ACK ────► 主动方
被动方 ──── FIN ────► 主动方 「我也不发了」
主动方 ──── ACK ────► 被动方
主动方 进入 TIME_WAIT(2MSL)后 CLOSED
TIME_WAIT 作用: 保证最后一个 ACK 可达;让旧连接报文在网络中消散。
TIME_WAIT 过多(短连接、压测):
ss -s
ss -tan | awk '{print $1}' | sort | uniq -c
# Linux 调优(示例,按环境评估)
net.ipv4.tcp_tw_reuse = 1 # 仅客户端复用 TIME_WAIT 端口
net.ipv4.tcp_fin_timeout = 30半关闭: TCP 是全双工,一方 shutdown(SHUT_WR) 只关写方向,仍可读对端数据;应用需正确处理 CLOSE_WAIT 堆积(忘记 close())。
状态机(概要)
CLOSED → SYN_SENT / SYN_RCVD → ESTABLISHED
→ FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED
→ CLOSE_WAIT → LAST_ACK → CLOSED
可靠传输
确认、超时与快速重传
- 累计确认: Ack=N 表示 N 之前字节全部收到
- RTO: 根据 RTT 自适应;超时未确认则重传
- 快速重传: 收到 3 个重复 ACK 立即重传对应段,不等超时
- SACK: 告知非连续已收块,减少不必要的重传
滑动窗口(流量控制)
发送窗口 = min(拥塞窗口 cwnd, 接收窗口 rwnd)
- rwnd=0: 发送方停发并周期性发 零窗口探测(1 字节)
- Window Scaling: 高 BDP 链路将窗口扩展到远超 64KB
MSS 与路径 MTU
- MSS: 单段数据最大长度,通常由 MTU 推导(以太网常见 1460 字节 payload)
- PMTUD / PLPMTUD: 发现路径 MTU,避免 IP 分片(分片丢一片则整包作废,对 TCP 不利)
拥塞控制
根据网络状况调节 cwnd(拥塞窗口):
| 阶段 | 行为 |
|---|---|
| 慢开始 | cwnd 指数增长直至 ssthresh |
| 拥塞避免 | 每个 RTT 线性 +1 MSS |
| 快重传 / 快恢复 | 3 重复 ACK → 重传并 cwnd 减半恢复 |
| 超时 | cwnd 置 1,重新慢开始 |
CUBIC: Linux 默认,高带宽长延迟友好。
BBR: 以带宽与 RTT 估计 BDP,减轻 bufferbloat,不主要依赖丢包信号。
sysctl net.ipv4.tcp_congestion_control
sysctl -w net.ipv4.tcp_congestion_control=bbr延迟相关行为
Nagle 与 TCP_NODELAY
Nagle: 小包合并,前一个段被 ACK 前不发新小包 → 降低小包数量。
与 延迟 ACK(常等 ~40ms 再确认)叠加时,交互应用可能多出几十毫秒延迟。
int on = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));适用: 游戏、SSH 交互、自定义 RPC;不适用盲目关闭: 大量极小写入且对带宽敏感的场景。
TCP Keepalive
内核级探测空闲连接(与 HTTP Connection: keep-alive、WebSocket ping 不是同一概念):
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9编程与调优要点
| 选项 / 概念 | 说明 |
|---|---|
SO_REUSEADDR | 重启后快速绑定同一端口 |
SO_REUSEPORT | 多进程/线程负载均衡同一端口(Linux) |
TCP_USER_TIMEOUT | 控制未确认数据的最长等待 |
TCP_QUICKACK | 提示尽快 ACK(短时) |
| 全连接队列满 | 客户端可能收到 SYN+ACK 后 RST 或超时 |
详见 Socket。
诊断
ss -tnap
ss -tan | awk '{print $1}' | sort | uniq -c
ss -s
ss -tlnp | grep :8080
tcpdump -i eth0 host 192.168.1.100 -w cap.pcap
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0'
tshark -r cap.pcap -Y "tcp.analysis.retransmission"| 现象 | 常见原因 |
|---|---|
| 大量 SYN_RCVD | SYN Flood 或半连接队列过小 |
| TIME_WAIT 爆炸 | 短连接过多 |
| 重传率高 | 链路丢包、拥塞、无线干扰 |
| CLOSE_WAIT 堆积 | 应用未关闭 socket |
| 单连接吞吐上不去 | 窗口小、拥塞算法、RTT 大、未开 GRO/GSO |
常见 TCP 端口
| 端口 | 用途 |
|---|---|
| 22 | SSH |
| 80 | HTTP |
| 443 | HTTPS |
| 3306 | MySQL |
| 5432 | PostgreSQL |
| 6379 | Redis |
DNS 亦可用 TCP 53(大响应);见 DNS。