1. TCP协议基础回顾:头部结构、三次握手与四次挥手、状态转换图

各位同学,咱们今天正式开讲《TCP重传定位与端口复用实战》的第一章。说实话,TCP协议这东西,很多搞了几年网络的老手都未必能把它吃透。我见过太多人一遇到TCP重传就抓瞎,端口复用更是稀里糊涂。所以,咱们先把地基打牢。

这一章,我带你过一遍TCP协议最核心的三个东西:头部结构、三次握手与四次挥手、状态转换图。别嫌基础,后面所有实战技巧都建立在这上面。

1.1 TCP头部结构:每一比特都有用

TCP头部,说白了就是20字节的固定部分,后面还能跟选项。我刚开始学的时候,总觉得这20字节记不住。后来在项目里抓包分析问题,才发现每一比特都可能是关键线索。

字段 长度(位) 说明
源端口 16 发送方端口号
目的端口 16 接收方端口号
序列号 32 本报文段数据的第一个字节序号
确认号 32 期望收到的下一个字节序号
数据偏移 4 头部长度,单位是4字节
保留 3 保留位,必须为0
标志位 9 URG、ACK、PSH、RST、SYN、FIN等
窗口大小 16 接收窗口,用于流量控制
校验和 16 覆盖头部和数据
紧急指针 16 URG标志位为1时有效

重点记忆:序列号和确认号是TCP可靠传输的命根子。重传定位时,你盯着这两个字段看,基本就能判断出问题出在哪一端。

嗯,这里要注意一下标志位。9个标志位里,我们最常用的是SYN、ACK、FIN、RST这四个。SYN用来发起连接,ACK用来确认,FIN用来关闭连接,RST用来异常复位。我在一次线上故障排查中,就是靠RST标志位锁定了对端服务异常关闭的问题。

1.2 三次握手:建立连接的三步舞

三次握手,说白了就是客户端和服务端互相确认对方能收能发的过程。你想想看,如果只有两次握手,服务端怎么知道客户端已经收到了自己的SYN-ACK?

  1. 第一次握手:客户端发送SYN包,序列号设为x,进入SYN_SENT状态。
  2. 第二次握手:服务端收到后,回复SYN-ACK包,序列号设为y,确认号为x+1,进入SYN_RCVD状态。
  3. 第三次握手:客户端收到后,发送ACK包,确认号为y+1,进入ESTABLISHED状态。

个人经验:我曾经遇到过一个问题,客户端一直卡在SYN_SENT状态。抓包一看,服务端根本没回SYN-ACK。后来发现是防火墙把SYN包给丢了。所以,三次握手失败,先查中间设备。

这里有个坑,我提醒一下大家。三次握手中的序列号不是从0开始的,而是随机生成的。为什么?为了防止历史连接干扰。我记得有一次,一个老系统升级后频繁断连,查了半天才发现是序列号生成算法有bug,导致新旧连接混淆。

1.3 四次挥手:优雅地告别

四次挥手比三次握手复杂一些。为什么?因为TCP连接是全双工的,每一端都要单独关闭自己的数据流。

  1. 第一次挥手:主动关闭方发送FIN包,序列号为u,进入FIN_WAIT_1状态。
  2. 第二次挥手:被动关闭方回复ACK包,确认号为u+1,进入CLOSE_WAIT状态。主动方收到后进入FIN_WAIT_2状态。
  3. 第三次挥手:被动关闭方发送FIN包,序列号为v,进入LAST_ACK状态。
  4. 第四次挥手:主动关闭方回复ACK包,确认号为v+1,进入TIME_WAIT状态。

避坑指南:我曾经在端口复用时踩过TIME_WAIT的坑。主动关闭方在发送最后一个ACK后,会进入TIME_WAIT状态,持续2MSL(约2分钟)。这段时间内,该端口不能被重用。如果你在写高并发服务器,一定要考虑这个因素。

1.4 状态转换图:TCP的完整生命周期

下面这张图,是我自己整理的TCP状态转换图。你把它记熟了,以后排查连接问题会快很多。

TCP状态转换图 TCP状态转换图 CLOSED SYN_SENT ESTABLISHED FIN_WAIT_1 FIN_WAIT_2 TIME_WAIT CLOSED LISTEN SYN_RCVD ESTABLISHED CLOSE_WAIT LAST_ACK 主动打开 收到SYN+ACK 主动关闭 收到ACK 收到FIN 被动打开 收到SYN 收到ACK 收到FIN 主动关闭 SYN SYN+ACK ACK FIN ACK FIN 2MSL超时 收到ACK 说明: 左侧为客户端状态,右侧为服务端状态。 实线为本地状态转换,虚线为网络事件触发。

这张图里,我特别标出了客户端和服务端的状态路径。你注意看,客户端在主动关闭后会进入TIME_WAIT,而服务端不会。这就是为什么高并发服务器通常设计为被动关闭方,避免TIME_WAIT堆积。

1.5 状态转换中的关键点

我个人习惯把状态转换图分成三个阶段来记:连接建立阶段、数据传输阶段、连接关闭阶段。每个阶段都有几个关键状态需要关注。

核心要点:

  • SYN_SENT和SYN_RCVD:这两个状态持续时间很短,如果长时间停留,说明网络有问题。
  • CLOSE_WAIT:服务端收到FIN后进入此状态。如果大量连接卡在CLOSE_WAIT,说明应用程序没有正确调用close()。
  • TIME_WAIT:主动关闭方最后的等待状态。端口复用时,这个状态是最大的障碍。

我记得有一次,一个同事跑来跟我说,他的服务端程序跑着跑着就报"Too many open files"。我让他用netstat -tan | grep CLOSE_WAIT一看,好家伙,几千个CLOSE_WAIT连接。说白了,就是他的代码里没有正确处理对端关闭后的清理逻辑。

实用命令:排查TCP状态问题,我常用这三个命令:

# 查看所有TCP连接状态统计
netstat -tan | awk '{print $6}' | sort | uniq -c

# 查看特定端口的连接状态
ss -tan state time-wait | grep :8080

# 实时监控连接状态变化
watch -n 1 'netstat -tan | grep ESTABLISHED | wc -l'

好了,这一章的内容就到这里。TCP头部结构、三次握手、四次挥手、状态转换图,这四个东西你吃透了,后面讲重传定位和端口复用的时候,你就能跟上我的节奏。记住,网络问题排查,90%的线索都藏在TCP的状态和头部字段里。