开篇暴击(建议收藏!)
各位老铁们!今天咱们要聊的这个知识点堪称程序员界的"驾照理论考试必考题"!不管是后端开发、网络运维还是安全攻防,只要你的工作要和网络打交道,TCP的三次握手与四次挥手绝对是你必须吃透的底层原理!(面试被问到的概率高达99.9%!)
先来点前菜(基础不牢地动山摇)
想象一下你在网购时疯狂点击"立即购买"按钮的场景——你每一次点击的背后,其实都是浏览器和服务器在进行复杂的"暗号对接"。这个对接过程用专业术语说就是TCP连接建立与断开,也就是咱们今天的主角:三次握手(建立连接)和四次挥手(断开连接)。
关键术语速记(重点标红!)
- SYN:我要和你搞对象(同步序列编号)
- ACK:我同意搞对象(确认应答)
- FIN:咱们分手吧(结束连接)
- SEQ:咱们的聊天暗号(序列号)
- MSL:分手冷静期(最大分段生存时间)
正片开始:三次握手的爱情故事
第一幕:客户端发起求爱(SYN=1)
当你的浏览器(客户端)想和淘宝服务器(服务端)建立连接时,会先发个"求爱信号":
SYN=1, seq=x
(这个x就像初次见面的自我介绍暗号)
第二幕:服务器的双向确认(SYN+ACK)
服务器收到求爱信号后,会给出双重回应:
SYN=1, ACK=1, seq=y, ack=x+1
(y是服务器的自我介绍暗号,x+1表示"我记住你的暗号了")
第三幕:最后的确认(ACK)
客户端收到回应后必须再确认一次:
ACK=1, seq=x+1, ack=y+1
(这里如果客户端不回应,服务器会像等不到回应的舔狗一样持续发送确认请求)
灵魂拷问:为什么非要三次?
很多萌新会问:两次确认不行吗?想象这个场景:
- 客户端发送SYN(信号1)
- 信号1卡在路由器里
- 客户端重发SYN(信号2)
- 服务器响应信号2建立连接
- 此时卡住的信号1突然到达服务器…
(恭喜你收获了一个僵尸连接!)
分手大戏:四次挥手的哲学
第一幕:客户端提分手(FIN=1)
当数据传输完毕,客户端会发送:
FIN=1, seq=u
(此时进入FIN-WAIT-1状态,就像说"我想分手"但还没正式分)
第二幕:服务器的及时回应(ACK)
服务器立即回应:
ACK=1, seq=v, ack=u+1
(进入CLOSE-WAIT状态,相当于说"我知道了"但自己可能还有话要说)
第三幕:服务器的最后告白(FIN)
当服务器处理完所有数据,才会发送:
FIN=1, ACK=1, seq=w, ack=u+1
(进入LAST-ACK状态,相当于"我也准备好了")
第四幕:客户端的最终确认(ACK)
客户端必须再发一次确认:
ACK=1, seq=u+1, ack=w+1
(进入TIME-WAIT状态,要等2MSL才彻底关闭)
死亡问答:为什么分手比牵手多一步?
因为TCP连接是全双工的!就像情侣分手:
- 你说要分手(FIN)
- 对方说知道了(ACK)
- 对方可能还有话说(数据传输)
- 对方也说要分手(FIN)
- 你最后确认(ACK)
实战踩坑记录(血泪教训!)
场景1:线上服务TIME_WAIT堆积
某次大促时我们的Nginx服务器出现大量TIME_WAIT连接,直接导致新连接无法建立。解决方案:
# 调整内核参数
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
(注意:这两个参数在NAT环境下可能有副作用!)
场景2:惊群效应导致握手失败
早期版本Tomcat的BIO模型在应对高并发连接时,三次握手的ACK包可能被多个线程争抢,解决方案:
// 使用NIO模型+多路复用器
ServerSocketChannel serverChannel = ServerSocketChannel.open();
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
高级玩家必备(面试加分期)
Q:SYN洪水攻击原理?
攻击者伪造大量SYN包但不完成握手,耗尽服务器资源。防御方案:
- SYN Cookie技术
- 限制半开连接数
Q:TIME_WAIT为什么是2MSL?
- 确保最后一个ACK能到达
- 让本连接持续时间内产生的所有报文都从网络中消失
Q:CLOSE_WAIT状态堆积怎么办?
大概率是代码bug!检查是否忘记关闭socket:
# 错误示范
s = socket.socket()
s.connect(...)
# 忘记s.close()
# 正确做法
with socket.socket() as s:
s.connect(...)
抓包实战演示(Wireshark警告!)
当我们用Wireshark抓取一个HTTP请求时:
- 过滤器输入
tcp.port == 80
- 找到三次握手的三次TCP报文
- 观察Flags字段的SYN/ACK变化
- 四次挥手同理(注意FIN标志位)
(小技巧:右键任意TCP包 -> Follow -> TCP Stream 可以直观看到完整会话)
终极总结(建议刻进DNA)
三次握手和四次挥手就像程序员的网络礼仪:
- 建立连接要谨慎(三次确认防错乱)
- 断开连接要体面(四次告别保周全)
- 状态转换要记牢(八种状态如数家珍)
- 参数调优要慎重(内核参数别乱改)
下次当你curl
一个接口或者axios.get()
一个API时,不妨在脑海里过一遍这些底层交互——这不仅是面试装逼的资本,更是定位网络问题的核武器!