有趣的三次握手和四次挥手问题
说到计算机网络,那就必须要提到TCP协议,谈到TCP协议,那就要提一提TCP协议里最有名的“三次握手和四次挥手”,对于这个知识点大家肯定或多或少在大学课程里,在面试中被问到过,所以我就想专门写一篇博客来好好地学习和记录一下我从中所学到的知识。
首先要清晰透彻的学习这个知识点之前我们要对TCP报文有所了解,因为会用到很多TCP豹纹的知识点,所以加入你不熟悉的话,可以移步到:点击这里查看TCP报文知识点
假如你已经对TCP报文有所了解,那么我们可以开始对三次握手和四次挥手的学习了。
三次握手:
三次握手的本质是什么呢,其实就是两个服务器之间的TCP通信的建立过程。
下首先看一张图:
这是一张三次握手的过程图,首先我们可以看出左边的是client,也就是客户机,右边的是server,也就是服务器,图上别的都是一堆奇怪的英文,但是有些我们感到很熟悉,没关系,下面我会详细讲一讲整个过程是如何的:
(1)第一步:客户机发送请求连接报文
首先我们能看到客户机向服务器发送了一条报文,SYN=1,seq=J,SYN大家一定都知道,当它等于1时说明这条报文时连接请求或者连接接受报文,这条报文明显是一条连接请求报文,seq大家也知道是序号,客户机随机生成了一个序号等于J,然后将这整个报文传给了服务器,服务器一看到这条报文就明白了客户机向其请求建立TCP连接,这时候客户机是SYN_Client状态,是一个发送状态。服务器接收后变为SYN_RCVD状态,也就是连接接受状态。
(2)第二步:服务器回复并同意连接
我们知道服务器知道客户机想要连接,那么它要给客户机“回一封信”,告诉客户机以下两点:
1.你的请求我收到了
2.同意连接
所以便有了第二步
我们来看一下这一步所传递的参数:
SYN=1表示这个报文是一个连接请求报文或者连接接受报文,ACK=1表明连接建立,ack=J+1表明这个报文是第一步报文的下一条内容回复,seq则是服务器随机产生的一个序号
(2)第三步:客户端回复并同意连接
第三次握手是客户机对服务器的一次回复,一共两个参数,ACK=1表明连接建立,ack=K+1表示其是第二次握手中的报文的回复。至此,三次握手全部完成,连接建立。
这里我想提几个有趣的点给大家分享:
(1)为何不是两次握手?
大家可能会发现假如没有第三次握手好像也是行得通的,真的是这样么?
大家想一下以下两个场景:
1.客户端发送了一个连接,但是由于网络阻塞,导致这个请求延迟了很久,过期了,客户端此时新发送了一个请求,但是刚好这时候老的请求先到达了,服务器收到后立刻回复了客户端一条同意连接的报文,假如是两次握手,此时连接已经建立了,这明显是不对的,三次握手情况下,客户端收到回信后根据ack号来判断发现这个连接是一个已经过期的连接,所以它会回复一条RST=1的报文,表示我们要终止这次连接,这就是一种很完善的机制,可以有效避免过期的连接占用资源。
2.又是网络阻塞问题,导致客户端发出的报文迟迟没有回复,这时候客户端就会不断地发送报文请求连接,假如是两次握手机制,刚才那些报文过一会儿到了服务器,服务器便发送大量连接成功报文给客户端,这时候建立了大量无效连接,极大地浪费了服务器资源,但是三次握手情况下,客户端回复时会将回复无效连接的报文中表示不建立连接,这便可以节省大量服务器资源。
(2)根据三次握手产生的SYN攻击
这个可能大家很感兴趣,这是一个关于网络攻击的知识点,具体原理是客户端伪造大量不存在的IP地址向服务器发送连接请求,这时候服务器收到请求后进行回复,但是由于IP地址是不存在的,所以造成服务器收不到回复,便会不断重新发送回复请求,这时由于所有的重发都占用了未连接队列,导致正确的SYN请求由于队列满了而被丢弃,从而引起网络堵塞导致网络瘫痪,SYN攻击是一种典型的DDOS攻击,检测方式是查看服务器是不是有大量半连接状态且IP地址随机,具体命令如下:
#netstat -nap | grep SYN_RECV
解决SYN攻击的思路是对Linux 内核的参数(半连接队列大小)做设置,当网卡接受请求数大于内核能够处理数量时,做出处理,如直接回复RST。
四次挥手:
四次挥手的过程如下图所示:
四次挥手实际上就是TCP连接断开的过程。
整个过程分为以下四步:
(1)第一步:客户端发起断开连接请求
客户端发送一个数据包此时FIN=1,并且seq=M
(2)第二步:服务器回复客户端请求
服务端收到第一步的请求后,发送一个ack=M+1的确认包,表示已经收到请求,此时服务器进入到CLOSE_WAIT状态,表示关闭等待。
(3)第三步:服务器发送FIN包,关闭server到clinet的数据传送
服务器发送一个FIN=1的包,并且seq=N ,并且服务器发送后变为LAST_ACK状态,意味最后确认
(4)第四步:客户端发送回复确认包,服务器收到后关闭
客户端发送确认包ACK=1表明连接还在持续,ack=N+1(图中错误)
关于四次挥手,有地下几个问题需要注意:
1.为什么是四次挥手而不是三次挥手?
这个问题其实是因为服务端在进行第二次握手和第三次握手时,第二次握手回复给客户端后可能还有余下的报文或者数据没有传送完毕,在这些数据和报文都传送完毕后,才会发送FIN包,来表示服务端的数局全部传送完毕,可以关闭连接了,等待最后一次确认后,服务器就关闭了。
2.客户端在完成第四次握手后会有一个 TIME_WAIT 等待的时间是 2MSL,在此结束后会close,请问为什么?
这个是因为由于网络延迟或者别的问题发生时可能有一些数据包没有传送到客户端或者延迟传送到客户端,MSL是报文最大生存时间,2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。说白了就是客户端的第四次握手传送出去后假如丢失了,服务器没收到将会重新发送FIN报文,也就是第三次握手的报文,这两个过程加起来最多是2MSL 的时间。