三次握手的实现
TCP 三次握手(Three-Way Handshake)是建立 TCP 连接的核心过程,其目的是确保客户端和服务器双方的发送能力与接收能力均正常,并协商初始序列号(用于后续数据传输的有序性和完整性校验)。以下是详细过程:
三次握手的核心角色与参数
- 客户端:主动发起连接的一方(如浏览器)。
- 服务器:被动接受连接的一方(如 Web 服务器)。
- 序列号(Sequence Number,
seq
):发送方为每个数据包分配的唯一编号,用于接收方确认数据顺序和完整性(避免重复或丢失)。 - 确认号(Acknowledgment Number,
ack
):接收方告知发送方“已收到的最大序列号 + 1”,表示期望接收的下一个数据编号。 - 标志位:
SYN
(Synchronize):用于发起连接,请求同步序列号。ACK
(Acknowledgment):用于确认已收到数据。
三次握手的具体步骤
第一步:客户端 → 服务器(发送 SYN
报文)
客户端行为:
客户端向服务器发送一个SYN
报文(同步请求),表示“我想和你建立连接”。
报文中包含:- 标志位:
SYN = 1
(表示这是一个连接请求)。 - 初始序列号:
seq = x
(x
是客户端随机生成的 32 位整数,如1000
)。
- 标志位:
服务器状态变化:
服务器收到SYN
报文后,从“关闭状态”进入“同步收到状态(SYN-RCVD)”,表示“已收到连接请求,准备回应”。
第二步:服务器 → 客户端(发送 SYN+ACK
报文)
服务器行为:
服务器向客户端回复一个SYN+ACK
报文(同步 + 确认),表示“我收到了你的请求,也想和你建立连接”。
报文中包含:- 标志位:
SYN = 1
(服务器发起自己的同步请求)、ACK = 1
(确认收到客户端的请求)。 - 服务器的初始序列号:
seq = y
(y
是服务器随机生成的整数,如2000
)。 - 确认号:
ack = x + 1
(表示“已收到客户端序列号为x
的报文,下一次期望接收x+1
及以后的数据”)。
- 标志位:
客户端状态变化:
客户端收到SYN+ACK
报文后,从“同步发送状态(SYN-SENT)”进入“建立连接状态(ESTABLISHED)”,表示“确认服务器已准备好,连接即将建立”。
第三步:客户端 → 服务器(发送 ACK
报文)
客户端行为:
客户端向服务器发送一个ACK
报文(确认),表示“我收到了你的回应,连接可以正式建立了”。
报文中包含:- 标志位:
ACK = 1
(仅确认,无新的同步请求)。 - 客户端的序列号:
seq = x + 1
(基于第一步的x
递增,符合 TCP 序列号连续的规则)。 - 确认号:
ack = y + 1
(表示“已收到服务器序列号为y
的报文,下一次期望接收y+1
及以后的数据”)。
- 标志位:
服务器状态变化:
服务器收到ACK
报文后,从“同步收到状态(SYN-RCVD)”进入“建立连接状态(ESTABLISHED)”,表示“双方确认完毕,TCP 连接正式建立”。
三次握手的核心目的
验证双向通信能力:
- 第一步:客户端确认服务器“能接收”(服务器收到
SYN
)。 - 第二步:服务器确认客户端“能发送也能接收”(客户端收到
SYN+ACK
)。 - 第三步:客户端确认服务器“能发送”(服务器收到
ACK
)。
三次交互后,双方确认彼此的发送和接收功能均正常。
- 第一步:客户端确认服务器“能接收”(服务器收到
协商初始序列号:
客户端和服务器通过前两步交换各自的初始序列号(x
和y
),后续数据传输将基于这些序列号递增,确保数据有序且可校验(如通过序列号检测丢包或重复数据)。
为什么需要三次握手,而不是两次?
- 若仅用两次握手,服务器在发送
SYN+ACK
后就认为连接已建立,但客户端可能因网络延迟等原因未收到该报文,此时服务器会一直等待客户端发送数据,造成资源浪费。 - 三次握手的最后一步(客户端发送
ACK
)确保客户端确实收到了服务器的回应,避免服务器“单方面”建立无效连接,减少资源消耗。
总结
三次握手的本质是“双向确认”:
客户端请求连接 → 服务器确认并请求连接 → 客户端确认
通过三次交互,双方验证了通信能力并协商了初始序列号,最终建立可靠的 TCP 连接,为后续数据传输奠定基础。
四次挥手的实现
在 TCP 协议中,四次挥手(Four-Way Wavehand) 是关闭 TCP 连接的过程,用于确保双方都已完成数据传输,并安全释放连接资源。与三次握手建立连接不同,四次挥手需要四次交互,核心原因是 TCP 连接是全双工的(双方可同时发送数据),关闭连接时需各自独立终止发送方向的数据流。
四次挥手的核心背景
TCP 连接是“全双工”模式,即客户端和服务器可以同时向对方发送数据。因此,关闭连接时需分两步:
- 一方先终止向对方发送数据(关闭发送通道);
- 另一方确认并终止自己的发送通道。
这一过程需要四次交互来完成双向确认。
四次挥手的具体步骤
假设客户端主动发起关闭请求(实际中服务器也可主动发起),步骤如下:
第一步:客户端 → 服务器(发送 FIN
报文)
客户端行为:
客户端完成数据发送后,向服务器发送FIN
报文(Finish,结束),表示“我已完成数据发送,请求关闭我向你发送数据的通道”。
报文中包含:- 标志位:
FIN = 1
(表示终止发送)。 - 序列号:
seq = u
(u
是客户端已发送的最后一个字节的序列号 + 1,如客户端最后发送的序列号是 1000,则u = 1001
)。
- 标志位:
客户端状态变化:
从“建立连接状态(ESTABLISHED)”进入“终止等待 1 状态(FIN-WAIT-1)”,等待服务器确认。
第二步:服务器 → 客户端(发送 ACK
报文)
服务器行为:
服务器收到FIN
报文后,回复ACK
报文,表示“已收到你关闭发送通道的请求,我会处理剩余数据”。
报文中包含:- 标志位:
ACK = 1
(确认)。 - 序列号:
seq = v
(服务器当前的序列号,基于自身已发送的数据)。 - 确认号:
ack = u + 1
(表示“已收到序列号u
的FIN
报文,下一次期望接收u+1
”)。
- 标志位:
服务器状态变化:
从“ESTABLISHED”进入“关闭等待状态(CLOSE-WAIT)”,此时客户端到服务器的发送通道已半关闭(客户端不再发送数据,但服务器仍可向客户端发送数据)。客户端状态变化:
收到ACK
后,从“FIN-WAIT-1”进入“终止等待 2 状态(FIN-WAIT-2)”,等待服务器发送自己的FIN
报文。
第三步:服务器 → 客户端(发送 FIN
报文)
服务器行为:
服务器完成所有数据发送后,向客户端发送FIN
报文,表示“我也已完成数据发送,请求关闭我向你发送数据的通道”。
报文中包含:- 标志位:
FIN = 1
、ACK = 1
(通常同时携带确认)。 - 序列号:
seq = w
(服务器最后发送的字节序列号 + 1,基于v
递增)。 - 确认号:
ack = u + 1
(与第二步的确认号一致,因未收到新数据)。
- 标志位:
服务器状态变化:
从“CLOSE-WAIT”进入“最后确认状态(LAST-ACK)”,等待客户端的最终确认。
第四步:客户端 → 服务器(发送 ACK
报文)
客户端行为:
客户端收到服务器的FIN
报文后,回复ACK
报文,表示“已收到你关闭发送通道的请求,连接即将完全关闭”。
报文中包含:- 标志位:
ACK = 1
。 - 序列号:
seq = u + 1
(基于第一步的u
递增)。 - 确认号:
ack = w + 1
(表示“已收到序列号w
的FIN
报文,下一次期望接收w+1
”)。
- 标志位:
客户端状态变化:
从“FIN-WAIT-2”进入“时间等待状态(TIME-WAIT)”,并启动一个 2MSL(Maximum Segment Lifetime,报文最大生存时间,通常为 2 分钟) 的计时器。- 目的:确保服务器能收到最终的
ACK
报文(若服务器未收到,会重发FIN
,客户端在 2MSL 内可再次回复)。 - 2MSL 后,客户端进入“关闭状态(CLOSED)”。
- 目的:确保服务器能收到最终的
服务器状态变化:
收到ACK
后,从“LAST-ACK”直接进入“CLOSED”状态,释放连接资源。
四次挥手的核心目的
- 确保双向数据传输完成:
双方分别通过FIN
报文告知对方“自己的发送已结束”,通过ACK
确认对方已知晓,避免数据丢失。 - 避免连接关闭后的报文干扰:
客户端的 2MSL 等待时间,确保网络中残留的旧报文(属于当前连接)已过期,防止其干扰新连接(新连接可能使用相同的端口)。
为什么需要四次挥手,而不是三次?
- 三次握手时,服务器的
SYN
和ACK
可合并为一次报文(第二步SYN+ACK
),因为此时服务器无需处理未完成的数据发送。 - 四次挥手时,服务器收到客户端的
FIN
后,可能还有未发送完的数据,因此需先回复ACK
确认(第二步),待数据发送完毕后再发送FIN
(第三步),无法合并为一次报文,因此需要四次交互。
总结
四次挥手是 TCP 全双工连接关闭的必然过程:
客户端请求关闭发送通道 → 服务器确认 → 服务器请求关闭发送通道 → 客户端确认并等待
通过四次交互,双方安全终止双向数据流,并通过时间等待机制确保连接资源的彻底释放,是 TCP 可靠性的重要体现。