TCP三次握手与四次挥手

130 阅读5分钟

作者:Sean

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天

前言:经典面试题,TCP 建立连接需要经过三次握手,断开连接则需要四次挥手

前端必会面试题

一些术语

  • SYN(synchronous) - 用于初始化一个连接的序列号,建立联机
  • ack(acknowledgement)- 确认号码
  • seq(sequence) - 顺序号码
  • ISN(Initial Sequence Number) - 初始序列号
  • ACK - 确认,使得确认号有效(握手用)
  • FIN(finish) - 该报文的发送方已经结束向对方发送数据
  • MSL - 最长报文寿命,一个 MSL 是 2 分钟

TCP 的三次握手 (双端建立稳定连接)

  1. 客户端通过SYN报文段发送连接请求 (SYN 标志位为 1,初始序列号 x,和 ACK 标志位为 0),确定服务端是否开启端口准备连接,状态设置为SYN_SEND (SYN=1, seq=x)
  2. 服务器如果开着端口并且决定连接,就会返回一个SYN+ACK报文段给客户端(SYN 和 ACK 标志位均为 1),同时确认 ISN 序列号,放到 seq 域里,并将 ack 设置为客户端的 ISN 加 1,状态设置为SYN_RECV (SYN=1, Seq=y, Ack=x+1)
  3. 客户端收到服务器的SYN_RECV报文段,会向服务器发送ACK报文段表示确认(包含了服务器端发来的 ISN 序列号,并且将该 ISN 加 1),此时客户端和服务端都设置为ESTABLISHED状态。状态连接可以开始数据传输了 (SYN=1, Seq=x+1, Ack=y+1)

为什么是三次

  • 避免历史连接,例如,客户端发送了建立连接请求,在某些网络节点长时间滞留,一直未收到服务端确认,再次发送请求并收到确认建立连接,数据传输完毕后释放了连接。这时候第一次滞留的请求又成功发送到服务端,服务端同意建立连接,如果没有第三次握手,服务端会一直等待客户端发送数据,浪费资源。
  • 建立稳定可靠的 TCP 连接,需要确认客户端和服务端双方的接收和发送能力。第一次 SYN 和 Seq 确认客户端的发送能力,第二次 SYN 和 Seq 确认服务端的发送能力,Ack 确认服务端的接收能力,第三次 Ack 确认客户端的接收能力。

半连接队列

当第二次握手后服务端处于SYN_RECV 状态,服务端会把请求连接都放在半连接队列中。当第三次握手后,就会放在全连接队列中,当队列满了,就会存在丢包现象。

  • 第二次握手后如果服务端一直未收到客户端确认,会进行多次重传尝试,超过最大限次后,会将连接信息从半连接队列中删除。

ISN 是固定的吗?

ISN(Initial Sequence Number)是随时间变化的,每 4ms 加 1。方式网络中被延迟分组在以后又被传送,而导致某个连接的一方对它做错误的理解。同时 ISN 也会让双方知道建立连接后如何按序列号组装数据,动态可以防止攻击者猜出后续的确认号

三次握手期间可以携带数据吗

前两次不可以,防止在报文中夹带大量数据攻击,服务器会花费大量时间和空间来接收这些报文

SYN 攻击

一种典型的 DDoS 攻击,伪造大量不存在的 ip 向服务端发起连接请求,因为是伪造的地址,服务端会不停的发送确认请求直至超时,伪造的 SYN 包也会长时间占用半连接队列,导致正常的请求进不来,被废弃,引发网络堵塞,甚至系统瘫痪

TCP 的四次挥手 (双端关闭 TCP 连接)

  1. 客户端向服务器端发送一个FIN标志位置为 1,当前序列号为 u 的包,准备断开连接,并且进入FIN_WAIT_1状态 (FIN=1, seq=u)
  2. 服务器端收到 FIN 包,会发送一个确认序号收到序列号为u+1的包,表明自己接受到客户端关闭连接的请求,但还未准备好关闭连接。服务器端进入CLOSE_WAIT状态,客户端进入FIN_WAIT_2状态(ACK=1, seq=v, ack=u+1)
  3. 服务器端发送完剩余数据后,会发送一个自己的FIN包,序列号为u+1。服务器端进入LAST_ACK状态,等待客户端的最后一个ACK (FIN=1, ACK=1, seq=w, ack= u+1)
  4. 客户端收到服务器端的关闭请求好,发送最后一个ACK确认包,确认序列号设置为收到序列号加 1。客户端进入TIME_WAIT状态,等待可能出现的要求重传的ACK包。服务器端接受到这个确认包之后就关闭连接,进入CLOSED状态。客户端等待2MSL之后,没有收到服务器端的ACK,就确认服务器端已经关闭,然后自己就关闭进入CLOSED状态 (ACK=1, seq=u+1, ack=w+1)

为什么不是两次

  • 只有两次就是客户端说完结束立刻断开不再接受,这样无法确认服务器端是否接受到断开消息,服务器端可能还有消息未发送完

为什么不是三次

  • 只有三次情况为服务器端接受到断开消息,需要客户端给出确认断开的回复

为什么客户端发送最后一个 ACK 确认包后不直接关闭

  • 客户端还会再等 2MSL 后,才会断开连接进入 closed 状态,这样以防服务端发送的数据包网络延迟滞留,在关闭确认包之后才到达。
  • 也以防这时候客户端又建立其他连接接受到了之前链接的数据包

为什么等待 2MSL

1MSL 确保发起关闭方的最后一个 ACK 报文发送到接收方

1 个 MSL 确保对端没有收到 ACK 重传的 FIN 报文可以到达。