TCP的三次握手

TCP连接建立

  • 一、TCP的三次握手
    • 第一次握手
    • 第二次握手
    • 第三次握手
  • 二、为什么是三次握手?不是两次?四次?
    • 避免历史连接
    • 同步双方的初始序列号
    • 避免资源浪费
  • 三、为什么客户端ISN和服务端ISN不相同?
  • 四、初始化序列号ISN的生成方式
  • 五、IP层会分片,为什么TCP层还需要MSS
    • MTU与MSS
    • 原因分析
  • 六、SYN攻击
    • SYN攻击
    • 应对方法
      • 通过修改 Linux 内核参数,控制队列大小和当队列满时应做什么处理
      • syncookie

一、TCP的三次握手

第一次握手

客户端会随机初始化序号(client_isn),将此序号置于TCP首部的序号字段中,同时把SYN标志位置为1 ,表示SYN报文。接着把第⼀个SYN报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT状态。

第二次握手

服务端收到客户端的SYN报文后,首先服务端也随机初始化自己的序号(server_isn ),将此序号填入TCP首部的序号字段中,其次把TCP首部的确认应答号字段填⼊client_isn + 1 , 接着把 SYN 和 ACK 标志位置为 1 。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYNRCVD 状态。

第三次握手

客户端收到服务端报文后,还要向服务端回应最后⼀个应答报⽂,首先该应答报文TCP首部ACK标志位置为 1 ,其次确认应答号字段填⼊server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务器的数据,之后客户端处于 ESTABLISHED状态。 服务器收到客户端的应答报文后,也进⼊ ESTABLISHED状态。
TCP的三次握手_第1张图片

二、为什么是三次握手?不是两次?四次?

避免历史连接

客户端连续发送多次SYN建立连接的报文,在网络拥堵情况下:

  • ⼀个旧SYN报文比最新的SYN报文早到达了服务端
  • 那么此时服务端就会回⼀个SYN + ACK 报文给客户端
  • 客户端收到后可以根据自身的上下文,判断这是⼀个历史连接(序列号过期或超时),那么客户端就会发送RST 报文给服务端,表示中止这⼀次连接。
  • 如果是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可以在客户端(发送方)准备发送第三次 报文时,客户端因有足够的上下文判断当前连接是否是历史连接:
    • 如果是历史连接(序列号过期或超时),则第三次握手发送的报文是RST报文,以此中止历史连接
    • 如果不是历史连接,则第三次发送的报文是ACK报文,通信双方就会成功建立连接

所以,TCP使用三次握手建立连接的最主要原因是防止历史连接初始化了连接。

同步双方的初始序列号

序列号的作用

  • 接收方可以去除重复的数据
  • 接受方可以根据数据包的序号按序接受
  • 可以标识发送出去的数据包中,哪些已经被对方收到了

序列号在TCP连接中占据着非常重要的作用,所以当客户端发送携带初始序列号的SYN报文的时候,需要服务端回⼀个ACK 应答报文,表示客户端的SYN报文已被服务端成功接收,那当服务端发送初始序列号给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。

避免资源浪费

如果只有两次握手,当客户端的SYN请求连接在网络中阻塞,客户端没有接收到ACK报文,就会重新发送SYN ,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的ACK确认信号,所以每收到一个SYN就只能先主动建立一个连接,这会造成什么情况呢? 如果客户端的 SYN阻塞了,重复发送多次SYN报文,那么服务器在收到请求后就会建立个冗余的无效链接,造成不必要的资源浪费。

三、为什么客户端ISN和服务端ISN不相同?

如果⼀个已经失效的连接被重用了,但是该旧连接的历史报文还残留在网络中,如果序列号相同,那么就无法分辨 出该报文是不是历史报文,如果历史报文被新的连接接收了,则会产生数据错乱。 所以,每次建立连接前重新初始化⼀个序列号主要是为了通信双方能够根据序号将不属于本连接的报文段丢弃。 另一方面是为了安全性,防止黑客伪造的相同序列号的TCP报文被对方接收。

四、初始化序列号ISN的生成方式

起始ISN是基于时钟的,每 4 毫秒 + 1,转⼀圈要 4.55 个小时。
RFC1948 中提出了⼀个较好的初始化序列号ISN随机生成算法

  • M是⼀个计时器,这个计时器每隔 4 毫秒加 1。
  • F是⼀个 Hash 算法,根据源 IP、目的IP、源端口、目的端口生成⼀个随机数值。
  • 要保证Hash算法不能被外部轻易推算得出,用MD5算法是⼀个比较好的选择。

五、IP层会分片,为什么TCP层还需要MSS

MTU与MSS

TCP的三次握手_第2张图片

  • MTU:一个网络包的最大长度,以太网中一般为1500字节
  • MSS:除去IP和TCP头部之后,一个网络包所能容纳的TCP数据的最大长度

原因分析

当如果⼀个IP分片丢失,整个IP报文的所有分片都得重传。
因为IP层本身没有超时重传机制,它由传输层的TCP来负责超时和重传。 当接受方发现TCP报文(头部 + 数据)的某一片丢失后,则不会响应ACK给对方,那么发送方的 TCP在超时后,就会重发整个TCP报文(头部 + 数据)。 因此,可以得知由 IP 层进行分片传输,是非常没有效率的。
为了达到最佳的传输效能 TCP 协议在建⽴连接的时候通常要协商双方的MSS值,当TCP 层发现数据超过MSS时,则就先会进行分片,当然由它形成的IP包的长度也就不大于于MTU ,自然也就不用IP分片了。
经过 TCP 层分片后,如果⼀个TCP 分片丢失后,进行重发时也是以MSS为单位,而不⽤重传所有的分片,大大增加了重传的效率。

六、SYN攻击

SYN攻击

我们都知道TCP 连接建立是需要三次握手,假设攻击者短时间伪造不同IP地址的SYN报文,服务端每接收到一个 SYN报文,就进入SYN_RCVD状态,但服务端发送出去的 ACK + SYN报文,无法得到未知 IP主机的 ACK应答,久而久之就会占满服务端的SYN 接收队列(未连接队列),使得服务器不能为正常用户服务。

应对方法

通过修改 Linux 内核参数,控制队列大小和当队列满时应做什么处理

  • 当网卡接收数据包的速度大于内核处理的速度时,会有⼀个队列保存这些数据包。控制该队列的最大值如下参数:net.core.netdev_max_backlog
  • SYN_RCVD状态连接的最大个数: net.core.netdev_max_backlog
  • 超出处理能时,对新的 SYN 直接回报 RST,丢弃连接:net.ipv4.tcp_abort_on_overflow

syncookie

如果不断受到SYN攻击,就会导致SYN 队列被占满。 tcp_syncookies 的方式可以应对 SYN 攻击的方法: net.ipv4.tcp_syncookies = 1

  • 当SYN队列满之后,后续服务器收到SYN包,不进入SYN队列
  • 计算出⼀个cookie值,再以SYN + ACK 中的序列号返回客户端
  • 服务端接收到客户端的应答报文时,服务器会检查这个 ACK包的合法性。
  • 如果合法,直接放⼊到Accept 队列。 最后应用通过调用 accpet() socket 接口,从Accept 队列取出的连接。

你可能感兴趣的:(图解网络,TCP三次握手,ISN生成方式,IP分片,TCP分片,SYN攻击)