一、当你在刷抖音时,后台发生了什么?
大家天天都在用手机上网(别装了,你现在就在看这篇文章对吧?),但有没有想过当你点击视频的瞬间,手机和服务器之间到底怎么建立连接的?这就是我们今天要聊的TCP三次握手的神奇之处!
举个现实例子:你和女神约咖啡厅见面:
- 你:“到星巴克了吗?”(SYN)
- 女神:“到了,你呢?”(SYN-ACK)
- 你:“我也到了!”(ACK)
这个对话过程是不是很像TCP连接建立?没有第三次确认的话,女神可能还在傻等(这就是为什么必须是三次握手的关键)!
二、三次握手拆解(看我这波操作)
(此处应有灵魂手绘示意图)
1. 第一次握手:SYN攻击的根源
客户端发送SYN包(seq=x),这时候服务器端必须准备好两样东西:
- 接收缓冲区
- 发送缓冲区
- 半连接队列(划重点!面试常考)
2. 第二次握手:服务器的双倍谨慎
服务器返回SYN-ACK包(seq=y, ack=x+1),这里有两个关键数值:
- 自己的初始序列号y
- 对客户端序列号x的确认
(这时候如果客户端掉线,服务器会重试5次,每次间隔时间翻倍,总耗时63秒!)
3. 第三次握手:最后的确认
客户端发送ACK包(ack=y+1),此时服务器才会:
- 把连接从半连接队列移到全连接队列
- 应用程序调用accept()读取连接
三、四次挥手:分手也要体面
分手步骤比建立连接多一步,为什么?因为TCP连接是全双工的,必须双方都确认关闭:
经典分手四部曲:
- 客户端:“我要走了”(FIN)
- 服务器:“知道了”(ACK)
- 服务器:“我也要走了”(FIN)
- 客户端:“拜拜”(ACK)
必须注意的TIME_WAIT状态!!!
客户端在发送最后ACK后会进入TIME_WAIT状态,持续2MSL(Maximum Segment Lifetime)时间,这是为了:
- 确保最后一个ACK能到达
- 让旧连接的包在网络中消失(防止端口复用导致数据混乱)
(这个状态会占用端口号,高并发服务器要特别注意!)
四、面试官最爱问的5大坑题
Q1:为什么是三次握手不是两次?
如果只有两次握手,服务器无法确认客户端是否收到自己的SYN-ACK,可能导致:
- 服务器资源浪费(维护无效连接)
- 历史连接干扰(旧SYN包导致新连接混乱)
Q2:四次挥手能变成三次吗?
可以!当服务器收到FIN后,如果已经没有数据要发送,会把FIN和ACK合并发送,这就是"延迟确认"机制(但本质上还是四次的过程)
Q3:CLOSE_WAIT状态过多怎么办?
这是典型的应用层bug!说明服务器没有正确调用close(),常见于:
- 忘记关闭文件描述符
- 代码异常未执行关闭逻辑
- 连接泄露
Q4:SYN Flood攻击原理?
攻击者疯狂发送第一次握手包但不完成后续步骤,耗尽服务器的半连接队列。防御方案:
- 开启tcp_syncookies
- 调整半连接队列长度
- 减少SYN+ACK重试次数
Q5:TIME_WAIT状态有什么危害?
- 占用端口资源(默认可用端口约28000个)
- 可能导致新连接建立失败
解决方案:
# 调整内核参数
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
五、真实开发中的血泪教训
去年我们线上服务突然出现大量连接超时,排查后发现是因为Nginx的keepalive_timeout设置过长,导致大量连接堆积在TIME_WAIT状态。调整策略后:
- 将超时时间从300s改为60s
- 开启tcp_tw_reuse
- 增加本地端口范围
立即解决了问题!这个案例告诉我们:光懂理论不够,必须结合系统调优才能真正解决问题。
六、抓包实战分析(Wireshark演示)
用Wireshark抓取一个HTTP请求,你会看到:
- 前三个包是三次握手(SYN->SYN-ACK->ACK)
- 中间是HTTP数据交互
- 最后四个包是四次挥手
重点关注Sequence Number和ACK Number的变化规律,这是理解TCP可靠传输的关键!
七、总结与思考
最后给大家留个思考题:如果网络延迟非常大(比如卫星通信),三次握手会有什么问题?该如何优化?(提示:TCP快速打开机制)
理解三次握手和四次挥手,不仅是面试需要,更是我们处理网络问题的基础。下次遇到Connection timeout之类的报错,希望你能快速定位到是握手阶段还是挥手阶段的问题!