TCP 协议实战:握手、挥手与连接排障

TCP 是面试必问、排障必用的核心协议。三次握手为什么是三次?大量 CLOSE_WAIT 怎么办?这篇把原理和排障串成一条线,附抓包实证。

一、TCP vs UDP:先分清楚

特性 TCP UDP
连接 面向连接(三次握手) 无连接
可靠 可靠(确认重传) 不可靠
效率
应用 HTTP/HTTPS、邮件、FTP DNS(53)、视频流、语音、DHCP

类比记忆:TCP 是”外卖必须送到你手里(有确认)”,UDP 是”放门口就走(无确认)”。端口范围 1~65535。

二、三次握手(原理 + 抓包)

1
2
3
4
5
6
7
客户端 C                          服务端 S
│ SYN(seq=x) │
│───────────────────────────────>│ S 收到,状态 SYN_RCVD
│ SYN+ACK(seq=y, ack=x+1) │
│<───────────────────────────────│
│ ACK(ack=y+1) │
│───────────────────────────────>│ 连接建立

三个关键点(面试背诵版):

  1. SYN 报文不携带数据,但消耗一个序列号
  2. 第 2 次握手把”确认 ACK”和”请求 SYN”合并成一个报文——所以是三次不是四次;
  3. seq/ack 都 +1 是确认机制核心:ack = 对方 seq + 1,表示”下次该收 x+1 了”。

抓包实证(tcpdump 抓 80 端口)看三个包的 seq/ack 严格递进——第 2 个包 ack = 第 1 个包 seq+1

为什么不能只有两次握手? 场景:客户端发出 SYN 在网络滞留,超时重发后正常完成连接并释放;此时滞留的旧 SYN 才到达服务端——如果只有两次握手,服务端会为这个”已失效的请求”建立连接并一直占用资源。三次握手里,服务端发出 SYN+ACK 后必须等客户端的 ACK 才建立——客户端没发新请求就不会回 ACK,服务端等不到就自动放弃。一句话:两次握手无法确认”客户端能收到服务端的报文”,也无法拒绝失效的旧请求

三、四次挥手与 2MSL

1
2
3
4
5
6
7
8
9
10
客户端 C                            服务端 S
│ FIN(seq=m) │
│───────────────────────────────>│
│ ACK(ack=m+1) │ S 还有数据要发,先确认
│<───────────────────────────────│
│ ...S 继续发剩余数据... │
│ FIN(seq=n) │
│<───────────────────────────────│
│ ACK(ack=n+1) │ C 进入 TIME_WAIT,等 2MSL
│───────────────────────────────>│

为什么挥手 4 次而握手 3 次? 握手时服务端可以把”确认+请求”合并成一个包(SYN+ACK);挥手时服务端收到 FIN 后可能还有数据要发,不能立刻 FIN,必须先 ACK 再等数据发完单独 FIN——所以多一次。

挥手后客户端为什么要等 2MSL(Linux 默认约 60s)?

  1. 最后的 ACK 可能丢失——服务端没收到会重发 FIN,客户端在 TIME_WAIT 期间还能再回一次 ACK;
  2. 保证旧报文在网络中彻底消失,不会污染新连接。

四、连接状态与生产排障(重点)

1
2
ss -tulnp    # 查看连接状态统计
ss -s # 汇总

三类生产高频异常:

状态 含义 处理
大量 CLOSE_WAIT 对端关了,应用没关连接(代码层泄漏) 查应用:Java 连接池泄漏、请求未关闭;ss -lnt | grep CLOSE_WAIT 确认,修代码/调连接池
大量 TIME_WAIT 主动断开方等待 2MSL 高并发短连接的正常现象;用 keep-alive 连接复用减少;net.ipv4.tcp_tw_reuse=1 谨慎开启
大量 SYN_RCVD 收到 SYN 未完成握手,半连接堆积 可能被 SYN 洪水攻击;查半连接队列 ss -lnt | grep SYN,上限 net.ipv4.tcp_max_syn_backlog,配合防火墙限速

连接不上的四步排查(任何”连不上”都按这个顺序):

1
2
3
4
1. ss -lnt            # 服务有没有监听
2. telnet IP 端口 # 端口通不通(不通→防火墙/安全组)
3. 本机 curl # 服务本身是否正常
4. 抓包看握手 # tcpdump -i eth0 port 端口:SYN 有没有到、有没有回 SYN-ACK

TCP 的问题说到底是”连接建不起来(握手)”和”连接关不掉(挥手/资源泄漏)”两类。把状态机和排障命令对应起来,大部分网络故障都能落地到具体的一层。