问题:TCP连接的三次握手

三次握手、四次挥手

先明确几个关键字:

同步 SYN :synchronous。建立连接,将 SYN = 1。
序号 seq :sequence。第一个字节的编号随机产生。
确认位 ACK: acknowledgement 。
ack :表示确认字段的值。
结束 FIN : finish。FIN = 1 表示希望断开连接。
重置RST:重置。

三次握手

  • 第一次握手:客户端发送同步信号 SYN 和一个随机序列号 seq = x到服务器,并进入 SYN_SEND 状态,等待服务器确认;

  • 第二次握手:服务器收到 客户端的同步信号,返回确认位 AKC = 1 ,并将传来的 x 加上 1 的ack=x+1返回
    同时自己也发送一个 同步信号SYN 和一个随机序列号 seq=y给客户端,即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;

  • 第三次握手:客户端确认收到 服务器 的同步信号SYN+ACK包,返回确认位 ACK = 1 ,并将传来的 y 加上 1 的ack=y+1返回,此时更新后的 seq = x + 1 也一并返回,此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP 连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开 TCP 连接的请求,断开过程需要经过下面提到的“四次挥手”

三次握手.gif

为什么需要三次握手呢?为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
  • 防止旧的重复连接初始化造成混乱,避免资源浪费。

谢希仁版《计算机网络》中的例子是这样的,“已失效的连接请求报文段” 的产生在这样一种情况下:
client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段,但是server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求,于是就向client发出确认报文段,同意建立连接。
假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了,由于client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据,但server却以为新的运输连接已经建立,并一直等待client发来数据。所以没有采用“三次握手”,这种情况下server的很多资源就白白浪费掉了。
白话一点就是,如果只有两次握手,那么:
1. A 向 B 发送了第一个请求,但是因为网络堵塞没有到达
2. 超时后,于是 A 又发送了第二个请求,这此正常到达,而且 B 回复收到了
3. A 这时就开始和 B 相互通信,并一段时间后结束了连接
4. 此时最开始的请求到达了 B,B 以为 A 又请求了连接,并且回复了 A
5. 但 A 认为自己并没有发送请求,所以并不会理睬 B, B 就只能一直等着

  • 同步双方的初始序列号

三次握手主要是为了初始化 Sequence Number,即上述中的 seq,seq的作用就是确保数据包的有序传输,数据也是通过 seq 进行数据的拼接
举例:
1. 客户端先发送了一个请求连接的数据包,初始 seq = 100 , 因为网络阻塞收不到回复,于是又发送了一个请求连接的数据包,初始 seq = 200
2. 此时网络又恢复了正常,服务端接收到了最开始发送的 seq = 100, 于是返回 ack = 101
3. 客户端比较上下文,发现自己希望收到的 ack 应该是 201,于是发送了 RST 给服务端,中止连接。
4. 一段时间后 seq = 200 到达了服务端,并返回了 ack = 201 以及自己的 seq 客户端确认信息,返回服务端的 seq + 1,建立TCP连接

第二次握手如果客户端没有收到服务端发送 SYN-ACK 报文怎么办呢?

此时服务端会进行重试,Linux 默认是等待 63s 后断开连接。
第一次等待 1 秒,第二次等待 2 秒,第三次等待 4 秒,第四次等待 8 秒,第五次等待 16 秒,再等待 32 秒后服务器会断开连接。
总共等待时间为 1 + 2 + 4 + 8 + 16 + 32 = 63 秒。

第三次握手ACK 确认包丢失怎么办

三次握手其实解决了第二步的数据包丢失问题。那么第三步的 ACK 确认丢失后,TCP 协议是如何处理的呢?

按照 TCP 协议处理丢包的一般方法,服务端会重新向客户端发送数据包,直至收到 ACK 确认为止。但实际上这种做法有可能遭到 SYN 泛洪攻击。所谓的泛洪攻击,是指发送方伪造多个 IP 地址,模拟三次握手的过程。当服务器返回 ACK 后,攻击方故意不确认,从而使得服务器不断重发 ACK。由于服务器长时间处于半连接状态,最后消耗过多的 CPU 和内存资源导致死机。

正确处理方法是服务端发送 RST 报文,进入 CLOSE 状态。这个 RST 数据包的 TCP 首部中,控制位中的 RST 位被设置为 1。这表示连接信息全部被初始化,原有的 TCP 通信不能继续进行。客户端如果还想重新建立 TCP 连接,就必须重新开始第一次握手。

你可能感兴趣的:(问题:TCP连接的三次握手)