新闻详情

新闻详情

首页 / 资讯中心 / 详情

TCP三次握手与四次挥手:原理、状态与tcpdump排障实战

发布时间:2026/9/28 5:42:50来源:尧图网络
TCP三次握手与四次挥手:原理、状态与tcpdump排障实战
TCP 的三次握手与四次挥手你真的理解了吗半夜两点被电话叫醒说线上一个核心接口出现偶发的 3 秒超时。我爬起来连上跳板机tcpdump 一抓发现客户端连着发了三个 SYN等了大半天才收到服务端的 SYNACK——典型的 SYN 重传超时。那一刻我意识到平时背得滚瓜烂熟的三次握手真到排查问题的时候如果只记得一句客户端发 SYN服务端回 SYNACK客户端再回 ACK是完全不够用的。这篇内容想做的事不是再给你复述一遍教科书里的流程图而是站在实际使用的角度把 TCP 三次握手和四次挥手背后所有的为什么掰开揉碎讲清楚为什么是三次而不是两次或四次序列号到底怎么算挥手为什么要挥四次TIME_WAIT 和 CLOSE_WAIT 为什么是线上服务的头号杀手最后再带你把 tcpdump 抓包和线上异常排查串一遍。适合刚入门网络编程的后端工程师、运维同学以及面试前想把 TCP 这块彻底弄明白的人。1. 三次握手为什么必须是三次两次会踩的坑和四次的多余从我这些年排查连接问题的经验看很多人对三次握手的理解停留在背步骤阶段根本答不上来为什么非得三次。这个问题想通了TCP 设计哲学里一半的可靠性思想你就懂了。1.1 如果没有第三次握手一个迟到十年的包就能搞崩连接三次握手设计的核心动因是处理网络中滞留的过期报文。这里讲一个最经典的场景客户端 A 想连接服务端 B发出了一个 SYN 报文。很不凑巧这个报文在网络上绕了一圈迟到了很久才到达 B。这期间 A 已经放弃了这次连接或者已经关闭了 socket。如果采用两次握手——A 发 SYNB 回复 SYNACK 就算建立连接——那么 B 收到这个迟到的 SYN 后会误以为 A 想建立一条新连接于是回复 SYNACK然后开始为这条连接分配内核资源、置为 ESTABLISHED 状态满心期待地等 A 发数据。但 A 这边压根没有这条连接。B 傻等半天等不到数据资源却被白白占用。更要命的是如果这个迟到的 SYN 是旧连接的重传序列号可能和当前连接冲突导致数据错乱。三次握手就是为了解决这个过期报文导致错误建连的问题B 发出 SYNACK 后必须等 A 再回一个 ACK收到 ACK 才证明 A 确实在线、确实想要建这条连接而不是一个残留的报文在捣乱。1.2 三次握手的本质是确认双方的收发能力再往深挖一层TCP 是全双工协议数据是双向流动的。A 向 B 发数据的前提是B 能收B 向 A 发数据的前提是A 能收。三次握手实际上做的是收发能力的逐级确认第一次握手A 发 SYNB 收到后B 知道了A 能发、B 能收。第二次握手B 回 SYNACKA 收到后A 知道了B 能收我的 SYN 到了、B 也能发它回包了。第三次握手A 回 ACKB 收到后B 知道了A 能收我的 SYNACK 到了。这样双方都确认了对方的收发能力和自己的收发能力连接才敢真正开始传数据。注意第二次握手是被逼无奈的一肩挑它既是对第一次握手的确认ACK 角色又是发起反向连接请求SYN 角色。如果拆成四次里面还有一次是多余的——所以 TCP 把这两个合并成一个报文这也是三次不是四次的根本原因。1.3 一个生活中的类比想象两个人打电话A 想确认通话链路通畅。A 问听得见吗B 答听得见你听得见我吗A 再回听得见。这三句话就是完美的最小闭环多了浪费口水少了无法确认。如果只问不听A 根本不知道 B 在不在如果双方同时只说不听那谁也不知道对方到底有没有收到。提示面试被问到能不能把三次握手改成两次的时候从过期报文和能力确认两个角度回答基本就稳了。前者讲连接错误建立的危害后者讲确认链路的完整性需求。1.4 顺带澄清一个细节第三次握手能不能携带数据关于三次握手还有一个容易被人忽略的细节第三次握手是允许携带数据的。因为第三次握手发出时客户端已经收到了服务端的 SYNACK此时客户端知道链路通畅携带数据不会因为 SYN 重传导致数据重复。而第一次和第二次握手都不允许携带数据因为如果 SYN 超时重传数据会跟着重复发送接收方无法区分新旧。这个小知识点在很多高并发优化场景比如 HTTP 的 Fast Open里都有延伸理解了这一点后续看相关内容会轻松很多。2. 三次握手不止是点头问好报文细节与状态机逐段拆解明白了为什么是三次我们来看三次握手真正发生的时候报文长什么样、序列号是怎么滚动的、两端的内核状态是怎么迁移的。这部分我尽量还原得细一些因为很多人抓了包却看不懂包里的 seq 和 ack 是怎么算出来的。2.1 先看一组真实的握手报文在一台机器上用 tcpdump 抓包过滤条件tcp port 8080你大概会看到这样的输出tcpdump -i eth0 -nn tcp port 8080输出里和握手相关的部分通常长这样我简化了时间戳和 IPIP 10.0.0.1.50000 10.0.0.2.8080: Flags [S], seq 8000, win 64240, options [mss 1460,sackOK,TS val 123 echo 0], length 0 IP 10.0.0.2.8080 10.0.0.1.50000: Flags [S.], seq 15000, ack 8001, win 65535, options [mss 1400,sackOK,TS val 456 echo 123], length 0 IP 10.0.0.1.50000 10.0.0.2.8080: Flags [.], ack 15001, win 64240, length 0这三次交换就是握手的全部。注意几个关键字段Flags [S]表示 SYN 报文Flags [S.]表示 SYNACK点号代表 ACKFlags [.]表示纯 ACK。第一次客户端 seq8000这是客户端随机生成的初始序列号ISN。第二次服务端 seq15000这也是它自己的初始序列号ack8001 表示我收到了你的 seq 8000下一个请从 8001 开始发。第三次客户端 seq8001ack15001 表示我收到了你的 seq 15000下一个请从 15001 开始发。这里有个易错点SYN 报文本身要消耗一个序列号。所以客户端发了 seq8000 后虽然没带任何数据但下一个报文的序列号就变成了 8001。同理服务端也是 15000 变成 15001。很多人抓包后对着 ack 数字发懵就是没算上 SYN 这个虚拟字节。2.2 为什么初始序列号必须随机初始序列号ISN不是一个固定值。早期协议实现里ISN 从一个固定值开始递增后来发现这会导致严重的安全问题——攻击者可以猜测序列号伪造 RST 报文切断别人的 TCP 连接这就是经典的 TCP 会话劫持。现在的实现中 ISN 是随机生成的配合时间戳选项还能较好地防止序列号回绕。这也意味着如果你用 strace 观察两次连接的初始序列号它们几乎不可能相同。2.3 握手过程中的状态迁移三次握手涉及的状态不多但每一端的状态跳转很容易记混我把它们整理成了一张表配合这张表再去看ss -ant的输出会清楚很多阶段客户端状态服务端状态报文内容建立前CLOSEDLISTEN服务端提前 bindlisten第一次握手发送 SYN 后进入 SYN_SENT收到 SYN 后从 LISTEN 进入 SYN_RCVDSYN, seq8000第二次握手收到 SYNACK 前仍在 SYN_SENTSYN_RCVD发送后等待 ACKSYNACK, seq15000, ack8001第三次握手收到 SYNACK 后进入 ESTABLISHED收到 ACK 后进入 ESTABLISHEDACK, seq8001, ack15001虽然三次握手在逻辑上分三步但状态迁移有一个时间差客户端在发出第三次握手的同时已经进入了 ESTABLISHED而服务端要等收到第三次握手之后才进入 ESTABLISHED。这个时间差在生产里有实际意义客户端认为连接可用立刻开始发数据服务端可能还在 SYN_RCVD 里做着校验然后就面临半连接队列满、握手包被内核丢弃等问题。这个我们留到第 5 章展开。2.4 握手时协商的隐形参数三次握手除了建立连接还在讨价还价。第二次握手的 options 字段里通常会包含几项关键参数MSS最大报文段大小双方告知对方自己能接受的最大单包长度。比如服务端回mss 1400意思是超过 1400 字节的 TCP 数据段请拆开发。双方取较小值作为后续发送的依据可以有效避免 IP 层分片。Window Scale窗口缩放因子默认 TCP 窗口最大值是 64KB通过缩放因子可以把收发窗口扩大到 GB 级。这个参数如果协商失败高带宽下吞吐会很难看。SACK选择性确认允许接收方只确认收到的连续段丢包恢复效率更高。Timestamp时间戳用来精确计算 RTT同时能在序列号回绕时提供保护。这些参数协商完毕后才真正确定了这条连接后续的传输行为。所以不要小看三次握手它相当于两个系统在正式签合同之前先把合同条款逐条对齐了。3. 四次挥手拆开来看半关闭机制与两条独立通道的关闭如果说三次握手大家还能背个大概四次挥手真正能讲明白的人就少了一大截。最典型的问题是为什么挥手要四次握手才三次答案其实藏在一个词里半关闭。3.1 TCP 的双通道模型决定了关闭要分两次TCP 是全双工协议一条 TCP 连接可以看作客户端到服务端和服务端到客户端两条独立的数据通道。握手的时候一个 SYNACK 能同时打开两条通道。但关闭的时候两条通道的方向相反你没法用一个报文同时关闭两条通道——A 不再给 B 发数据了不代表 B 不能再给 A 发数据。这就要引入一个机制半关闭。TCP 允许一方先关闭自己的发送通道另一方仍然保留发送能力直到它确定自己也没有数据要发了才关闭自己的通道。体现在报文上就是每一方都单独发一个 FIN收到对端 FIN 后再回一个 ACK。所以完整关闭一条连接最少需要四个报文。3.2 四次挥手的完整时序和状态变化假设客户端主动发起关闭实际业务里不一定谁先调 close 谁就是主动方第一次挥手客户端发 FINseq9001然后进入 FIN_WAIT_1。这个 FIN 表示我的数据发完了不再向服务端发数据了。第二次挥手服务端收到 FIN 后回 ACKack9002然后进入 CLOSE_WAIT。这个 ACK 只是告诉客户端你的 FIN 我收到了但服务端可能还有数据要发给客户端所以此时关闭动作暂停。第三次挥手等服务端把剩余数据发完调 close()发出自己的 FINseq16001进入 LAST_ACK。这个 FIN 表示我的数据也发完了可以关闭了。第四次挥手客户端收到服务端的 FIN回最后一个 ACKack16002然后进入 TIME_WAIT。服务端收到这个 ACK 后进入 CLOSED。阶段主动关闭方状态被动关闭方状态报文内容第一次挥手FIN_WAIT_1ESTABLISHED收到后进入 CLOSE_WAITFIN, seq9001第二次挥手FIN_WAIT_2CLOSE_WAIT等待应用 closeACK, ack9002第三次挥手FIN_WAIT_2收到后进入 TIME_WAITLAST_ACK发出后等待 ACKFIN, seq16001第四次挥手TIME_WAIT等待 2MSL 后 CLOSEDCLOSEDACK, ack160023.3 为什么第二次和第三次挥手之间经常有停顿很多人抓包的时候会发现第二次挥手和第三次挥手之间往往隔着一段时间有时候甚至相隔几十毫秒甚至几秒。原因是服务端收到 FIN 后立即回了 ACK但它自己的业务代码可能还没处理完进程还有数据没发送完毕只有等它把数据全部发完、显式调用 close() 时内核才会发出 FIN。这段停顿在应用层就体现为一个很微妙的状态服务端处于 CLOSE_WAIT它还可以继续往客户端发数据。所以在双通道模型里客户端发完 FIN 后进入 FIN_WAIT_2并不是关死了它仍然要读服务端发来的数据。这也是为什么 FIN_WAIT_2 状态在应用层排查时经常成为服务端不主动关连接的凶手——如果服务端一直不调 close客户端可能长期挂在 FIN_WAIT_2 上。3.4 谁先挥手谁就承担更多责任主动关闭方在第四次挥手后会进入 TIME_WAIT 状态要等大约 2 倍的报文最大生存时间MSL才会彻底关闭一般是 60 秒。可以这么理解既然是你主动提的分手那你就得负责把最后一段分手把话说完的 ACK 送到底还得替这段关系可能残留的网络报文善后。被动关闭方则相对轻松收到最后一个 ACK 后直接进入 CLOSED 完事。这也是高并发场景下谁主动关闭连接谁倒霉的由来——服务器如果频繁主动关闭客户端连接TIME_WAIT 会在服务器上大量堆积。关于这个我放到下一节详细讲。4. TIME_WAIT 与 CLOSE_WAIT 堆积线上连接故障的两大元凶前面讲完了握手和挥手的正常流程从这一章开始进入实战区。在真实的线上环境里TCP 连接异常八成出在两个状态上TIME_WAIT 堆积和 CLOSE_WAIT 堆积。这两个状态代表了完全不同的两类问题——前者是内核协议栈的正常行为后者几乎都是应用层代码的 bug。4.1 TIME_WAIT 为什么要等 2MSLTIME_WAIT 存在的理由有两个都相当硬核第一确保最后一个 ACK 能送达。假设客户端发出的第四次挥手 ACK 在网络中丢包了服务端在 LAST_ACK 状态下迟迟等不到确认就会超时重传 FIN。如果客户端发完 ACK 立刻进入 CLOSED它就无法响应服务端重传的 FIN服务端会一直卡在 LAST_ACK最后连接被超时强行断开。TIME_WAIT 提供了一个窗口让客户端能接收并重答这个 FIN。第二让旧连接的滞留有包自然消亡。网络上可能还存在这条连接在生命周期内发出的旧数据包。如果同样四元组的新连接建立得太快旧包可能混入新连接造成数据错乱。等 2MSL 的目的就是保证这些迟到的报文在网络中全部消失新连接不会被旧报文污染。4.2 TIME_WAIT 过多的典型场景和调优思路TIME_WAIT 过多通常出现在高并发短连接场景。比如一台服务器对外提供 APIQPS 很高客户端频繁短连后主动断开服务器上就会积累大量 TIME_WAIT。每个 TIME_WAIT 连接都占用一个四元组源 IP、源端口、目标 IP、目标端口端口耗尽后新连接无法建立表现为Address already in use或者 connect 超时。遇到这种情况我建议按顺序排查和处理先量化一下现状ss -ant | grep TIME_WAIT | wc -l看看数量级对比sysctl net.ipv4.ip_local_port_range给出的可用端口范围。如果确实是 TIME_WAIT 过多先看能不能从源头减少改用长连接、连接池复用的思路这永远是最优解。内核参数调优作为辅助手段。Linux 下有几个参数需要了解参数默认值说明net.ipv4.tcp_fin_timeout60TIME_WAIT 在 Linux 上的实际保持时间可适度调小net.ipv4.tcp_tw_reuse0允许将 TIME_WAIT 连接复用给新的连接需要开启时间戳net.ipv4.ip_local_port_range32768-60999本地可用端口范围可以适当扩大注意内核较新版本中tcp_tw_recycle已经被移除早期文章常推荐的开启它很容易在 NAT 环境下出问题因为 NAT 后面的多个设备共享一个 IP时间戳校验会误杀合法连接。现在网上还能看到很多老文章推荐配这个参数建议直接忽略。4.3 CLOSE_WAIT 堆积本质是代码 bug如果说 TIME_WAIT 多还可以用业务特性如此来解释CLOSE_WAIT 堆积就完全是另一回事了。CLOSE_WAIT 的意思是对端已经发了 FIN我回了 ACK但我自己的应用程序一直没有调用 close() 把这条连接关掉。我排查过一个典型的 Java 服务 CLOSE_WAIT 堆积案例某内部系统的接口隔几天就假死用ss -antp一看CLOSE_WAIT 好几千。顺藤摸瓜发现是调用外部 HTTP 接口时用了不带超时和连接释放逻辑的 HTTP 客户端对端响应慢或提前断开后连接没有被正确关闭。这就是典型的资源泄漏只不过泄漏的是 socket 文件描述符和内核连接资源。排查 CLOSE_WAIT 堆积有一套固定的套路# 1. 统计 CLOSE_WAIT 数量 ss -ant | grep CLOSE_WAIT | wc -l # 2. 查看这些连接属于哪个进程 ss -antp | grep CLOSE_WAIT拿到进程号和连接信息后再去查应用日志、线程堆栈重点看三类代码HTTP 连接是否设置了连接池和超时、数据库连接是否用完就 close、网络 IO 的异常分支里是否忘了关闭 socket。CLOSE_WAIT 是一个不需要调内核参数的故障因为它唯一的解法就是在应用层正确释放连接。4.4 从状态数量判断问题性质根据我的经验看到ss -ant的输出后先别看细节先看状态分布TIME_WAIT 多属于协议栈正常行为 连接频繁建立的业务结果可以从连接复用和端口范围下手。CLOSE_WAIT 多几乎必然是应用层 bug优先排查代码而不是改内核参数。SYN_RCVD 多服务端收到了 SYN 但握手第三步没完成可能被攻击也可能半连接队列满了。FIN_WAIT_1 / FIN_WAIT_2 多主动关闭方在等对端的 ACK/FIN通常和对端处理慢、不关连接有关。这个先看状态分布再定排查方向的习惯比上来就抓包有效得多。5. 从抓包到定位握手挥手的实弹演练与异常排查最后一章带你把前面讲的东西落到实操里。网络问题排查里最有用的工具还是 tcpdump 和 Wireshark亲手抓一次握手和挥手比背十遍状态迁移图都管用。5.1 用 tcpdump 完整抓一次握手和挥手先起一个最简单的 TCP 服务然后从另一台机器连上去抓包看全过程。抓包命令这样写# 监听 eth0 网卡抓 8080 端口的 TCP 报文并写入文件 tcpdump -i eth0 -nn tcp port 8080 -w tcp_handshake.pcap如果是本机回环测试注意要抓 lo 接口tcpdump -i lo -nn tcp port 8080 -w tcp_local.pcap抓完用 Wireshark 打开文件过滤tcp.flags.syn 1 || tcp.flags.fin 1你就能看到完整的握手和挥手流程。这时候对照第 2 章和第 3 章的表格把每个报文的 seq、ack、flags 都标一遍基本就打通了。5.2 最常见的握手异常SYN 重传客户端发 SYN 后迟迟收不到 SYNACK会进入重传。默认net.ipv4.tcp_syn_retries是 6 次每次退避翻倍总耗时可能达到一两分钟。抓包时你会看到同一个 seq 的 SYN 发了多次中间没有 SYNACK 回应。SYN 重传的常见原因有服务端 accept 队列半连接队列已满、防火墙丢包、服务端负载过高来不及回包。排查时先netstat -s | grep -i syn看 SYN 溢出的计数再用ss -lnt看监听队列的 Send-Q 和 Recv-Q基本就能定位。5.3 连接被 RST 重置的几个典型场景RST 报文是 TCP 用来强制终止连接的信号出现 RST 通常意味着连接已经无法正常继续。常见场景连接了没有监听的端口对端会直接回 RST 而不是 FIN。应用层协议错误比如 HTTP 请求格式不对服务端解析失败直接断开。socket 超时被内核强制关闭应用没来得及处理数据内核发送 RST。安全设备或中间防火墙主动 reset有些安全策略会直接向两侧发 RST。抓包时看到 RST先确认它是哪一端发出的再去对应端看日志。RST 也是面试常考点能说清为什么 RST 不需要进 TIME_WAIT因为 RST 的语义就是立即放弃连接不需要确认也没有需要清场的残留数据会加分不少。5.4 一些实用抓包经验写到这里我想把抓包和排查中积累的零散经验集中分享下不要在服务器上长期跑 tcpdump尤其高峰时段抓包本身有开销。用-c 100限定包数量或者抓完立即分析完就关。过滤条件越精确越好。只需要关注握手挥手时过滤tcp[tcpflags] (tcp-syn|tcp-fin) ! 0只需要关注某个 IP 时直接用host 1.2.3.4。本机抓包容易踩坑没开 Npcap 的抓包选项时Wireshark 可能抓不到 loopback 流量有的系统 tcpdump 在 lo 接口上默认抓包会丢很多包。抓包前先确认网卡名ip addr看一下别在错误的网卡上白等半天。一定要看时序分析的顺序是时间线 - 标志位 - 序列号 - 重传/乱序。很多人一上来就看 payload反而忽略了连接建立和断开过程中的问题。这些看起来零散但真正在线上排障的时候每一条都能帮你省下不少时间。我个人一直建议每个写网络代码的人都要亲手抓一次本机回环的握手包。等你能不看任何文档指着 Wireshark 里每一行报文说出这是第几次握手、序列号为什么这么滚动、下一个包该是什么的时候TCP 在你眼里就不再是一张需要背诵的流程图了。往后的调优也好、排障也好你都比别人多想一层——你看到的不是一个孤立的现象而是一整套有因果、有逻辑、有妥协的传输系统。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

做一个小说网站需要多少钱?别被坑,教你怎么选不挂马 2026/9/28 6:35:33

做一个小说网站需要多少钱?别被坑,教你怎么选不挂马

做一个小说网站需要多少钱?别被坑,教你怎么选不挂马 昨晚刚给一个做网文平台的客户做完代码审计,心有余悸。他们那个站被黑得稀碎,首页挂满了赌博链接,后台数据库被拖了个底朝天,最要命的是用户支付记录全泄露了。客户急得跳脚,问我为什么之前没发现,…

阅读更多 →
拒绝踩坑:ccg搭建wordpress从零搭建实战指南 2026/9/28 6:35:32

拒绝踩坑:ccg搭建wordpress从零搭建实战指南

拒绝踩坑:ccg搭建wordpress从零搭建实战指南 域名买错了?服务器配置全乱?很多项目经理在接手“ccg搭建wordpress”这种需求时,第一反应是头大。明明只是装个博客或官网,怎么搞出这么高的门槛?其实, 域名服务器搞不懂…

阅读更多 →
3步搞定网上做期末试卷的网站完整流程 2026/9/28 6:35:32

3步搞定网上做期末试卷的网站完整流程

3步搞定网上做期末试卷的网站完整流程 找建站公司怕被坑高价?别急,今天直接拆解网上做期末试卷的网站完整流程。很多教育创业者卡在技术选型和成本上,其实核心就三点:需求精准、技术轻量、合规先行。 网上做期末试卷的网站真的需要重型开发吗…

阅读更多 →
手把手设计锂电池主动均衡电路:反激变压器与MOSFET驱动实战 2026/9/28 6:35:25

手把手设计锂电池主动均衡电路:反激变压器与MOSFET驱动实战

手把手设计锂电池主动均衡电路:从反激变压器选型到MOSFET驱动实战锂电包用久了最头疼的问题,就是电芯之间电压越来越不齐。容量好的电芯冲到4.2V还在出力,容量差的早早就顶到保护电压,整组电池只能用一半就喊饿。我以前组过几组48…

阅读更多 →
CPU实时口罩人脸检测系统:双模型协同与工业级部署实践 2026/9/28 6:35:25

CPU实时口罩人脸检测系统:双模型协同与工业级部署实践

简介:本资源是一套基于Python与深度学习技术实现的口罩佩戴检测与人脸识别双任务系统,面向计算机、电子信息及人工智能相关专业的本科生与研究生,适用于课程设计、期末大作业及高分毕业设计参考。项目采用PyramidBox Lite与RetinaFace等轻量级…

阅读更多 →
STM32H743 USB BULK传输实战:CubeMX配置与libusb上位机通信 2026/9/28 6:35:25

STM32H743 USB BULK传输实战:CubeMX配置与libusb上位机通信

做嵌入式玩到USB这一层,很多人第一反应是“调通就行”,但真正跑BULK传输时,描述符、端点、时钟、驱动,哪个环节掉了链子都能让你卡上一整天。我之前在一个数据采集项目里,需要把STM32H743采集到的传感器数据以较大吞吐…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉