老生常谈的TCP三次握手和四次挥手,你会了吗?

本文正在参与 “网络协议必知必会”征文活动

基本概念

在了解TCP的三次握手和四次挥手前,我们首先需要了解一些基本概念,更有助于我们理解。

1、TCP序号:

TCP序号的作用是确定数据发送的顺序,比如HelloWorld可能会被切成三个数据包发送,而服务端是如何确定这三个数据包的顺序呢?这里依靠的就是TCP序号,详细内容请戳这里

下面要讲的内容会涉及到两种序号,分别是seq序号和ack序号,seq序号标志了数据包的顺序ack序号则是确认序号,用于确认数据包是否被接收。

2、TCP报文标志位:

  • SYN:synchronous。当SYN为1时,表示发起一个连接
  • ACK:acknowledgement。当ACK为1时,表示收到数据包(注意这里的ACK是标志位,与上文的ack序号是不一样的)
  • FIN:finish。当FIN为1,表示结束一个连接

注:TCP规定当SYN被置为1的报文段不能携带数据

三次握手

关于三次握手,我们先来看看这三次握手的流程:
66135950d3ca40d0aa69608eb88432da.png

注:图片来源于参考的第二篇文章,侵权删

首先我们知道,客户端其实是通过IP+端口找到对应的应用的,所以服务器这一端必须先启动一个端口监听请求(即LISTEN)

第一次握手:从图中可以看到,第一次握手发送的数据包是SYN=1, seq=x,表示客户端请求与服务端建立连接。SYN=1的作用前面已经提过,表示发起一个连接,而seq=x则是一个序号,用来表示包的顺序。

第二次握手:假设网络畅通,服务端接收到了SYN=1, seq=x,这时它知道客户端想建立连接,为了让客户端知道服务端这边一切正常,允许建立连接,所以服务端发送了SYN=1, ACK=1, seq=y, ack=x+1的报文。

这段报文SYN=1的作用是告知客户端,服务端允许建立连接;ACK=1的作用是告知客户端,服务端已经收到之前第一次握手的报文;seq=y是服务端产生的TCP序号,以便第三次握手时进行确认;ack=x+1的作用是确定这段报文来自其请求的服务器,而不是其他服务器,因为seq并不是一个确定的数。

第三次握手:这次握手客户端会发送ACK=1, seq=x+1, ack=y+1的报文,以便让服务端知道客户端这边也准备好了,连接建立成功,可以发数据了。


整个流程其实理解不难,接下来进入经典问答环节:
1、为什么是三次握手,不能二次握手吗?

大家可以想想,建立TCP连接的目的其实是为了客户端和服务端之间能够互相传递数据,所以在传递之前,即建立连接之前,客户端和服务端必须确定对方都具备发送和接收数据的能力(双方都必须具备发送和接收数据的能力,再强调一遍),这个是理解的重点!!

如果只有两次握手,对于客户端,它是知道服务端具备发送(第二次握手)和接收(第一次握手)的能力的,但是!!!服务端只知道客户端具备发送的能力,并不知道它是否能接收数据,这就是为什么要进行第三次握手的原因。

2、什么是SYN攻击?

由上图可知,第二次握手后,服务端处于SYN_RCVD状态,当收到第三次握手的ACK后,状态才变为ESTABLISHED状态。

SYN攻击就是在短时间内伪造大量不存在的IP地址(这里的IP指的是客户端的IP),并向服务端不断发送SYN包(即第一次握手),服务端收到SYN包肯定会进行响应,但由于客户端IP不存在,收不到ACK包,所以服务端会不断重发数据包直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列慢而被丢弃,从而引起网络堵塞,客户端的感觉就是很卡,或者访问奔溃。

四次挥手

关于四次握手,我们继续看TCP四次挥手流程图
b2334da4fc78469184e49534d38511e1.png

注:图片来源于参考的第二篇文章,侵权删


整个流程其实和三次握手大同小异,所以我们直接进入问答环节:

1、为什么要四次挥手?两次或者三次不行吗?

这个是因为TCP连接虽然每次只处理一个请求,但这个请求是包含多个数据包的往来的,即在一个请求中,客户端有可能向服务端发送了多个数据包。

这也就是意味着服务端并不知道客户端的数据包什么时候发送完毕,所以一般关闭连接由客户端发起。(只是一般情况下),这也对应了我们上图的第一次挥手。这里还有一个注意点,第一次挥手,客户端只是停止发送数据,但还是会接收数据的。

当服务端收到了第一次挥手的数据包,如果它没有数据需要发送给客户端,它就可以同时关闭发送数据和接收数据的通道,因为客户端不会再发数据给它,它也没有数据给客户端。但它要告知客户端可以关闭接收的通道了,所以就有了第二次挥手。

所以,如果在第一次挥手后,服务端没有数据需要给客户端,两次挥手是可以关闭TCP连接的(但这时的第二次挥手会多一个FIN标志位)!

但是!!!

如果在第一次挥手时,服务端还有数据发送给客户端,此时第二次挥手只是告知客户端,服务端已经收到客户端关闭的请求了,但由于还有数据需要发送,所以第二次挥手并没有FIN标志位。

当服务端数据发送完了,它就会进行第三次挥手,告知客户端你可以关闭接收数据的通道了,客户端收到信息后,进行第四次挥手,告知服务端我这边已经关闭了,你也可以关闭了。此时TCP连接正式断开!

2、TIME-WAIT状态的作用?

细心的人或许还能发现,上面的四次挥手中,客户端还有一个TIME-WAIT的状态,在等待2MSL后,才会彻底断开连接。

之所以出现TIME-WAIT状态,有两个原因:

1.当第二次挥手的数据包比第三次挥手的数据包晚到达,客户端接收的数据就不完整,所以有个延时(TIME-WAIT),可以解决大部分丢包的问题

2.可能会由于网络问题,客户端第四次挥手的报文丢失,或者服务端比较晚收到,这时服务端处于last-ack状态,会一直重试第三次挥手的报文,如果这时候客户端的连接直接断开,没有等待,那么就无法响应。
当客户端再次发起建立连接的请求时,服务端由于处于last-ack状态,就会发送RST报文拒绝连接。

综上,这就是TIME-WAIT状态存在的原因。

参考

Tcp协议的三次喔手和四次挥手

TCP连接的建立为什么需要三次握手?为什么不能两次或者四次?

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%