为什么TCP需要三次握手,四次挥手?


TCP是一个面向连接的,可靠的,字节流的,传输层的协议,在数据发送前,通信双方需要建立 连接来保存一份关于对方的信息。

TCP连接三个阶段:建立连接,数据传输,关闭连接。三次握手就是为了建立连接,四次挥手就是为了关闭连接

为什么需要三次握手?

握手是为了建立连接,方便后期传送数据。因此,我们需要确认客户端和服务器端 都具有正常的 接收和发送报文 的能力,并且在握手的过程中互相交换信息,进行基本的认识

下面列出大概的流程:

在这里插入图片描述

  • 第一次握手,客户端发送连接请求到服务端,服务端能够收到请求,说明 服务端的接收能力 和 客户端的发送能力 都没问题。(不过服务端知道客户端的发送没问题,但是客户端自己不知道,因此需要第二次握手,服务端来告诉客户端,你的请求,我收到了,你的发送没问题。同时,我想验证一下我的发送有没有问题)
  • 第二次握手,客户端收到服务端的响应 ,说明 客户端知道 自己的接收能力 和 服务器端的发送能力 是没问题的。但是服务器自己不知道是否发送成功。因此,需要第三次握手。
  • 第三次握手,客户端告诉服务端,你的发送能力是没问题的。

三次握手完成之后,服务端和客户端都知道自己以及对方的发送和接收能力都是OK的,就可以正常传输数据。

看懂上面的逻辑了以后,就可以来看看正式的请求了

三次握手

在三次握手之前,先介绍一下TCP的6种标志位:

  • SYN(synchornous) 建立联机
  • ACK(ackonwledgement) 确认
  • FIN(finish) 结束
  • RST(reset) 重置
  • URG(urgent) 紧急
  • PSH(push) 传输

三次握手过程:

在这里插入图片描述
seq (Sequence number) 序列号
ack (Acknowledge number) 确认号,这个和大写的ACK记得区分开
x , y: 随机生成的数字

  • 最开始都处于closed状态
  • 客户端向服务端发送建立连接请求,报文头中SYN=1,seq=x,客户端进入SYN-SENT状态。此时不能携带数据,但是需要消耗一个序列号。
  • 服务端接收到请求后,需要发送SYN以及确认ACK给客户端,报文头中 SYN=1,seq = y,ACK=1,ack=x+1, 服务端处于SYN-RCVD状态。这时也不能携带数据,同样要消耗一个序列号
  • 客户端接收到SYN+ACK后,发送一个ACK给服务端,报文头 ACK=1,seq=y+1。客户端在收到SYN+ACK后,确认自己已经具备发送和接收数据的能力,就进入ESTABLISHED状态, 因此在第三次握手的时候,可以携带数据,如果不携带数据,则不消耗序号。
  • 服务端收到客户端的响应后,也确认自己具有发送和接收的能力,因此调整状态为ESTABLISHED

三次握手完成以后,服务端和客户端就可以进行数据传输了

为什么不是两次或者四五六次握手呢?

如果是两次:

​不能确认客户端是否收到了服务器返回的SYN+ACK。

  • 举个栗子1:客户端给服务端发送了SYN后,客户端突然断网,服务端接收到了连接请求,返回客户端一个SYC+ACK后就建立连接,但是此时客户端已经断网了,这个连接建立起来客户端不会有任何的回应,这就是资源的浪费。
  • 举个栗子2: 客户端给服务端发送请求,由于网络原因,请求一直没有到达,经过很长一段时间,客户端已经不需要该请求了,但是网络变好了,该请求继续发送到服务端,服务端接收到请求后建立连接,但是此时,客户端不需要这个请求了,因此也存在资源的浪费。

如果是多次:
​ 四五六次甚至100次都是可以的,但是最精简高效的次数是三次,更多的话,没有什么必要。

为什么需要四次挥手

挥手是为了确认服务端和客户端都已经发送完数据,都想关闭连接,TCP连接是全双工、对等的模式,因此想要关闭,必须是双方都想关闭的状态,才能断开连接。

在这里插入图片描述

  • 第一次挥手,客户端告诉服务端,我没有数据想要发送了,我们可以关闭连接了。服务器端收到消息后,但是现在很忙,出于礼貌性,得回复客户端,我收到你的请求了,因此需要第二次挥手。
  • 第二次挥手,服务器端告诉客户端,我收到你的关闭请求了,但是,我现在还有数据没有发完,我还不能关闭,你再等等。
    客户端收到服务端的回复以后,就让自己处于半关闭状态,不发送数据,但是可以接收数据。
    等到服务器端发送完成以后,就需要告诉客户端,我发完了,因此需要第三次挥手。
  • 第三次挥手:服务端告诉客户端,我的数据发送完了,咱们可以关闭连接了。客户端收到以后,需要回复服务端,好的,所以第四次挥手就来了。
  • 第四次挥手: 客户端告诉服务端,你的关闭连接请求我收到了,你可以关闭连接了。

服务端收到第四次挥手消息后,就立即关闭连接了。但是客户端不确定服务端是否收到了第四次挥手的消息,因此他就等一定的时间,如果再也没有收到服务端的消息,他才关闭。打个比方: 已经12点了,你的朋友给你发了晚安,你回了一句晚安,她看到后就去睡了,但是你不知道她到底睡没睡,所以等了那么一会,估计她应该是睡了,然后你也去睡了。

四次挥手

在这里插入图片描述

v: 客户端上一次发送的序列号+1
w:服务端上一次发送的序列号+1
x: 由于在CLOSE-WAIT这段时间,服务端可能继续向客户端发送数据,因此x为最后一条数据的序列号+1。如果中间不存在数据的发送,则序列号为w+1

  • 刚开始都处于连接状态,进行数据传送

  • 客户端主动发起了关闭连接的请求,发送连接释放报文:FIN=1,seq=v。然后客户端就进入FIN-WAIT-1状态。此时FIN报文即使不携带数据,也要消耗一个序列号

  • 服务端接收到这个FIN后,服务端关闭读通道,并回复一个ACK:ACK=1,ack=v+1,seq=w。进入CLOSE-WAIT状态。当然也占用一个序列号。

    客户端在收到服务端的ACK后,关闭写通道,进入FIN-WAIT-2 的状态,等待服务端发送释放报文。此时客户端处于半关闭,不能发送数据,但是可以接收数据。

  • 当服务端处理完成以后,发送释放报文到客户端:FIN=1,seq=x,ACK=1,ack=v+1。服务端进入LAST-ACK状态,等待客户端的回复。

  • 客户端收到服务端的释放报文后,关闭读通道,并给服务端返回一个ACK: ACK=1,ack=x+1,seq=v+1。进入TIME-WAIT状态。

服务器收到客户端的ACK后,关闭写通道,并且直接CLOSED连接

TIME-WAIT一般为2个MSL(报文最长寿命),如果在这个时间段内没有收到服务端的超时重发,说明客户端发过去的ACK服务端收到了并且已经关闭连接,客户端才进入CLOSED状态。

那为什么是两个MSL呢:

第一个MSL 是保证最后一次挥手客户端响应服务端的ACK到达了服务端

第二个MSL 是保证服务端没有重发新的报文给客户端,没有超时重传

即客户端发过去了,服务端也没有重传请求过来,说明它已经收到了,那么就可以关闭了。

如果不等待2*MSL会造成什么后果呢?

  • 客户端返回ACK (第四次挥手) 后直接关闭,可能因为网络延迟导致服务端收不到ACK,一段时间后,服务端认为客户端没有收到FIN请求(第三次挥手),进行超时重发,但是客户端已经关闭了,不会给响应。理论上来说 服务器超时重发5次后,就会主动断开连接,这样数据既不会丢失也不会错乱,是可以的,但是这样不符合可靠连接
  • 如果客户端直接关闭,然后向服务器建立新连接,如果新连接和老连接的端口是一样的。假设老连接还有一些数据,因为网络或者其他原因,一直滞留没有发送成功,新连接建立后,就直接发送到新连接里面去了,造成数据的紊乱,因此,我们需要等到2*MSL,让滞留在网络中的报文失效,再去建立新的连接。

为什么不是三次挥手呢?

服务端在收到客户端的释放报文时,可能自己的数据报还没有发完,所以不会直接返回FIN+ACK,而只先返回一个ACK,表示自己收到了客户端的释放请求。等到服务端报文发完以后,在返回FIN。

那么,我们是否可以在服务器端数据传送完成后,再返回FIN+ACK呢?中间就可以省略一次ACK了?

试想一下,如果服务端还有很多数据需要传送,耗时长,客户端在发送释放报文后,一直没有收到反馈,那么他会认为服务端没有收到我的FIN,因此就会不停的重发FIN。

所以最好的办法就是,客户端发送FIN,服务端回复ACK,表示我已经收到了,但是我在忙,你等等,我处理完成后联系你。服务端数据传送完成后,发送FIN给客户端,客户端再回复ACK。

什么是SYN Flood(洪水) 攻击

再理解这个之前,我们先来搞明白三个概念

半连接队列和全连接队列、SYN Timeout

半连接队列: 客户端发送SYN到服务端,服务端收到了SYN并返回一个SYN+ACK,在客户端收到SYN+ACK之前,这些处于SYV-RECV状态的TCP连接被成为半连接,并存放在内核的半连接队列中。

全连接队列: 客户端发送一个ACK到服务端(第三次握手)后,服务端收到ACK,三次握手完成,TCP连接建立,它会查找半连接队列,将符合条件的request_sock信息存储到全连接队列中,并将其从半连接队列中删除。

SYN Timeout:假设客户端发送SYN请求(第一次握手)后死机或者断线,服务端收到请求后返回的SYN+ACK (第二次握手)给客户端,但是客户已经死机,不能应答,即第三次握手没法完成。
但是服务端不知道客户端已经死机了,它只会认为它的SYN+ACK没有到达客户端,这种情况下,服务端会进行请求重发,默认情况下Linux系统中,服务端会发送重发五次SYN+ACK包,第一次等待时间为1秒,之后每次时间为前面时间的两倍,因此五次分别是 1s、2s、4s、8s、16s,第五次发出后等待32秒,没有响应,那么第五次也超时了,一共需要 1+2+4+8+16+32 = 63s后,TCP就会把这个连接断开。这个 63s我们称之为 SYN Timeout

SYN Flood攻击

理解了上面三个概念后,我们就可以很好的理解洪水攻击了。

由于 半连接队列的大小是固定的,因此能够存入半连接队列的TCP连接是有限的,如果队列已经满了,那么多余的TCP连接将会被内核丢弃,只有当半连接队列有空间时,才会继续接收新的TCP连接。

半连接队列的释放有两种情况:

  • TCP三次握手完成,进入全连接队列,释放半连接队列中对应的数据
  • TCP连接超时63s,表明客户端可能死机或者断线,TCP主动断开连接,释放半连接队列中对应的数据

SYN Flood攻击是指伪造大量的客户端发送SYN请求到服务端,然后服务端返回SYN+ACK,并将该TCP连接放在半连接队列中,占用资源。

但是由于客户端的IP地址是伪造的,收不到服务端的SYN+ACK,客户端就不能响应ACK,因此服务端就进行超时重传,直到SYN Timeout时间后,中断该TCP连接,释放资源。

那么在服务端主动断开连接之前,半连接队列的资源都被攻击者伪造的TCP半连接占用,正常的连接请求到达时,由于半连接队列已经满了,因此正常的请求会被丢弃,导致目标系统不能很好的提供服务。

SYN Flood是一种典型的Dos攻击(denial of service拒绝服务攻击)

防御SYN Flood攻击

1 缩短SYN Timeout

2 增加半连接队列的空间,让半连接的数量增加

3 过滤网关防护SYN

4 SYN Cookie

下面将一下第四种的基本原理吧,SYN Cookie是通过对TCP服务器端的三次握手进行一些修改:

当TCP连接处于SYN-RECV状态的时候,不在内核中分配专门的数据区(即 半连接队列)去存储,而是根据这个SYN包计算出一个Cookie值,这俄格Cookie作为要返回的SYN+ACK的初始系列号。当客户端返回ACK包时,根据包头信息计算Cookie与返回的确认序列号(初始序列号+1)进行对比,如果相同,则正常连接。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值