TCP 三次握手

三次握手的过程

刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。

  • 第一次握手:客户端发送 SYN=1,seq=x(SYN=1 的报文段不能携带数据,但要消耗掉一个序号),此时客户端处于 SYN_SENT 状态

  • 第二次握手:服务器回复 SYN=1,ACK=1,ack=x+1,seq=y,此时服务器处于 SYN_RCVD 的状态

  • 第三次握手:客户端回复 ACK=1,ack=y+1,seq=x+1,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。

ACK 报文段可以携带数据,不携带数据则不消耗序号

注意:不要混淆 ACK 标志位和 ack 确认序列号,ACK 标记位为 1 表示这是包一个确认包,而 ack 确认序列号是 Acknowledgement Number,是对对方初始序列号的回复(ack = x+1),对方以此确认该报文是对其 SYN 报文的回复。ack 确认序列号只有在 ACK 标记位为 1 时才有效

为什么是三次握手

TCP 的可靠连接是靠 seq(sequence numbers 序列号)来达成的。

TCP 设计中一个基本设定就是,通过 TCP 连接发送的每一个包,都有一个 sequence number。而因为每个包都是有序列号的,所以都能被确认收到这些包。

确认机制是累计的,所以一个对 sequence number X 的确认,意味着 X 序列号之前 (不包括 X) 包都是被确认接收到的。

所以这样就有一个问题:这条连接突然断开重连后,TCP 怎么样识别之前旧链接重发的包?这就需要独一无二的 ISN(初始序列号)机制。

那么 TCP 连接握手,握的是啥?

三次握手是为了双方确定彼此的初始序列号

注意:seq = x,这个 x 就是初始序列号

  1. 如果是四次握手:

    1.1 A 发送同步信号 SYN + A 的初始序列号(Initial sequence number,简称 ISN)

    1.2 B 收到后回包,并记录 A 的初始序列号到本地

    1.3 B 发送同步信号 SYN + B 的初始序列号

    1.4 A 收到后回包,并记录 B 的初始序列号到本地

    这样,A 和 B 就互相确认了彼此的初始序列号。但很显然 1.2 和 1.3 这两个步骤可以合并,只需要三次握手

  2. 如果是两次握手

    2.1 A 发送同步信号 SYN + A 的初始序列号

    2.2 B 收到后,发送同步信号 SYN + B 的初始序列号

    这种情况,A 的初始序列号双方都知道;但是 B 的初始序列号,B 无法确定 A 是否知道

中途丢包的情况

  1. 第一个包,即 A 发给 B 的 SYN 中途被丢,没有到达 B

    A 会周期性超时重传,直到收到 B 的确认

  2. 第二个包,即 B 发给 A 的 SYN+ACK 中途被丢,没有到达 A

    B 会周期性超时重传,直到收到 A 的确认

  3. 第三个包,即 A 发给 B 的 ACK 中途被丢,没有到达 B

    A 发完 ACK,单方面认为 TCP 为 Established 状态,而 B 显然认为 TCP 为 Active 状态

    这种情况下 A 会超时重传这个 ACK 吗?不会!TCP 不会为没有数据的 ACK 超时重传

    a. 假定此时双方都没有数据发送,B 会周期性超时重传,直到收到 A 的确认,收到之后 B 的 TCP 连接也为 Established 状态,双向可以发包

    b. 假定此时 A 有数据发送,B 收到 A 的 Data + ACK,自然会切换为 established 状态,并接受 A 的 Data

    c. 假定 B 有数据发送,数据发送不了,会一直周期性超时重传 SYN + ACK,直到收到 A 的确认才可以发送数据

三次握手过程中可以携带数据吗?

其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据

为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。

也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。

对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病

SYN 攻击是什么?

服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到 SYN 洪泛攻击。

SYN 攻击就是 Client 在短时间内伪造大量不存在的 IP 地址,并向 Server 不断地发送 SYN 包,Server 则回复确认包,并等待 Client 确认,由于源地址不存在,因此 Server 需要不断重发直至超时,这些伪造的 SYN 包将长时间占用未连接队列,导致正常的 SYN 请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。

检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源 IP 地址是随机的,基本上可以断定这是一次 SYN 攻击。在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击。

参考资料

  1. 三次握手的过程
  2. 为什么是三次握手
  3. 中途丢包的情况
  4. 三次握手过程中可以携带数据吗?
  5. SYN 攻击是什么?
  6. 参考资料