TCP面试高频考点全解析:三次握手、四次挥手与抓包实战

发布时间:2026/9/29 16:06:23
TCP面试高频考点全解析:三次握手、四次挥手与抓包实战 面试常问的TCP题目十次有八次开场就是“三次握手四次挥手”。但同样是背八股文有人只能复述“客户端发SYN、服务端回SYNACK、客户端再回ACK”有人却能顺着这个问题一路聊到SYN Flood、半连接队列、TIME_WAIT和粘包拆包最后还能掏出tcpdump抓包记录现场还原。差距就在“是什么”和“为什么”之间。这篇东西的目标是把TCP中级面试题里那些高频考点按“问题、原因、解决方案”串起来。不管是准备面试还是正在排查线上连接异常、写高并发网络服务都可以把这套思路当做一个检查清单来用。我会把热词里反复出现的知识点——三次握手、四次挥手、粘包、TCP与UDP区别、HTTP与TCP的关系、TCP/IP模型、WireShark抓包、MSS、端口、MODBUS TCP、LWIP这些场景——全部揉进对应的知识点里尽量讲清楚原理再给出能直接落地的工具和参数。1. 三次握手背得滚瓜烂熟一问“为什么”就卡壳1.1 握手三次的意义双方都要确认“我能发你也能收”先回归最基础的问题为什么TCP建立连接一定要三次用一句话讲握手的目的不是“建立连接”这个动作本身而是让通信双方都确认彼此的发送能力和接收能力正常。第一次客户端发SYN服务端收到后能确认客户端的发送能力OK、自己的接收能力OK。第二次服务端回SYNACK客户端收到后能确认服务端的发送能力OK、自己的接收能力OK。但此时服务端还有一个疑虑——自己发出的SYNACK客户端到底收到没有直到第三次客户端返回ACK服务端才能确定“哦客户端确实能收到我的数据”。所以从能力确认的角度看三次是必然的两次不够四次多余。面试官到这里一般不会轻易放你走他会追问一个经典场景如果是两次握手会怎样答案是会浪费服务端资源。比如客户端因为网络延迟导致一个旧的SYN报文先到达服务端服务端只要回一个SYNACK就认为连接建立了然后傻等客户端发数据可客户端根本不知道这回事。三次握手配合双方各自的状态翻转让这种情况能够被识别出来——客户端发现这不是它期望的应答序列直接发RST终止这个假连接。这也是为什么握手报文里的seq、ack序列号必须被严格核对的原因。1.2 SYN Flood与半连接队列面试官最爱问的进阶陷阱三次握手讲完紧接着就是攻击与防护。SYN Flood的原理特别简单攻击者伪造大量源IP给服务端发SYN服务端陆续回SYNACK之后因为永远等不到第三次ACK这些连接就一直挂在半连接队列里。队列一旦被撑满正常的客户端请求就无法再进入服务端表现为“端口能通但连不上或者连接建立极慢”。这里有个很容易被忽略的细节半连接队列和全连接队列是分开的。内核完成三次握手后连接从半连接队列SYN Queue移到全连接队列Accept Queue应用层调accept()后从全连接队列取出。队列容量受net.ipv4.tcp_max_syn_backlog和net.core.somaxconn共同限制而listen()时传入的backlog参数不能超过somaxconn否则会被内核自动截断。很多后端服务明明没崩溃却报出“accept queue full”就是这里出了问题。解决方案要看场景。如果只是偶然的瞬时高并发调大backlog配合应用层及时accept()就够了。如果真是SYN Flood那就得靠SYN Cookie机制半连接队列满时内核不再保存连接信息而是把连接信息编码进SYNACK的seq里等客户端ACK回来再重建相当于把存储换成了计算。Linux下默认是net.ipv4.tcp_syncookies1生产环境不建议关。1.3 用tcpdump亲眼看到三次握手面试中如果能把“我抓到过握手过程”这句话说出来是很大的加分项。实操非常简单在服务端机器上执行tcpdump -i eth0 tcp port 8080 -nn然后用另一台机器执行telnet或者curl访问这个端口。你会看到三行关键报文第一行是客户端的SYNflag标记为[S]第二行是服务端回应的SYNACK标记为[S.]注意这里有一个点表示ACK第三行是客户端的ACK标记为[.]。这三行报文就是完整的握手过程。我在实际项目里靠这个方法抓到过不少有意思的现象。印象最深的一次是客户端一直在发SYN服务端不回任何包抓包结果里只有单向的SYN不停重传。排查下来是服务端iptables规则里DROP了入方向的TCP包应用层日志完全没有报错服务看起来就是“启动正常但外部不通”。如果没有抓包光靠看日志根本定位不到这种问题。2. 四次挥手与TIME_WAIT为什么挥手要四次2MSL又是从哪来的2.1 四次挥手拆解每个FIN都要单独确认TCP连接关闭比建立更复杂因为连接是全双工的两条方向上的数据传输通道必须独立关闭。四次挥手的过程是主动关闭方发送FIN表示“我的数据发完了”被动关闭方收到后先回ACK表示“我收到了你的关闭请求”随后被动关闭方把自己剩余的数据发完再发送自己的FIN表示“我的数据也发完了”最后主动关闭方回ACK确认。所以四次不能省——中间被动方的FIN和ACK被拆成两个包因为被动方可能还有数据要发。面试里常被追问的是被动关闭方收到FIN后能不能把ACK和FIN合并成一个包如果它当时已经没有数据要发了理论上可以这也是某些实现里会出现三次挥手的情况。但标准流程还是四次因为对端无法知道你后面还有没有数据要发必须等你的业务层主动调用close()才能发出FIN。2.2 TIME_WAIT到底在等什么2MSL的真正用途主动关闭方在发送最后一个ACK后会进入TIME_WAIT状态并且要等待2MSLMaximum Segment Lifetime报文最大生存时间才能完全关闭。MSL一般取30秒到2分钟Linux下常见的是30秒或60秒所以TIME_WAIT通常会持续60秒到120秒。为什么等这么久两个原因。第一个是确保最后一个ACK能可靠到达。如果这个ACK丢了被动关闭方会超时重发FIN主动方必须还在TIME_WAIT状态里才能重新回应ACK。第二个是防止旧连接的报文在新连接里出现。如果主动方立刻关闭并迅速重用同一个四元组源IP、源端口、目的IP、目的端口网络中可能还有旧连接的延迟报文新连接收到这些残包会产生数据串扰。等2MSL可以让网络中所有旧报文都自然消亡。很多人在面试里会对TIME_WAIT的数量感到恐慌ss -s一查一堆TIME_WAIT挂在系统上是不是有问题事实上大量TIME_WAIT只代表这台机器主动关闭了大量连接它本身不是故障。高并发的短连接服务出现几万个TIME_WAIT很常见只要内存没被耗尽、新连接没有因为端口不够而失败都不需要过度处理。2.3 大量TIME_WAIT的清理方案如果TIME_WAIT确实影响到了业务比如客户端端口被占满导致无法发起新连接可以按优先级尝试下面几个方案改业务侧尽量用长连接复用。这是治本方案连接建立一次多次请求复用从源头上减少主动关闭次数。开启net.ipv4.tcp_tw_reuse。注意这个参数只在主动连接方有效也就是客户端角度它允许内核在安全条件下复用TIME_WAIT状态的连接。Linux文档说得很清楚配合tcp_timestamps开启后只有当新连接的起始时间戳大于该TIME_WAIT流中最近一次时间戳时才能复用所以是“安全复用”不是盲目重用。曾经有tcp_tw_recycle参数现在不要用了。它依赖时间戳判断在NAT网络环境下会导致不同客户端的时间戳被错误比较结果就是一部分用户连接被随机RST。新版本内核已经移除或不再建议开启面试里提一句“我不用它因为NAT环境下有坑”会很加分。调整临时端口范围。如果端口耗尽导致connect失败可以查看net.ipv4.ip_local_port_range比如把范围从默认的32768-60999调整成1024-65535给客户端更多可用端口。用sysctl配置时注意临时测试可以sysctl -w直接写但重启会失效。若要持久生效写入/etc/sysctl.conf然后sysctl -p加载。3. 粘包与拆包写网络服务最容易踩的坑3.1 粘包是怎么产生的没有消息边界只有字节流在外面面试十个写网络业务的至少六个会被问到粘包。粘包的背后是TCP的核心特性TCP是面向字节流的协议它不关心你一次write()了多少字节只保证字节顺序不变但不保证每次read()到的数据和你调用write()时的数据块一一对应。为什么会出现“一包变两包粘在一起”或者“一包被拆成两包”原因在两端都有。发送端有Nagle算法它会把多个小的数据包合并成一个TCP段发出如果业务层连续调用两次send()发送很小的数据内核可能把它们拼在一起发出。接收端也有延迟ACK机制加上TCP缓冲区会把多个到达的段拼接应用层read()时一次就读到了多块数据。另外TCP段的长度受MSS限制MSS的计算是MTU减去IP头20字节再减TCP头20字节标准1500字节MTU下通常是1460字节。超过MSS的数据必须拆成多个TCP段接收端再按buf大小聚合。所以粘包拆包不是bug是TCP的正常行为关键在于应用层必须自己定义“消息边界”。3.2 三个通用解决方案定长、分隔符、长度字段解决粘包思路就三条。最简单的是固定长度业务层约定每条消息都是N字节不够补零读满N字节就认为是一条完整消息。这个方案实现最省事但空间浪费大只适合字段结构固定的场景。第二种是分隔符比如用换行符\r\n作为消息结束标志。HTTP/1.x的报文头就是这么干的。但分隔符方案有个隐藏风险消息内容本身如果包含分隔符需要做转义而且每次都要扫描缓冲区找分隔符数据量大了会消耗CPU。第三种是长度字段也是实际项目里用得最多的方案。典型格式是“4字节包头长度 长度字段 载荷”。如果再加上协议版本、消息类型、CRC校验就是一个完整的企业级私有协议。接收端处理逻辑固定为先读4字节得到长度L再继续读L字节载荷读完再回到“读4字节长度”的状态。这样无论TCP怎么粘、怎么拆只要按这个状态机循环消息就不会错。我写C服务端时还会额外强调一点在真正的阻塞与非阻塞socket上多次read()返回的字节数都可能不足调用read()后必须把返回值与期望长度比较读不完要继续读不能想当然认为一次read()就拿到完整消息。这就是“半包”问题属于拆包的子集。下面给一个最简单的长度字段处理逻辑参考伪代码思路def read_packet(sock): header recv_exact(sock, 4) # 先读4字节长度头 length int.from_bytes(header, big) body recv_exact(sock, length) # 再读length字节正文 return body def recv_exact(sock, n): data b while len(data) n: chunk sock.recv(n - len(data)) if not chunk: raise ConnectionError(连接关闭) data chunk return data这段代码的核心就是“凑够n个字节再返回”不管底层怎么粘包拆包应用层都能得到一条完整的数据。这也是所有长度字段方案的根基。3.3 嵌入式场景补充LWIP、MODBUS TCP与简单设备通信搜索热词里有LWIP、W5500、ESP32、正点原子Nios TCP Server、Freemodbus TCP这些词。嵌入式网络开发和通用后端有个显著区别资源紧张不能动不动起线程和开大缓冲区但协议逻辑反而更简单——很多场景就是一个TCP Server和一个客户端做一问一答。嵌入式里解决粘包最优雅的现成协议就是MODBUS TCP。它的报文头MBAP自带长度字段前四个字节是事务标识符最后两个字节是后续字节数。所以用Freemodbus或者自己基于LWIP移植时只需要先收MBAP头解析出长度再收相应长度的PDU就能准确切割出完整报文。这也是MODBUS TCP从来不用考虑分隔符方案的原因。如果是裸写W5500或LWIP的TCP server我的建议是在驱动层之上加一个环形缓冲区读出来的数据全部先丢进环形缓冲然后在业务层按“包长度”解析。不要在中断回调或poll循环里直接按单次接收长度处理业务逻辑否则一旦收到半包后面所有报文都会解析错位。这类设备调试时手机装一个TCP调试助手ESP-01S发TCP消息到手机正好可以用来验证服务端的收发逻辑和拆包逻辑很方便。4. TCP和UDP、HTTP的关系协议族辨析题不能只背结论4.1 TCP/IP模型的分层思想数据在每一层都干了什么TCP/IP模型常被简化为四层应用层、传输层、网络层、网络接口层。面试里更有区分度的是能准确说出每层“解决什么问题”应用层负责语义比如HTTP的GET/POST、DNS的域名解析、SSH的远程登录传输层负责进程到进程的通信核心是端口号和可靠传输网络层负责主机到主机的寻址与路由核心是IP地址和路由表网络接口层负责在物理链路上传输数据帧核心是MAC地址和以太网帧。数据发送时是层层封装的应用层数据交给传输层加上TCP头变成TCP段再交给网络层加上IP头变成IP数据报再交给网络接口层加上以太网头尾变成帧。接收时反向拆封每层只处理自己关心的头。这个“封装-解封装”流程值得自己动手画一遍面试官如果让你描述“一个HTTP请求从浏览器到服务器的完整过程”本质就是在考这个。端口号在传输层的作用常被误解。IP负责把数据包送到主机端口负责把数据交给主机上哪个进程。同一个IP可以同时跑Web服务和数据库服务靠的就是TCP端口号做区分。这也是为什么“TCP端口号”和“UDP端口号”是两个独立的命名空间TCP 8080和UDP 8080可以同时被不同进程占用而不冲突。4.2 TCP与UDP区别一张表看清但更要讲清楚“场景取舍”TCP与UDP的区别几乎是必考题列清楚不难难的是能说明白每个区别背后的设计动机。对比维度TCPUDP连接性面向连接必须先三次握手无连接直接发数据可靠性可靠有确认、超时重传、乱序重排不可靠发完不管数据边界字节流无消息边界报文独立每个报文有边界传输效率相对低握手和确认开销大相对高无连接状态管理传输方式全双工流式数据报式典型场景文件传输、HTTP、数据库连接视频直播、语音通话、DNS查询、游戏位置同步面试官通常还会追问为什么视频通话这种对实时性要求高的场景反而用UDP因为TCP的重传机制在网络抖动时会增加延迟你宁可丢几帧画面也不希望因为等重传导致声音画面卡顿。但如果你在企业内网给一个设备做遥测数据采集数据不能丢也不能乱那就必须TCP。其实“哪个更好”不成立只有“哪个更匹配场景”。4.3 HTTP与TCP的关系HTTP不是另一种传输协议很多人把HTTP和TCP放在一起比较这是个误区。HTTP是应用层协议它在TCP之上依赖TCP的可靠传输。HTTP/1.0每请求建一次TCP连接性能差HTTP/1.1加Keep-Alive一个TCP连接可以连续发多个请求HTTP/2进一步在一个TCP连接上做多路复用多个请求交错传输但也带来了队头阻塞问题HTTP/3干脆换成基于UDP的QUIC协议就是为了避免TCP层面丢包重传导致整个连接阻塞。所以同一个TCP连接上可以跑很多HTTP请求但这不意味着HTTP和TCP是同层的东西。面试时一个清晰的表述是“HTTP关心的是请求和响应的格式、状态码、头字段这些语义TCP关心的是这些字节怎么可靠地、有序地、不丢不重地送到对端。”如果对方问“HTTP和TCP的区别”先纠正层级差异再讲演进历史基本就能拿满这个分数。5. 网络排查工具箱从现象到根因的实战思路5.1 连接建立不起来先看SYN有没有回声线上最常见的故障是“端口通不了”第一步永远先确认链路和端口状态。用telnet测试端口telnet 192.0.2.10 8080如果连接失败立刻在服务端抓包tcpdump -i eth0 tcp port 8080 -nn。如果抓包结果显示客户端SYN反复重传而服务端没有回应那就是典型的“服务端没人监听”或“中间设备丢包”。用ss -lntp看服务端口有没有LISTEN再用iptables -L -n看有没有DROP规则。另一种情况是三次握手成功了但数据发不出去。比如客户端发了一个很大的TCP段服务端一直重传但收不到ACK最后连接被RST。这种问题十有八九是MSS和MTU不匹配。TCP握手时会协商MSS但如果路径上有隧道或者调整过MTU协商出来的MSS大于路径实际能承载的大小且IP层的DF位设了1不允许分片包就会卡在链路中间。排查方法是抓包看“TCP segment of a merged pkts”“TCP Previous segment lost”这类提示或者用ping -M do -s 1472测一下路径MTU是多少。5.2 连接很慢、吞吐上不去窗口、RTT与重传数据连接能建起来但压测时吞吐量上不去这是另一类高频问题。不要一上来就怀疑应用代码先用ss -i查看当前连接的发送窗口、接收窗口和RTT。窗口太小会限制在途数据量一个直观的估算方式是最大吞吐约等于窗口大小除以RTT。比如RTT是50ms窗口是64KB那理论上限就是64KB/50ms约等于10Mbps不管带宽多大都跑不上去。所以大带宽长距离场景下调大TCP窗口通过net.ipv4.tcp_wmem、net.ipv4.tcp_rmem等参数比换服务器更有效。如果是带宽充足但重传率很高抓包看WireShark里大量TCP Retransmission标记就要怀疑链路质量或者网卡驱动问题了。我在一次排查里发现两台服务器之间的交换机开启了广播风暴pcap里看到大量重复ACK和重传网络利用率明明不高业务却频繁超时。这种问题用应用层日志根本看不出原因只有抓包才能看到“链路里有噪声”。Zabbix这种监控系统可以做更长期的观察。如果需要监控服务器的TCP连接数趋势可以配置agent执行ss -s统计把TIME_WAIT、ESTABLISHED数量作为item采集再设置触发器在连接数超过阈值时告警。做性能压测时也可以用iperf3分别测TCP和UDP的带宽基线确认物理链路本身没有问题再定位到业务层。5.3 连接无故断开保活机制与心跳的必要性服务端报“连接被对端重置”或者客户端报“远程主机强迫关闭”最常见的原因是中间设备空闲超时把连接给踢了。很多路由器、防火墙默认会把空闲一定时间的TCP会话从状态表里清掉这个时间长短不等可能是5分钟可能是30分钟。操作系统自带的TCP KeepAlive一般默认7200秒才发一次探测包根本挡不住中间设备的回收。成年人解决这类问题就两个手段一是业务层主动做心跳比如每30秒或60秒发一个心跳请求保证连接上有流量二是调低系统保活探测时间net.ipv4.tcp_keepalive_time从7200调到600配合tcp_keepalive_intvl和tcp_keepalive_probes。但如果走业务心跳记得把心跳包和业务消息区分开别让对端把心跳当成业务数据处理。这里又回到第三节粘包的问题如果你连消息边界都没定义清楚心跳和业务数据混在一起后面解析必然乱套。写在最后的实操体会面试和实际排查有一点是共通的把概念讲得再漂亮不如拿出一段真实抓包记录有说服力。我建议准备面试时花一个下午做三件事用tcpdump完整抓一次三次握手和四次挥手写一个带长度字段的TCP服务端和客户端故意在循环里发小包看粘包效果再用ss观察一次长连接断开前后的TCP状态变化。这三件事做完你对TCP的理解会明显上一个台阶。面试官问你的时候你脱口而出的不是教科书原文而是“我在现场看到的现象和当时的处理过程”这才是中级工程师该有的状态。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询