本文正在参与 “网络协议必知必会”征文活动
基本概念
在了解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的报文段不能携带数据
三次握手
关于三次握手,我们先来看看这三次握手的流程:
注:图片来源于参考的第二篇文章,侵权删
首先我们知道,客户端其实是通过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四次挥手流程图
注:图片来源于参考的第二篇文章,侵权删
整个流程其实和三次握手大同小异,所以我们直接进入问答环节:
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连接的建立为什么需要三次握手?为什么不能两次或者四次?
本文转载自: 掘金