TCP协议回顾:三次握手与四次挥手、状态转换、序列号与确认号

说实话,做网络抓包这么多年,我见过太多人一上来就盯着应用层数据猛看。结果呢?连最基本的TCP握手都没搞明白,抓到的包全是乱序重传,还以为是服务器出了问题。

TCP协议,说白了就是两台机器之间的一条「虚拟管道」。这条管道怎么建、怎么拆、中间怎么保证数据不乱——这就是我们今天要聊的核心。

一、TCP三次握手:建立连接的过程

我记得刚入行那会儿,带我的老工程师跟我说:「TCP握手就像打电话——你拨号,对方说听到了,你再回一句好的,这才算通。」这个比喻我一直记着。

三次握手的本质:客户端和服务器互相确认对方的接收能力和发送能力都正常。

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

你想想看,如果只有两次握手会怎样?服务器没法确认客户端是否收到了自己的SYN+ACK。万一那个包丢了,服务器白白等在那里,资源就浪费了。

实战小技巧:我在排查连接超时问题时,经常先看三次握手是否完成。如果只看到SYN包没有SYN+ACK,八成是防火墙拦截了。如果SYN+ACK发出去了但没收到最后的ACK,那可能是客户端半连接队列满了。

二、TCP四次挥手:优雅地断开连接

断开连接比建立连接复杂一些。为什么?因为TCP是全双工的,两边都要说「再见」才行。

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

注意:我曾经遇到过一个线上问题——服务器上TIME_WAIT连接堆积到几万个,导致新连接无法建立。后来发现是短连接太多,TIME_WAIT的2MSL等待时间太长。解决方案是用端口复用或者调整tcp_tw_reuse参数。

三、TCP状态转换图

状态转换是TCP协议里最容易让人头晕的部分。我建议你把它当成一张地图——每次抓包看到某个状态,就想想它下一步会去哪。

TCP状态转换图 TCP状态转换图 CLOSED SYN_SENT ESTABLISHED FIN_WAIT_1 FIN_WAIT_2 TIME_WAIT LISTEN SYN_RCVD ESTABLISHED CLOSE_WAIT LAST_ACK 主动打开 收到SYN+ACK 主动关闭 收到ACK 收到FIN 2MSL超时 收到SYN 收到ACK 收到FIN 应用关闭 收到ACK 蓝色:客户端状态 橙色/青色:服务器状态

这张图里,我特意把客户端和服务器分成了两列。你抓包的时候,对照着看状态变化,会清晰很多。

四、序列号与确认号:TCP的「快递单号」

TCP能保证数据不乱、不丢、不重,靠的就是序列号和确认号。我习惯把它们比作快递包裹上的单号——每个包裹都有唯一编号,收件人收到后告诉你「我收到第几号了,下一个发第几号」。

概念 含义 抓包中的表现
序列号(SEQ) 当前数据包的第一个字节的编号 Seq=1 表示这是从字节1开始的数据
确认号(ACK) 期望收到的下一个序列号 Ack=1001 表示已收到前1000字节
初始序列号(ISN) 连接建立时随机生成的起始序列号 三次握手中的SYN包携带ISN

抓包小技巧:我每次看TCP流,第一件事就是看初始序列号。如果发现ISN是0或者规律递增,那八成是某些嵌入式设备或者老系统。正常的操作系统会用随机ISN防止攻击。

五、实战中的常见问题

说了这么多理论,咱们聊聊实际抓包中经常碰到的情况。

  • 半连接攻击:只发SYN不完成握手。服务器资源被耗尽。我见过一个案例,某电商平台大促前被打了半连接攻击,nginx直接挂掉。
  • CLOSE_WAIT堆积:应用层没调用close()。我曾经排查过一个Java应用,发现数据库连接池没释放,CLOSE_WAIT堆到上千个。
  • TIME_WAIT过多:短连接场景下常见。可以用端口复用或者长连接来缓解。
  • 序列号回绕:高速传输时序列号可能溢出。TCP用时间戳选项来解决这个问题。

避坑指南:我曾经在排查一个「连接卡死」的问题时,抓包发现三次握手都完成了,但数据就是发不出去。后来发现是中间防火墙把某些TCP选项给干掉了——比如窗口缩放因子。从那以后,我每次抓包都会先看TCP选项字段。

嗯,TCP协议这块内容确实不少。但只要你把三次握手、四次挥手、状态转换、序列号确认号这四块吃透了,后面讲端口复用的时候就会轻松很多。毕竟,端口复用本质上就是在TCP连接的生命周期里做文章。