TCP协议的"三次握手"过程与超时重传机制详解
TCP协议的"三次握手"过程与超时重传机制详解
TCP协议是网络通信中非常重要的一种传输层协议,其"三次握手"过程是建立可靠连接的关键环节。本文将详细介绍TCP协议的"三次握手"过程、为什么需要三次握手,以及TCP协议的超时重传机制。
TCP的"三次握手"
TCP协议是有连接的传输层协议,即使用TCP协议通信,是需要建立连接的。TCP协议建立连接的过程,叫做"三次握手",这个过程具体是什么?为什么是三次?这两个问题可以分析一下。
TCP"三次握手"建立连接的过程,用图片展示是这样的:
注意,通信中发送的SYN、SYN+ACK、ACK,并不是发送报文中携带的数据,而是指发送的报文报头中设置的标记位。此图中,Client代表客户端,Server代表服务端,不同颜色的片段表示不同的状态。
那么,这个"三次握手"的过程就是:
- 客户端先发送连接请求,即 将TCP报文中SYN标记位设置为1,然后再将整个报文发送给服务端(一般情况下不会携带数据)。客户端发送了此报文之后,客户端进入SYN_SENT状态,表示已发送了建立连接的请求。
- 如果服务端收到了客户端发送的连接请求,那么服务端就会应答客户端的请求,即 将TCP报文中SYN和ACK标记位都设置为1,然后再将整个应答报文发送给客户端(同样一般不会携带数据)。服务端发送了此应答报文之后,服务端进入SYN_RCVD状态,表示已经应答了客户端的连接请求。
- 然后客户端就应该收到来自服务端的应答报文之后,客户端就需要再向服务端发送一个应答,即 客户端将TCP报文中ACK标记位设置为1,然后将报文发送给服务端。客户端发送了此次应答报文之后,客户端就会进入ESTABLISHED状态,客户端认为连接建立成功。
- 最后,服务端应该收到来自客户端的应答报文,收到之后,服务端不会再发送应答报文,而是进入ESTABLISHED状态,服务端认为连接建立成功。
"三次握手"的实际就是客户端和服务端在互相发送报文,用来确认连接的过程。
如何理解简单TCP的连接
连接,在网络中是一个比较抽象的概念。TCP协议是面向连接的,那么如何理解TCP连接呢?
一个主机是可以同时建立大量的连接的,那么操作系统就需要同时维护、管理大量的连接。按照以往操作系统管理大量进程、文件等的经验,操作系统一定会针对每个连接均维护包含此连接所有属性的结构体。不过,由于TCP连接的管理较为复杂,所以对应需要维护的结构体不止一个。
那么,也就是说,当客户端或服务端为了维护TCP连接创建了对应的结构体对象并已经完成了结构体内数据的填充,就表示客户端或服务端认为此次TCP连接已完成且成功。
为什么是"三次握手"
上面介绍了"三次握手"的过程,但是为什么是三次?
一次不行吗?
我们已经了解到,当服务端针对此次TCP连接创建并维护了对应的结构体对象并完成了结构体数据的填充时,服务端就认为连接建立完成。如果是"一次握手",就表示客户端发送SYN连接请求之后,就直接认为自己创建好了连接,服务端收到请求不需要应答,服务端就同样直接认为连接已建立。虽然"一次握手"也同样可能成功的建立连接,但是,如果只是"一次握手"就会出现一些问题:
- 客户端和服务端无法正确协定、同步双方的初始序号。TCP报头存在序号,此字段的初始值是在建立连接时,客户端和服务端互相协定、同步的。如果只是"一次握手",那么只能同步客户端的初始序号,因为只有客户端发送了携带初始序号的报文。
- 由于网络延迟,客户端可能多次发送连接请求,服务端就有可能多次建立连接。服务端多次建立了连接,即多次创建了 一些维护连接所需的结构体,但是只有一套是有效的。这样,会造成对服务端资源的无效占用。
两次不行吗?
如果是"两次握手",就表示客户端先发送SYN连接请求,服务端收到请求需要SYN+ACK应答,然后服务端认为连接建立完成,客户端收到服务端的应答之后,客户端认为连接建立完成。我们知道,客户端和服务端认为连接建立完成的标志是系统已经创建并填充完成了 一系列维护TCP连接所需的结构体。那么如果"两次握手",则是服务端系统先完成了创建并填充一系列维护连接所需的结构体。这就可能出现一个问题:如果客户端不断地发送请求,但是不接收服务端的请求,然后导致服务端不断地维护TCP连接,而客户端并不维护连接。这就实现了对服务端主机的攻击:服务端会不断地消耗时间和空间资源,用于维护TCP连接,而客户端不会。
并且,由于服务端依旧是仅接收一次客户端的报文就确认连接已建立,所以还可能会出现仅"一次握手"出现的问题:
- 由于网络延迟,客户端可能多次发送连接请求,服务端就有可能多次建立连接。服务端多次建立了连接,即多次创建了 一些维护连接所需的结构体,但是只有一套是有效的。这样,会造成对服务端资源的无效占用。
“两次握手”,理论上来说不会出现无法协定、同步通信双方初始序号的问题。因为,客户端发送连接请求可以携带初始序号,服务端进行应答也可以携带初始序号。即使存在网络延迟,导致客户端发送了多个连接请求。服务端也会针对多个连接请求一一进行应答。所有应答报文都会填充对应的确认序号和初始序号,所以客户端只要收到了应答报文,就可以确认服务端应答的目标以及服务端的初始序号。然后,连接建立成功。如果,客户端没有收到应答报文,那就意味着连接还没有建立成功,客户端可能会继续发送请求,直到成功接收应答报文。
如果是三次呢?
"三次握手"的过程已经简单的分析了一下。从图中可以看到,Client率先进入了ESTABLISHED状态,也就是说Client率先完成了维护TCP连接操作。然后,Server进入了ESTABLISHED状态,这样让客户端先完成维护连接的操作,可以避免像"两次握手"那样服务端被攻击,至少客户端也要付出相同的代价。其次,因为在Client和Server进入ESTABLISHED状态之前,都经历了一收一发,所以不会出现无法正确协定和同步双方初始序号的情况。并且,"三次握手"通过三次报文传输,顺便完成了客户端的发送(SYN请求)和接收(SYN+ACK应答)能力的检测以及服务端的发送(SYN+ACK应答)和接收(ACK应答)能力的检测。"三次握手"是可以完成上面这些功能的最少的次数,如果"四次握手"或更多次数的握手,也只是徒增连接消耗罢了。
协定、同步双方初始序号
上面展示"三次握手"过程的图,没有展示出通信双方同步初始序号的过程。"三次握手"过程可以这样展示:
- SYN
- ACK
大写的,表示设置的标记位
- seq
- ack
小写的,表示序号和确认序号
整个协定、同步初始序号的过程是:
- 客户端发送连接请求,携带随机初始序号的seq = x
- 服务端收到请求,读取到客户端的初始序号,应答报文携带随机初始序号的seq = y,且填充确认序号ack = x+1
- 客户端收到应答,读取确认序号确认服务端已同步客户端初始序号,同时读取到服务端的初始序号,然后应答报文填充序号seq = x+1和确认序号ack = y+1。客户端确认连接建立
- 服务端收到应答,读取确认序号确认客户端已同步服务端初始序号。服务端确认连接建立
整个过程中,客户端和服务端,都是经过一发一收读取收到确认序号之后,才确认的初始序号已同步。"一次握手"和"两次握手"无法完善这个过程。了解了"三次握手"的过程,回到上面提到的一个问题:发送方如何在第一次发送数据之前,就知晓接收方的窗口大小呢?这个答案就是:双方会在"三次握手"阶段对窗口大小进行交换、同步。
TCP的超时重传机制
TCP的超时重传机制表示,在TCP通信中,如果一端长时间没有收到来自对端的应答,那么就会重新发送没有收到应答的报文。但是,长时间没有收到对端应答有两种情况:
- 报文根本就没有发送到对端,在传输过程中丢包了
- 对端接收到报文了,但是对端的应答却在传输的过程中丢包了
这两种情况的区别是:
对端没收到数据
对端收到了数据
对端没有收到数据
此时,只需要在超时之后将报文重新发送给对端就可以了
- 对端收到了数据
如果对端已经收到了数据,但是对端的应答报文丢了。那么,当报文重新发送给对端之后,对端会再次发送应答报文。但是,此时对端就会接收到重复的数据。但重复的报文、数据是没用的需要丢弃,所以TCP协议需要有能力识别接收的报文是否重复。这就要用到TCP报文的序号字段。只要两个报文的序号字段相同,就说明收到了相同的报文。TCP协议的超时重传机制,说明了TCP报文在发送出去或接收到之后,并不会立刻丢弃,而是会存储一段时间。这也是TCP超时重传机制的基础。
那么,TCP如何界定是否超时?最理想的情况,就是可以找到一个最短的时间,保证此次发送之后"确认应答一定能在这个时间内返回"。但是,网络环境是会变化的,所以这个最短的时间也是不可能固定下来的。所以Linux中TCP协议就需要自行的界定、计算超时边界。不过,重传不会一直进行,当重传累计到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接。
关于超时的设定:
- 如果超时时间设的太长,会影响整体的重传效率
- 如果超时时间设的太短,有可能会频繁发送重复的包
至此,本篇文章主要内容结束。感谢阅读~