Skip to content

三次握手四次挥手

🕒 Published at:

三次握手

TCP 提供 面向连接 的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作,也就是说无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。

三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。

握手的目标

三次握手的目的:

  • 同步连接双方的 Sequence 序列号和确认号
    • 初始序列号 ISN(Initial Sequence Number)
  • 交换 TCP 窗口大小信息
    • 如 MSS、窗口比例因子、选择性确认、指定校验和算法

在 Socket 编程中,这一过程由客户端执行 connect 来触发。

三次握手流程图

  1. 第一次握手:建立连接。客户端发送连接请求报文段,将标志比特位 SYN 置为 1,随机产生一个序列号码 Sequence Number 值为 X(由操作系统动态随机选取一个 32 位长的序列号),并将该数据包发送给服务端,客户端进入 SYN_SENT 状态,等待服务端确认。

  2. 第二次握手:服务端收到 SYN 报文段。服务端收到数据包后需要对标志位 SYN 报文段进行确认,确认后设置确认号码 Acknowledgment Number 为 X+1(Sequence Number+1);同时,自己还要发送 SYN 请求信息(以建立服务端对客户端的连接),将 SYN 设置为 1,设置 Sequence Number 值为 Y(由操作系统动态随机选取一个 32 位长的序列号),服务端将上述所有信息放到一个报文段(即 SYN+ACK 报文段)中,一并发送给客户端以确认建立连接请求,服务端进入 SYN_RCVD 状态。

  • 第三次握手:客户端收到服务端的 SYN+ACK 报文段。确认后,然后将 Acknowledgment Number 设置为 Y+1,向服务端发送 ACK 报文段,这个报文段发送完毕后,客户端和服务器端进入 ESTABLISHED 状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。

握手过程中传送的包里不包含数据,只有三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP 连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

为什么建立 TCP 连接需要三次握手?

主要是为了防止服务端开启无用的连接。

因为我们知道网络传输是有延时的,因为终端间隔了非常远的距离,数据包通过光纤以及各种中间代理服务器进行传输,但是在服务端和客户端的传输过程中,往往由于网络传输的不稳定原因丢失了数据包,客户端一直没有收到服务端返回的数据包,客户端可能设置了超时时间关闭了连接创建,那么就会再发起新的请求。如果没有第三次握手,服务端是不知道客户端到底有没有接收到服务端返回给他的数据的,客户端也没有一个确认说要关闭还是要创建这个请求,服务端的端口就一直开着,等着客户端发送实际的请求数据,那么这个时候开销就浪费了,服务端不知道这个连接已经创建失败了,可能客户端已经创建别的连接去了。

所以我们需要三次握手来确认这个过程,让服务端和客户端能及时察觉到网络原因导致的网络连接的关闭的问题,从而规避网络传输中因为延时导致导致的服务器开销问题。

三次握手中的第一次握手可以携带数据吗?

不可以,因为三次握手还没完成。

第三次握手可以发送数据吗?为何?

可以。因为能够发出第三次握手报文的主机,肯定接收到第二次(来自服务端)的握手报文。因为伪造 IP 的主机不会收到第二次报文。

对方难道不可以将数据缓存下来,等握手成功后再提交给应用程序?

这样会放大 SYN FLOOD 攻击。如果攻击者伪造了成千上万的握手报文,携带了 1K+ 字节的数据,而接收方会开辟大量的缓存来容纳这些巨大数据,内存会很容易耗尽,从而拒绝服务。

四次挥手

四次挥手即终止 TCP 连接,就是指断开一个 TCP 连接时,需要客户端和服务端总共发送 4 个包以确认连接的断开。在 Socket 编程中,这一过程由客户端或服务端任一方执行 close 来触发。

由于 TCP 连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个 FIN 来终止这一方向的连接,收到一个 FIN 只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个 TCP 连接上仍然能够发送数据,直到这一方向也发送了 FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。

四次挥手流程图

  • 第一次挥手 客户端设置 Sequence Number,发送一个 FIN 报文段,用于关闭客户端到服务器端的数据传送,客户端进入 FIN_WAIT_1 状态。意思是说「我客户端没有数据要发给你了」,但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。
  • 第二次挥手 服务器端收到 FIN 报文段,回复 ACK 报文段,Acknowledgment Number 为 Sequence Number 加 1,告诉客户端,你的请求我收到了,我同意你的关闭请求。这个时候客户端就进入 FIN_WAIT_2 状态。
  • 第三次挥手 当服务器端确定数据已发送完成,则向客户端发送 FIN 报文段,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入 LAST_ACK 状态。
  • 第四次挥手 客户端收到 FIN 报文段后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送 ACK 报文段回复服务端,然后进入 TIME_WAIT 状态,如果服务端端没有收到 ACK 则可以重传。服务器端收到 ACK 后,就知道可以断开连接了。客户端等待了 2MSL(通常是两分钟)后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。

MSL(Maximum Segment Lifetime)报文最大生存时间。维持 2MSL 时长的 TIME-WAIT 状态,保证至少一次报文的往返时间内端口是不可复用。

  • 第一次挥手是服务端确认客户端需要断开连接
  • 第二次挥手是客户端确认服务器接收断开请求
  • 第三次挥手是客户端确认服务器数据发完,断开连接
  • 第四次挥手是服务端确认客户端断开连接,断开连接

所以如果服务端的数据全部发送完,是没有第三次挥手,直接进入第四次挥手。

为什么断开 TCP 连接需要四次挥手?

由于 TCP 连接采取全双工的通信方式,因此每个方向都必须单独进行关闭,这个原则是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向的连接。收到一个 FIN 只意味着这一方向上没有数据流动,一个 TCP 连接在收到一个 FIN 后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

为什么基于 TCP 的程序往往都有个应用层的心跳检测机制?

TCP 建立链接后,只是在两端的内核里面维持 TCP 信息,实际上并没有一个物理的连接通路,对端这个时候挂了,谁也不知道。