TCP 连接管理 之 三次握手详解
(一)TCP三次握手详细过程及状态变化
1. 第一次握手(客户端 → 服务器)
- 报文标志位:
SYN=1
(同步序列号),ACK=0
(首次握手无确认) - 序列号:客户端随机生成初始序列号(例如
seq=x
) - 过程:客户端发送
SYN
报文,进入SYN_SENT
状态 - 状态变化:
- 客户端:
CLOSED
→SYN_SENT
- 服务器:
LISTEN
(等待连接)
- 客户端:
2. 第二次握手(服务器 → 客户端)
- 报文标志位:
SYN=1
,ACK=1
(确认客户端的SYN) - 序列号:服务器随机生成初始序列号(例如
seq=y
),确认号为ack=x+1
(第一次握手不携带数据,但SYN=1
会消耗一个序列号) - 过程:服务器发送
SYN+ACK
报文,进入SYN_RCVD
状态 - 状态变化:
- 服务器:
LISTEN
→SYN_RCVD
- 客户端:保持
SYN_SENT
- 服务器:
3. 第三次握手(客户端 → 服务器)
- 报文标志位:
ACK=1
,SYN=0
(非同步) - 序列号:
seq=x+1
(收到的确认号),确认号为ack=y+1
(第二次握手不携带数据,但SYN=1
会消耗一个序列号) - 过程:客户端发送
ACK
报文,双方进入ESTABLISHED
状态 - 状态变化:
- 客户端:
SYN_SENT
→ESTABLISHED
- 服务器:
SYN_RCVD
→ESTABLISHED
- 客户端:
(二)连接建立后数据传输的序列号规则
1. 客户端先发送数据
- 客户端发送数据:
seq=x+1
(第三次握手没有数据部分,沿用第三次握手的序列号)ack=y+1
(保持对服务器序列号的确认)
- 服务器响应ACK:
seq=y+1
(服务器初始序列号+1)ack=x+1+数据长度
(确认客户端数据)
2. 服务器先推送数据
- 服务器发送数据:
seq=y+1
(握手最后使用的序列号)ack=x+1
(第三次握手没有数据部分,保持对客户端序列号的确认)
- 客户端响应ACK:
seq=x+1
(客户端初始序列号+1)ack=y+1+数据长度
(确认服务器数据)
3. 关键点总结
- 初始序列号:双方随机生成,防止历史连接冲突。
- ACK确认机制:总是对已接收数据长度+1进行确认(期待下一个字节的序列号)。
- 数据传输:先发送数据的一方使用自己最后一次ACK的序列号作为起始值。
(三)TCP三次握手服务端资源建立的详细过程
1. 服务端内核在第一次握手(收到客户端的SYN
报文)时为客户端生成套接字
2. 详细过程分析
① 第一次握手前(服务端状态:LISTEN
)
- 服务端调用
listen()
后,进入LISTEN
状态,此时仅有一个监听套接字(Listening Socket),用于接收所有客户端的连接请求。 - 监听套接字不直接关联具体客户端,而是等待
SYN
报文触发新连接。
② 第一次握手(客户端发送SYN
,服务端收到)
-
内核动作:
当服务端内核收到客户端的SYN
报文(标志位SYN=1
)时,会立即创建一个普通套接字(Socket),称为未完成连接套接字(Incomplete Connection Socket),并进入SYN_RCVD
状态。该套接字记录客户端的IP、端口、初始序列号(
client_isn
)等信息。
此时套接字尚未完全建立(未通过第三次握手),但已占用资源(进入了半连接队列)。 -
为什么此时生成?
资源预留:为防止SYN Flood攻击,内核需要限制半连接队列大小(
net.ipv4.tcp_max_syn_backlog
)。
状态跟踪:服务端需维护客户端的seq
和连接状态,以完成后续握手。
③ 第二次握手(服务端发送SYN+ACK
)
- 服务端使用新生成的套接字发送
SYN+ACK
(seq=server_isn
,ack=client_isn+1
),但仍处于SYN_RCVD
状态。 - 此时套接字仍在半连接队列中,等待客户端的最终
ACK
。
④ 第三次握手(客户端发送ACK
,连接完成)
- 服务端收到
ACK
后,将套接字从半连接队列移到全连接队列(Accept Queue),状态变为ESTABLISHED
。 - 当服务端应用调用
accept()
时,从全连接队列中取出该套接字,交给应用程序使用。
3. 总结
阶段 | 服务端动作 | 套接字状态 |
---|---|---|
第一次握手前 | 仅有监听套接字(LISTEN ) | 无客户端套接字 |
第一次握手 | 收到SYN 后,内核生成新套接字 | 半连接队列(SYN_RCVD ) |
第三次握手后 | 套接字移至全连接队列,等待accept() | 可用的ESTABLISHED 套接字 |
① 为什么不在第三次握手后再生成套接字?
- 如果在第三次握手后才生成,服务端无法在第二次握手时发送
SYN+ACK
(需要记录客户端信息)。 - 提前生成可确保资源分配和状态跟踪,但需防范SYN Flood攻击(通过半连接队列限制)。
② 半连接队列 vs 全连接队列
- 半连接队列(SYN Queue):存储未完成三次握手的套接字(
SYN_RCVD
状态),队列大小由net.ipv4.tcp_max_syn_backlog
参数决定。 - 全连接队列(Accept Queue):存储已完成握手但未被
accept()
的套接字(ESTABLISHED
状态),队列大小由backlog
参数和net.core.somaxconn
决定。
③ 如果第三次握手的ACK
丢失怎么办?
- 服务端会重传
SYN+ACK
(次数由net.ipv4.tcp_synack_retries
控制),超时后删除半连接套接字。