3. TCP三次握手与并发:高并发下TCP连接建立的特征、SYN洪泛攻击的抓包特征
说到TCP三次握手,很多朋友觉得太基础了,不就是SYN、SYN-ACK、ACK嘛。但我要说,在高并发场景下,三次握手的表现完全不一样。你平时在实验室抓一个包看三次握手,跟线上几万个并发连接同时建立,那抓出来的东西简直是两个世界。
我个人习惯,每次接手一个新系统,第一件事就是在网关层抓一把握手阶段的包。为什么?因为握手阶段的异常,往往能提前暴露整个系统的瓶颈。今天我们就来聊聊,高并发下TCP连接建立到底长什么样,以及那个臭名昭著的SYN洪泛攻击,在Wireshark里到底怎么认。
3.1 标准三次握手的抓包回顾
先快速过一遍正常的三次握手,这是后面所有分析的基础。
| 步骤 | 方向 | 报文特征 | 关键字段 |
|---|---|---|---|
| 1 | 客户端 → 服务器 | SYN | Seq=0(相对值),Flags=0x002 |
| 2 | 服务器 → 客户端 | SYN-ACK | Seq=0,Ack=1,Flags=0x012 |
| 3 | 客户端 → 服务器 | ACK | Seq=1,Ack=1,Flags=0x010 |
在Wireshark里,你看到的就是三个包,时间间隔通常在毫秒级。但注意,这只是理想情况。我在项目中遇到过,有些中间件或者云厂商的负载均衡器,会在握手阶段插入额外的包,比如TCP选项协商、窗口缩放因子等。如果你看到握手包超过3个,别急着下结论,先看看是不是有TCP选项在搞鬼。
tcp.flags.syn == 1 and tcp.flags.ack == 0 可以快速定位所有SYN包。配合 tcp.stream eq N 可以只看某个TCP流的握手过程。
3.2 高并发下的握手特征
好,现在进入正题。当并发量上来以后,三次握手会出现哪些肉眼可见的变化?我总结了三个核心特征。
3.2.1 握手延迟与重传
高并发下,服务器处理SYN包的能力是有限的。每个SYN包到达后,服务器需要分配一个传输控制块(TCB),这个操作涉及内存分配和哈希表操作。当并发量超过服务器的处理能力时,SYN包就会被丢进队列里排队。
你在抓包里会看到什么?
- SYN包的响应时间变长:正常情况客户端发出SYN后,几十微秒就能收到SYN-ACK。高并发下可能变成几毫秒甚至几十毫秒。
- SYN重传出现:如果服务器太忙,客户端等不到SYN-ACK,就会重传SYN。Wireshark里会标记为
[TCP Retransmission]。 - 握手完成时间波动大:有些连接可能100ms完成,有些要500ms,甚至超时失败。
3.2.2 Backlog队列与SYN丢包
这里要提一个重要的概念——半连接队列(SYN Queue)。服务器收到SYN后,连接状态是 SYN_RCVD,这个连接会放在半连接队列里。如果队列满了,新的SYN包就会被直接丢弃。
抓包特征非常明显:
- 客户端发了SYN,服务器没有任何回应(没有SYN-ACK,也没有RST)。
- 客户端等超时后重传SYN,重传几次后放弃。
- Wireshark里显示
[SYN]后面跟着[TCP Retransmission],然后就没有然后了。
嗯,这里要注意:不是所有SYN无响应都是队列满了。也可能是防火墙丢包、路由问题、或者服务器宕机。怎么区分?看丢包模式。如果是队列满,通常是间歇性丢包——有些SYN能通,有些不能。如果是防火墙策略,往往是全部丢或者按规则丢。
3.2.3 全连接队列溢出
半连接队列之后,还有全连接队列(Accept Queue)。当三次握手完成,连接进入 ESTABLISHED 状态,但还没被应用层 accept() 取走时,就放在这个队列里。
全连接队列溢出的抓包特征更隐蔽:
- 三次握手看起来是正常的(SYN、SYN-ACK、ACK都完成了)。
- 但后续的数据包发不过去,或者服务器直接发RST断开连接。
- Wireshark里你会看到完整的握手,然后紧接着一个
[RST]或[TCP Dup ACK]。
accept() 循环里加了一个耗时的数据库查询,导致全连接队列溢出。所以,握手成功不代表连接可用,一定要看后续的数据交互。
3.3 SYN洪泛攻击的抓包特征
说到高并发握手,就绕不开SYN洪泛攻击。这是最经典的DDoS攻击方式之一,原理很简单:攻击者发送大量SYN包,但不完成第三次握手,让服务器维护大量半连接,耗尽资源。
3.3.1 攻击包的典型特征
在Wireshark里,SYN洪泛攻击的包有几个非常明显的特征:
| 特征 | 描述 | Wireshark中的表现 |
|---|---|---|
| 源IP随机 | 攻击者伪造源IP,每个SYN包来自不同IP | 统计 → IPv4统计 → 源和目标,看到大量不同源IP |
| SYN包密集 | 短时间内大量SYN包,每秒数万甚至数百万 | IO图表显示SYN包速率异常高 |
| 无后续包 | 攻击者不回应SYN-ACK,没有第三次握手 | 过滤 tcp.flags.syn==1 and tcp.flags.ack==0,看不到对应的ACK |
| TCP选项异常 | 部分攻击工具会省略TCP选项或使用异常值 | 查看SYN包的TCP选项,可能为空或格式错误 |
| 窗口大小固定 | 伪造的SYN包窗口大小往往是一个固定值 | 所有SYN包的Window size值相同 |
说白了,你打开Wireshark,看到满屏的 [SYN] 包,而且每个包的源IP都不一样,目标端口都是同一个(比如80或443),那就八九不离十了。
3.3.2 如何用Wireshark快速确认
我个人习惯用三步法确认SYN洪泛:
- 看速率:Wireshark → 统计 → IO图表,选择
tcp.flags.syn==1 and tcp.flags.ack==0作为过滤条件。如果SYN包速率超过正常值的10倍以上,就要警惕了。 - 看源IP分布:统计 → 端点 → IPv4,按发送包数排序。如果前几个源IP的包数差不多,而且数量巨大,说明是伪造IP。
- 看握手完成率:用
tcp.flags.syn==1 and tcp.flags.ack==1过滤出SYN-ACK包,跟SYN包数量对比。正常情况SYN-ACK数量应该接近SYN数量。如果SYN-ACK数量远小于SYN,说明大量握手没完成。
3.3.3 SYN Cookie与抓包表现
很多系统会开启SYN Cookie来防御洪泛攻击。开启后,服务器不再为每个SYN分配半连接,而是把连接信息编码到SYN-ACK的序列号里。这样即使半连接队列满了,服务器也能正常响应。
开启SYN Cookie后,抓包会有什么变化?
- SYN-ACK包的序列号不再是0,而是一个经过特殊计算的值。
- Wireshark可能会显示
[TCP Cookie: Present]或类似提示。 - 如果攻击流量过大,SYN Cookie本身也会消耗CPU,抓包中可能看到SYN-ACK的响应延迟增加。
但注意,SYN Cookie不是万能的。它只能防御半连接耗尽,如果攻击流量大到把带宽打满,那什么机制都没用。
3.4 高并发握手优化建议
最后,基于抓包分析的结果,我给出几个实用的优化方向:
| 问题 | 抓包特征 | 优化方案 |
|---|---|---|
| 半连接队列溢出 | SYN重传,无SYN-ACK响应 | 增大 net.ipv4.tcp_max_syn_backlog,开启SYN Cookie |
| 全连接队列溢出 | 握手成功但立即RST | 增大 net.core.somaxconn,优化应用层 accept() 性能 |
| SYN洪泛攻击 | 大量SYN无ACK,源IP随机 | 开启SYN Cookie,配置防火墙限速,使用CDN/高防IP |
| 握手延迟高 | SYN到SYN-ACK间隔大 | 优化服务器CPU/内存,使用多队列网卡,调整RPS/RFS |
你想想看,这些优化其实都是基于抓包分析的结果。没有抓包,你根本不知道问题出在半连接还是全连接,也不知道是攻击还是正常流量。所以,抓包不是万能的,但不抓包是万万不能的。