TCP 三次握手(一次性讲透,面试版,结合工控Qt网络场景)
一句话本质
TCP 是可靠的、面向连接的协议。三次握手核心目的:双向确认收发能力,协商初始序列号ISN,建立连接。
简单大白话:
客户端 ↔ 服务端,要互相确认两件事:我能发消息给你,我也能收到你的消息。
一次握手只能单向验证,需要来回3次报文,完成双向确认。
角色约定:
• 客户端(Client):主动发起连接(比如你的Qt程序去连接工控设备服务端)
• 服务端(Server):监听端口,等待连接
📩三步完整流程
第1次握手:客户端 → 服务端 【SYN】
客户端:SYN报文,携带客户端初始序列号 ISN(c)
含义:客户端说:我想和你建立连接,我的消息编号从 ISN(c) 开始
客户端状态:CLOSED → SYN_SENT
服务端收到后状态:LISTEN → SYN_RCVD
SYN(Synchronize):同步序列号标志位
第2次握手:服务端 → 客户端 【SYN + ACK】
服务端回复一个报文同时带两个标记
-
SYN:服务端自己的初始序列号 ISN(s)
-
ACK:确认客户端的SYN,确认号 = ISN(c)+1
含义:服务端说:收到你的请求了!我这边也准备好,我的编号从ISN(s)开始;我确认收到了你第ISN(c)号包
✅到这里:服务端确认两件事:客户端能发消息给我、我能收到客户端消息
但是!客户端还不知道:服务端能不能收到我发的ACK,连接还不能建立。
第3次握手:客户端 → 服务端 【ACK】
客户端发送ACK报文,确认号 = ISN(s)+1
含义:客户端说:收到你的SYN消息,确认你的序列号。
✅收到这个ACK之后:
服务端:SYN_RCVD → ESTABLISHED(连接成功)
客户端收到服务端的SYN+ACK之后就已经进入 ESTABLISHED,发送完第三次ACK,连接双向就绪,可以收发业务数据。
👉重点:第三次ACK报文可以携带业务数据(部分操作系统支持),但是一般不会放;SYN报文不能携带数据
✅核心结论:为什么是3次,不是2次?(面试必考题)
假设只握手2次:
客户端发SYN → 服务端回复SYN+ACK,服务端单方面认为连接建立,直接准备资源。
会有经典的失效SYN报文问题:
网络延迟的旧连接请求,到达服务端,两次握手后服务端建立连接、分配内存;但是客户端早就放弃这个连接了,不会发数据。服务端白白占用端口和内存资源,造成资源浪费。
三次握手,必须客户端最后发ACK确认,服务端收到ACK才正式完成连接,规避这个问题。
一句话记:两次握手,服务端无法确认「客户端能不能收到我的消息」。
序列号 ISN 是干嘛的?
TCP是字节流,每个字节都有序列号:
-
用来排序:网络乱序到达的数据包,接收端按序列号重组
-
去重:网络重复投递的包,用序列号识别丢弃
初始ISN不是固定0,是随机生成,防止网络嗅探、序列号预测攻击。
🧩和你Qt工控开发相关的实际理解
Qt QTcpSocket:
-
socket->connectToHost() 底层就是发出第一次SYN;
-
服务端QTcpServer收到SYN,内核回复SYN+ACK(第二次握手);
-
客户端内核发出ACK(第三次握手);
-
三次握手全部完成后,Qt才会触发connected()信号。
注意:三次握手是操作系统内核TCP协议栈完成的,不是Qt应用层代码做的。你的Qt代码感知不到SYN报文,只能收到连接成功信号。
📌面试高频坑点
- Q:第三次握手丢包会怎么样?
A:服务端没收到ACK,不会进入ESTABLISHED,会超时重传【SYN+ACK】;如果多次重传失败,服务端释放资源。客户端这边已经认为连接建立了,后续发业务包,服务端会回复RST重置。
- Q:SYN、ACK是什么?
A:TCP头部的标志位,1表示开启。
• SYN=1:同步序列号,用于发起连接
• ACK=1:确认号有效,代表应答报文
- Q:三次握手可以传数据吗?
SY报文(1、2次握手)不允许携带数据;第三次握手ACK报文,协议允许携带业务数据,但是工程上一般不带。
配套对比:TCP四次挥手(顺带区分,面试经常一起问)
握手:建立连接,双向同步序列号,3次
挥手:断开连接,TCP是全双工,双方各自关闭发送通道,4次
极简记忆口诀
-
客:SYN,我要连你(1次)
-
服:SYN+ACK,收到!我也准备好(2次)
-
客:ACK,收到你的消息,连接就绪(3次)