TCP三次握手和四次挥手
关于TCP协议中的标志位和字段的说明
为了下边的讲解更加清晰,这里首先对会涉及到的TCP协议中的标志位和字段等做一个简单的说明。
TCP段是TCP协议的数据单元,它由TCP首部和TCP数据组成。TCP首部中包含了一些标志位和字段,这些标志位和字段用于控制TCP连接的建立、维护和释放等。
标志位
SYN
:同步标志位,用于建立连接。简写为S
。ACK
:确认标志位,表示接收到了发送方的数据。简写为.
。FIN
:结束标志位,表示发送方已经完成了所有数据的发送,想要关闭连接。简写为F
。
字段
- Sequence Number (
seq
):序号,用于标识TCP段的数据部分的起始字节在整个数据流中的位置。 - Acknowledgment Number (
ack
):确认号,对上一次seq
序号做出的确认号。
NOTE
通过wireshark
等抓包工具可以查看TCP段的详细信息,包括TCP首部中的标志位和字段等。可能会遇到简写形式。
在连接建立的过程中,每一方都会生成一个随机的初始序号 Initial Sequence Number(ISN
),这避免了不同连接之间的混淆。 每次新的连接都从一个不同的 ISN 开始,然后后续数据包的序号基于这个初始值递增。
假设某个 TCP 段的 Sequence Number 为 1000,并且这个段包含了 100 个字节的数据,那么这个段实际上包含了字节流中的第 1000 到 1099 号字节。
如果接收方收到的段的序号范围是 1000 到 1099,那么它将发送一个确认号为 1100 的ack
段,表示 "我已经收到了前 1100 个字节,下一次请从第 1100 个字节开始发送"。
NOTE
为了简化讲解,这里只介绍了TCP首部中的部分字段和标志位,实际上TCP首部中还有很多其他字段和标志位,这里不再一一介绍。
注意ACK
和ack
的区别,ACK
是标志位,ack
是字段。
三次握手
TCP协议的三次握手过程是在浏览器和服务器之间通过TCP建立连接时,为了确保在数据传输之前,通信双方已经准备好,并能确认彼此的接收和发送能力。
第一次握手
客户端发起连接请求,向服务器发送一个带有SYN
标志的 TCP 段,并生成一个初始序号x
,seq
字段的值为x
,客户端进入 SYN_SENT 状态,等待服务器的响应。
此时简化后的报文内容为:
seq = x, SYN = 1
第二次握手
服务端接收到客户端的连接请求后,会向客户端回复一个同时带有SYN
(表示同意建立连接)和ACK
(表示对第一次握手SYN
对确认)标志的 TCP 段。
服务端还会生成属于自己的初始序号y
,seq
字段的值为y
,ack
字段的值为x + 1
,表示对客户端的seq = x
字段的确认,服务端进入 SYN_RECEIVED 状态。
此时简化后的报文内容为:
seq = y, ack = x + 1, SYN = 1, ACK = 1
第三次握手
客户端接收到服务端的响应后,会向服务端发送一个带有ACK
标志的 TCP 段(表示对第二次握手服务端SYN
的确认),并将seq
字段的值设置为x + 1
(本次报文不包含SYN
,所以不需要+1), 将ack
字段的值设置为y + 1
,表示对服务端的seq = y
字段的确认。
客户端进入 ESTABLISHED 状态,准备发送数据。
此时简化后的报文内容为:
seq = x + 1, ack = y + 1, ACK = 1
服务器收到ACK
报文后,也进入 ESTABLISHED 状态,标志着TCP连接正式建立,双方可以开始数据传输。
NOTE
在三次握手和四次挥手中,SYN
和FIN
段本身不携带实际的数据,但它们会占用一个序号。
假设客户端发起连接时,发送了一个带有SYN
标志的 TCP 段,seq
的值为 1000,表示它的初始序号是1000
。由于SYN
段占用了一个序号, 服务器接收到这个段后,发送的ack
段的确认号会是1001
,表示它确认了客户端的SYN
段,并期望接收到从序号1001
开始的数据。这就是ack = seq + 1
的原因。
为什么是三次握手
- 三次握手是能够确保双方同时具有收发能力,并且后续数据传输有序、可靠的最少安全次数。
四次挥手
TCP 的“四次挥手”指的是在 TCP 连接断开时,双方按照规定的顺序交换四个报文以确保连接的安全终止。这是为了保证双方都有足够的时间发送和接收剩余的数据。
第一次挥手
客户端发送一个带有FIN
标志的 TCP 段给服务器,表示客户端已经完成了所有数据的发送,希望关闭连接。生成一个序号x
,seq
字段的值为x
。
客户端进入 FIN_WAIT_1 状态,等待服务器的确认。
此时简化后的报文内容为:
seq = x, FIN = 1
第二次挥手
服务端接收到客户端的关闭请求后,会向客户端回复一个带有ACK
标志的 TCP 段,表示对客户端的FIN
段的确认。 生成一个序号y
,seq
字段的值为y
,ack
字段的值为x + 1
。
服务端进入 CLOSE_WAIT 状态,注意此时仍可以发送数据,并未结束连接。
客户端收到服务端的确认后,进入 FIN_WAIT_2 状态,无回复,等待服务端发送FIN
报文。
此时简化后的报文内容为:
seq = y, ack = x + 1, ACK = 1
第三次挥手
服务端已经完成了所有数据的发送,发送一个带有FIN
标志的 TCP 段给客户端,希望关闭连接。生成一个序号z
,seq
字段的值为z
,ack
字段的值为x + 1
。
服务端进入 LAST_ACK 状态,等待客户端的确认。
此时简化后的报文内容为:
seq = z, ack = x + 1, FIN = 1
第四次挥手
客户端接收到服务端的关闭请求后,向服务端回复一个带有ACK
标志的 TCP 段,表示对服务端的FIN
段的确认。ack
字段的值为z + 1
。
客户端进入 TIME_WAIT 状态,服务端收到客户端的确认后,进入 CLOSED 状态,连接正式关闭。
客户端等待 2MSL(最长报文段寿命)时间后后依然没有收到回复,则证明服务端已正常关闭,客户端也关闭连接。
此时简化后的报文内容为:
ack = z + 1, ACK = 1
为什么是四次挥手
- TCP 是全双工通信协议,意味着连接的每一端都需要单独关闭。每一端的关闭都需要通过
FIN
和ACK
来确认,以确保剩余的数据安全传输。 - 由于关闭请求和确认是分开处理的,这导致需要四次报文交换。客户端和服务端各自独立地关闭发送通道和接收通道,这样既保证了数据完整性,也防止了中断数据的传输。