三次握手
假设运行在客户端的一台主机上的进程,想与另一台服务器端的进程建立一条连接,客户中的TCP
会用以下方式与主机的TCP
建立联系。
第一次握手——客户端–>服务器
客户端的TCP
首先向服务器端的TCP
发送一个特殊的TCP
报文段。该报文段不包含应用层数据。但在报文段的首部行的SYN
标志位置1。另外,客户会随机选择一个初始的序号(client_isn)并将该序号字段放在该其实的TCP
SYN
报文段的序号字段。该报文段会封装在一个ip数据报中,并发送给服务器。
此时,客户端的
TCP
进入了SYN_SENT
状态。在这种状态下,客户TCP
等待来自服务器TCP
的对客户所发报文段的进行确认且SYN
比特被置为1的报文段。
第二次握手——服务器–>客户端
接收到第一次握手的报文段,服务器应当做什么?
一旦包含TCP
SYN
报文段的数据报到达服务器(假设它一定到达了)网络层会将数据报中运输层的数据交付给运输层,服务器将会从这些数据中提取TCP
SYN
报文段,为该TCP
连接分配TCP
缓存和变量,并向该客户TCP
发送允许连接的报文段。这个报文段也不包含应用层数据,这个报文段被称为SYNACK
报文段。
服务器发送的报文段中包含着什么?
报文段的首部行包含着三个特别重要的信息:
SYN
比特被置为1。- 该
TCP
报文段首部的确认字段被置为client_isn+1
。 - 最后服务器选择自己的初始序号(
server_isn
),并将其放入TCP
报文段首部的序号字段中。
这个允许连接的报文实际上表明了:我收到了你发出的SYN
分组了,该分组带有client_isn的初始序号,我的初始序号是server_isn
。
即在第二次握手结束后,客户端和服务器都为这条
TCP
连接分配了资源,并且此时在客户端和服务器端都形成了一种半开连接。但是这种半开连接是有着漏洞的,而这种漏洞便给了SYN
- Flood攻击以可乘之机。
第三次握手——客户端–>服务器
在收到SYNACK
报文段之后,客户端也要为这次的TCP
连接分配缓存和变量。客户主机则向服务器发送另一个报文段,这个报文段对服务器的允许连接的报文段进行了确认(将server_isn
+1的值放入TCP
报文段首部行的确认字段来完成此项工作)。因为连接已经建立,所以SYN
比特被置0,该报文段可以携带客户到服务器的数据了。
当收到
SYNACK
报文段后,客户TCP
变为ESTABLISHED
的状态。当处在这个状态时,TCP
客户就能够发送和接收包含有效载荷数据(即应用层数据)的TCP
报文了。
常见问题
为什么需要第三次握手?
第三次握手看似多余其实不然,这主要是为了防止已失效的请求报文段突然又传送到了服务端而产生连接的误判。
比如:客户端发送了一个连接请求报文段A到服务端,但是在某些网络节点上长时间滞留了,而后客户端又超时重发了一个连接请求报文段B该服务端,而后 正常建立连接,数据传输完毕,并释放了连接。
但是请求报文段A延迟了一段时间后,又到了服务端,这本是一个早已失效的报文段,但是服务端收到后会误以为客户端又发出了一次连接请求,于是向客户端发出确认报文段,并同意建立连接。
那么问题来了,假如这里没有三次握手,这时服务端只要发送了确认,新的连接就建立了,但由于客户端没有发出建立连接的请求,因此不会理会服务端的确认,也不会向服务端发送数据,而服务端却认为新的连接已经建立了,并一直等待客户端发送数据,这样服务端就会一直等待下去,直到超出保活计数器的设定值,而将客户端判定为出了问题,才会关闭这个连接。这样就浪费了很多服务器的资源。
而如果采用三次握手,客户端就不会向服务端发出确认,服务端由于收不到确认,就知道客户端没有要求建立连接,从而不建立该连接。[^1]
第 3 次握手失败会怎么办?
第三次失败,只有客户端处于成功状态(因为第 2 次服务器返回了 ACK
),服务器端没有接收到客户端的 ACK
。
这要分几种情况讨论:
ACK
丢失,但下一个包含数据的报文段没有丢失。则服务端接收到下一个数据包(这个数据包里也会带上
ACK
信息),能够进入正常的ESTABLISHED
状态客户和服务器之间没有数据交换,或者服务器想发数据。
首先服务器发不了数据,因为少了客户端的
ACK
报文段。其次,服务器都会有定时器发送第二步SYNACK
数据包,如果客户端再次发送ACK
成功,建立连接。如果一直不成功,服务器肯定会有超时设置,超时之后会给客户端发
RTS
报文,进入CLOSED
状态,防止SYN
洪泛攻击。[^1]
四次挥手
天下没有不散的宴席,对于TCP
连接也同要如此。参与TCP
连接的任意一方都有权终止连接,在连接终止后,与连接相关的资源(包括变量和缓存)将被释放。
假设现在有一条TCP
连接,客户端想要拆除这条连接,应该怎么做?
第一次挥手——客户端–>服务器
客户应用进程发出一个关闭连接的命令,这会引起客户TCP
向服务器进程发送一个特殊的TCP
报文段。在这个报文段中,其首部行标志位中的FIN
比特,将被置1。
此时,客户端进入FIN_WAIT_1状态。在这种状态下,客户
TCP
连接等待一个来自服务器的带有确认的TCP
报文段。
第二次挥手——服务器–>客户端
当服务器接收到这个特殊的TCP
报文段时,它会向客户端回传一个ACK
的确认报文段。
当客户端收到这个
ACK
报文段时,客户端将从FIN_WAIT_1状态进入FIN_WAIT_2状态。在这种状态下,客户端等待收到一个来自服务器端的FIN
比特被置为1的报文段。
第三次挥手——服务器–>客户端
在服务器发送过ACK
报文段后,它会向客户端发送一个FIN
比特置为1的TCP
报文段。
当客户端收到该报文段时客户
TCP
将进入TIME_WAIT
状态。
第四次挥手——客户端–>服务器
客户端回传一个ACK
报文段。此时,所有资源将被释放。
由于此时客户端在
TIME_WAIT
状态,假设回传的ACK
报文丢失,TIME_WAIT
会使TCP
重传最后的确认报文,并进行等待。在一定等待时间过后,连接将正式关闭,所有资源被释放。
TIME_WAIT
状态
TIME_WAIT
状态也被称为2MSL
状态,它处于主动关闭的一方在发送完对对方FIN报文的确认(ACK
)报文后的状态。在这个状态下,TCP
会等待两倍于最大报文段生存期的时间,它代表任何报文段在被丢弃前在网络中,被允许存在的最长时间。这个时间是有限制的,因为TCP
报文段是以ip数据报的形式传输的,ip数据报拥有ttl和跳数限制字段,而这限制了IP数据报有效生存时间。
假设已经设定了MSL的数值,按照规则:当TCP
执行一个主动关闭并发送最终的ACK
时,连接必须处于TIME_WAIT
状态并持续两倍于最大生存期的时间。这样就能让TCP
重新发送最终的ACK
以避免丢失的情况。
应当注意,重发ACK
并不是因为ACK
丢失(不消耗序列号,也不引起重传),而是因为通信的另一方重传了FIN
。
另一个影响2MSL
等待时间的因素是,当TCP
处于TIME_WAIT
状态时,通信双方将该连接定义为不可重新使用。除非:
2MSL
等待时间结束时;- 一条新连接使用的初始序列号超过了之前连接实例的最高序列号;
- 允许使用时间戳选项来区别之前连接实例的报文段来避免混淆时;
这条连接才能够重新使用。[^2]
常见问题
为什么连接的时候是三次握手,关闭的时候却是四次握手?
- 当服务器端收到客户端的
SYN
连接请求报文后,可以直接发送SYNACK报文。其中ACK
报文是用来应答的,SYN
报文是用来同步的。 - 关闭连接时,因为
TCP
提供可靠数据传输服务,而服务器端有可能在接收到客户端的FIN
报文时,还有剩余的报文段没有传送。所以只能回传一个ACK
报文段,待服务器端将剩余的报文段发送完毕,服务器端才能够发送FIN
报文来拆除TCP
连接。[^4]
为什么存在TIME_WAIT
状态?
可靠地终止
TCP
连接就像我在第四次挥手中说的那样,假设服务器将重发
FIN
报文段,因此客户端需要停留在TIME_WAIT
状态以处理重复收到的结束报文段,即向服务器发送确认报文段。保证让迟来的tp报文段有足够的时间被识别并丢弃。
当一个
TCP
连接处在TIME_WAIT
状态是,我们将无法立即使用该连接占用着的端口来建立一个新连接。反过来思考,如果不存在TIME_WAIT
状态,则应用程序能够立即建立一个和刚关闭的连接相似的连接(具有相同ip地址和port)。这个相似的连接被称为原来链接的化身。而这个化身可能收到属于原来连接的携带者应用程序数据的
TCP
报文段,这是很危险的,这也显然是不应当发生的。[^4]
FIN_WAIT_2状态会一直保持下去吗?
只有当客户端收到第三次挥手的的FIN
报文段时,FIN_WAIT_2状态才能够转到TIME_WAIT
状态,而却带来了连接的一段能够永远保持这种状态,而这种情况带来了死锁的危险。
为防止这种情况的发生,比如设置一个定时器,当定时器超时时,连接处于空闲状态,TCP
连接就会转入CLOSED状态。[^3]
尾巴
本文的主体参考了《计算机网络自顶而下方法》原书第七版,同时参考了知乎和csdn的一些答主和博主的一些文章。TIME_WAIT
状态参考了《TPC/IP详解》。
REFERENCE
[^1]: Java面试知识点:TCP
三次握手和四次挥手协议 - java喵的文章 - 知乎
[^2]: TCP
/IP详解卷1:协议 原书第二版.P442-445
[^3]: TCP
/IP详解卷1:协议 原书第二版.P446
[^4]: TCP三次握手和四次挥手以及缺陷(详细)专栏: 网络安全版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
[^5]: 计算机网络自顶而下方法 原书第7版. P166-168