聊一聊TCP:三次握手我背了100遍,TIME_WAIT还是把我问住了

聊一聊TCP:三次握手我背了100遍,TIME_WAIT还是把我问住了 一、为什么今天还要聊TCP说实话TCP这东西我面试前背过无数遍。三次握手、四次挥手、流量控制、拥塞控制……背得滚瓜烂熟。但真到线上出问题时才发现背过不代表懂过。比如有一次我们服务突然出现大量端口分配失败查了半天发现是TIME_WAIT状态太多了。还有一次客户端说“请求超时”抓包一看超时重传或RTO重传一直在发生。所以今天我决定把这些年踩过的TCP坑和自己重新理解过的特性好好捋一遍。不写得太教科书尽量讲清楚“为什么”。二、先看一眼TCP长什么样TCP的报文头其实不算复杂但有几个字段你最好记住因为后面大部分特性都靠它们序列号Seq解决乱序和重复的问题。每个字节都有自己的编号。确认号Ack告诉对方“我收到几号之前的所有数据了”。窗口大小Window告诉对方“我还能收多少你慢点”。标志位SYN用来建立连接ACK用来确认FIN用来关闭连接RST用来强行重置。你可以把TCP想象成一个带签收和编号的快递系统——IP层只管扔包裹TCP负责拼顺序、补丢件。三、三次握手不只是背个“SYN、SYNACK、ACK”握手的目的很简单让双方都确认彼此的收发能力是正常的。过程我就不重复默写了但我想聊聊两个被问烂的问题为什么是三次不是两次如果只有两次握手服务端发了SYNACK就认为连接建立了但万一这个SYN是很久之前延迟到达的旧包呢客户端早就忘记它了。这时候服务端会白白等一个不存在的连接。三次握手可以保证客户端确认了自己的ACK能被服务端收到历史连接不会意外建立。这是防止历史连接初始化的核心目的。SYN洪水攻击是怎么回事攻击者只发SYN不回复ACK。服务端每收到一个SYN就分配资源等待很快把半连接队列塞满。常规解法是SYN Cookie——不提前分配资源而是根据这个SYN算出一个cookie放在SYNACK里只有收到正确的ACK才真正建立连接。四、四次挥手TIME_WAIT是个好人挥手比握手复杂一点因为TCP是全双工的双方都要单独关闭自己的方向。客户端发FIN表示“我不再发数据了”但还可以收。服务端回ACK然后发自己的FIN。客户端最后回ACK。这里有一个经常被低估的状态TIME_WAIT。TIME_WAIT为什么要等2MSLMSL是报文最大生存时间。两个原因保证最后一个ACK能被服务端收到。如果ACK丢了服务端会重发FIN客户端还能再回应。让旧连接残留的包在网络上消失不会干扰新连接。五、可靠传输丢包了怎么办TCP的可靠性不是靠玄学是靠确认 重传。累积确认发送方不需要等每一个包的回复可以连续发一个窗口。接收方回复Ack100就表示“99号及之前全收到了”。这叫累积确认。重传策略超时重传RTO发出去一个包计时器到了还没收到ACK就重传。但RTO不能固定因为网络延迟在变。TCP会动态测量RTT然后计算RTO。这个机制的问题如果网络只是轻微丢包你要等一个RTO通常几百毫秒太慢了。于是有了快速重传当发送方连续收到3个相同的Ack比如三个Ack100就说明100号包丢了立即重传不等超时。再后来又有了SACK它解决了“我不知道到底丢了哪几个”的问题——可以在ACK里明确告诉对方“我缺了100到200之间的某些段”。六、流量控制你慢点我快接不住了流量控制解决的是接收方能力不足的问题。接收方把自己的剩余缓冲区大小放在Window字段里告诉发送方。发送方严格遵守这个窗口不能多发。如果窗口变成0呢发送方会定期发零窗口探测去探测窗口有没有打开防止死锁。还有一个经典问题糊涂窗口综合征。如果接收方每次只打开很小的窗口发送方就只发很少的数据效率极低。常见的解法是Nagle算法——把小包攒一下再发但注意在实时性要求高的场景比如游戏可能需要TCP_NODELAY。七、拥塞控制大家都慢点前面真的堵了流量控制是对端的问题拥塞控制是整个网络的问题。TCP假设丢包 网络拥塞。核心是四个算法慢启动刚开始不知道网络容量从一个小窗口开始指数增长直到遇到丢包或达到慢启动阈值。拥塞避免进入这个阶段后线性增长小心试探。快速重传 快速恢复发生快速重传后不回到慢启动而是把窗口减半继续拥塞避免。这个模型在当年是合理的但在高带宽低延迟的网络里等丢包再降速其实已经晚了。所以谷歌提出了BBR算法不再是“丢包驱动”而是测量实际带宽和RTT来主动调速。现在很多内核已经默认支持了。八、几个你一定会遇到的坑实战向1. CLOSE_WAIT 堆积CLOSE_WAIT出现在被动关闭方。如果它收到FIN后不回FIN就会卡在这个状态。绝大多数情况是代码忘了关socket。2. 粘包问题TCP是流式协议没有边界。你发了两个独立的包接收方可能一次读完。解法不靠TCP靠应用层固定长度、特殊分隔符、或者TLV或类型-长度-值格式。3. 如何快速看连接状态netstat -an | grep TIME_WAIT | wc -lss -state time-wait后者更快。九、总结一句人话TCP的核心哲学很简单牺牲一点实时性换来极高的可靠性。靠连接的建立与关闭、确认与重传、流量控制、拥塞控制这四个轮子跑起了整个互联网的可靠传输。当然它也有缺点——比如队头阻塞问题所以现在像QUIC这样的协议基于UDP实现TCP的可靠性正在变得流行。但对每一个后端开发者来说TCP依然是绕不开的一课。懂它不是为了背面试题而是为了在线上出问题时抓包能看出门道。