Skip to content

TCP三次握手和四次挥手

关于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首部中还有很多其他字段和标志位,这里不再一一介绍。

注意ACKack的区别,ACK是标志位,ack是字段。

三次握手

TCP协议的三次握手过程是在浏览器和服务器之间通过TCP建立连接时,为了确保在数据传输之前,通信双方已经准备好,并能确认彼此的接收和发送能力。 TCP三次握手

第一次握手

客户端发起连接请求,向服务器发送一个带有SYN标志的 TCP 段,并生成一个初始序号xseq字段的值为x,客户端进入 SYN_SENT 状态,等待服务器的响应。

此时简化后的报文内容为:

seq = x, SYN = 1

第二次握手

服务端接收到客户端的连接请求后,会向客户端回复一个同时带有SYN(表示同意建立连接)和ACK(表示对第一次握手SYN对确认)标志的 TCP 段。

服务端还会生成属于自己的初始序号yseq字段的值为yack字段的值为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

在三次握手和四次挥手中,SYNFIN段本身不携带实际的数据,但它们会占用一个序号。

假设客户端发起连接时,发送了一个带有SYN标志的 TCP 段,seq的值为 1000,表示它的初始序号是1000。由于SYN段占用了一个序号, 服务器接收到这个段后,发送的ack段的确认号会是1001,表示它确认了客户端的SYN段,并期望接收到从序号1001开始的数据。这就是ack = seq + 1的原因。

为什么是三次握手

  • 三次握手是能够确保双方同时具有收发能力,并且后续数据传输有序、可靠的最少安全次数。

四次挥手

TCP 的“四次挥手”指的是在 TCP 连接断开时,双方按照规定的顺序交换四个报文以确保连接的安全终止。这是为了保证双方都有足够的时间发送和接收剩余的数据。 TCP四次挥手

第一次挥手

客户端发送一个带有FIN标志的 TCP 段给服务器,表示客户端已经完成了所有数据的发送,希望关闭连接。生成一个序号xseq字段的值为x

客户端进入 FIN_WAIT_1 状态,等待服务器的确认。

此时简化后的报文内容为:

seq = x, FIN = 1

第二次挥手

服务端接收到客户端的关闭请求后,会向客户端回复一个带有ACK标志的 TCP 段,表示对客户端的FIN段的确认。 生成一个序号yseq字段的值为yack字段的值为x + 1

服务端进入 CLOSE_WAIT 状态,注意此时仍可以发送数据,并未结束连接

客户端收到服务端的确认后,进入 FIN_WAIT_2 状态,无回复,等待服务端发送FIN报文。

此时简化后的报文内容为:

seq = y, ack = x + 1, ACK = 1

第三次挥手

服务端已经完成了所有数据的发送,发送一个带有FIN标志的 TCP 段给客户端,希望关闭连接。生成一个序号zseq字段的值为zack字段的值为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 是全双工通信协议,意味着连接的每一端都需要单独关闭。每一端的关闭都需要通过FINACK来确认,以确保剩余的数据安全传输。
  • 由于关闭请求和确认是分开处理的,这导致需要四次报文交换。客户端和服务端各自独立地关闭发送通道和接收通道,这样既保证了数据完整性,也防止了中断数据的传输。

基于 Apache-2.0 许可发布