“三次握手”,参考:http://www.javashuo.com/article/p-ycpsakky-nt.htmlhtml
前面进行“三次握手”创建链接后,当客户端的数据发送完毕,它就会要求与服务器端断开链接,那么就要进行“四次挥手”进行链接的释放。服务器
注意,此处所谓的“客户端”与“服务器端”,只是为了方便标识链接的双方,即确认哪一方是“要求断开链接”的主动方,哪一方是“要求断开链接”的被动方。事实上任何一方均可能在发送完数据后要求与另外一方断开链接。网络
一、“四次挥手”过程spa
以下图:htm
“四次挥手”的具体过程以下:blog
1)“第一次挥手”:首先,客户端已经发送完数据,想要释放链接(客户端是释放链接的主动方),向服务器端发送一段TCP报文,其中:资源
i)标记位 FIN=1:表示这个TCP请求是“请求释放链接”;get
ii)报文的序号 seq=u:u 等于前面客户端已经传送的数据的最后一个字节的序号加1;it
iii)客户端由“ESTABLISHED”链接创建状态,进入“FIN-WAIT-1”终止等待状态1。此时客户端不会再向服务器端发送数据。服务器端
须要注意,FIN报文虽然不携带数据,可是可是它会消耗一个字节序号。即客户端第一次发送的FIN报文 seq=u,报文会消耗一个序号,那么客户端下次发送的报文应该从 seq=u+1 开始发送。
2)“第二次挥手”:服务器端接收到从客户端发出的TCP报文以后,确认了客户端想要释放链接,会发送回一个TCP报文,其中:
i)标记位为ACK=1:表示“服务器端告知客户端,本身已经接收到客户端发送的释放链接的请求”;
ii)报文序号 seq=v:服务器端到客户端的链接尚未关闭,服务器端还会向客户端发送数据包,这个报文序号为 v;
iii)确认号ack = u+1:表示但愿客户端下一个报文的序号是 u+1,即但愿客户端下一个报文从序号为 u+1 的字节开始发送。咱们知道客户端第一个报文 seq=u,且该报文为FIN报文,占一个序号,那么客户端下一个报文就应该从 u+1 开始发送。
iv)服务器端结束 “ESTABLISHED” 链接阶段,进入“CLOSE-WAIT” 关闭等待状态;
此时从 客户端到服务器端 这个方向的链接就被释放,TCP链接处于“半关闭状态” 。 此时客户端不会再向服务器端发送数据,可是服务器端可能还会想客户端发送数据。
v)客户端收到从服务器端发出的TCP报文以后,确认了服务器端收到了客户端发出的释放链接请求,随后客户端结束“FIN-WAIT-1”终止等待状态1,进入“FIN-WAIT-2”终止等待状态2。
总结:前两次挥手,既让服务器端知道了客户端想要释放链接,也让客户端知道了服务器端知道了本身想要释放链接。
3)“第三次挥手”:客户端通过 “ClOSE-WAIT” 关闭等待 状态后,它要发送给服务器端的数据也发送完毕,即它作好了释放服务器端到客户端链接的准备,就会想客户端发送一段FIN的TCP报文:
i)标记位 FIN=1,ACK=1:表示“服务器端告知客户端,本身已经作好释放链接的准备”;
ii)报文序号 seq=w:关闭等待阶段,服务器端可能又向客户端发送了数据,所以此时 seq 不是v,而是最新的数据 w;
iii)确认号 ack=u+1:仍是但愿客户端下一次发送的报文序号为 u+1;
iv)发送“第三次挥手”报文后,服务器端进入“LAST-ACK” 最后确认阶段。此后,服务器端再也没法向客户端发送数据。
注意,第三次挥手发送的是FIN的报文,没有数据可是会占一个序号,即下一次服务器端发送的报文序号 seq=w+1。
4)“第四次挥手”:客户端接收到服务器端“第三次挥手”的报文后,确认服务器端已经作好断开链接的准备,会向客户端发送“第四次握手”的报文:
i)标记位ACK=1:表示“客户端已经知道服务器端作好释放链接的准备”;
ii)ack = w+1:将收到服务器端报文的 seq+1,做为本身的ack。表示但愿服务器端下次发送的报文的序号为 w+1;
iii)seq=u+1:将收到服务器端报文的 ack 做为本身的 seq,由于服务器端报文 ack=u+1 表示但愿客户端此次发送的报文序号是 u+1,那么客户端此次发送报文的序号就设置为 u+1;
iv)客户端发送完第四次挥手的报文后,启动等待计时器,等待2MSL后,若是没有收到服务器端新的请求,就进入“CLOSED” 链接关闭状态;
v)服务器端在收到客户端发送的第四次挥手的报文后,进入进入“CLOSED” 链接关闭状态。
总结:后“两次挥手”既让客户端知道了服务器端准备好释放链接了,也让服务器端知道了客户端了解了本身准备好释放链接了。因而,能够确认关闭服务器端端到客户端方向上的链接了,由此完成“四次挥手”,关闭了链接。
二、三个关键问题
1)为何“握手”是三次,而“挥手”倒是四次?
对于“三次握手”,在第二次握手的时候,服务器端向客户端发送的报文的标记位包含 SYN=1以及ACK=1,SYN是请求链接标志,表示服务器端赞成创建链接;ACK是确认报文,表示告诉客户端,服务器端收到了它的请求报文。即“确认接收”与“赞成链接”是在同一次握手中传输的。那么经过三次握手就刚恰好能够创建链接;
对于“四次挥手”,第二次挥手的时候,服务器端可能尚未作好关闭链接的准备(它可能还有数据要发送给客户端),所以,它不会当即释放链接,会在第二次挥手先返回一个ACK=1,表示已经接受到客户端的断开链接的请求。随后,客户端处理完数据后,会发送第三次挥手报文,其中FIN=1,表示服务器端已经准备好释放链接。所以,释放链接须要通过“四次挥手”。
2)为何客户端要在发送第四次挥手的报文后等待2MSL的时间才进入CLOSED状态?
目的是为了确认服务器端会收到客户端发送的“第四次挥手”的ACK确认报文。
MSL(Max Segment Lifetime): 最长报文段寿命,也就是一个报文在网络中存活的最长时间,通常设置为2分钟。当客户端发出最后的ACK确认报文时,并不能肯定服务器端可以收到该段报文。因此客户端在发送完ACK确认报文以后,会设置一个时长为2MSL的计时器。
在1MSL的时候,若是服务器端没有收到客户端发送的ACK确认报文,就会再次向客户端发送FIN报文,这个报文在1MSL的时间内会到达客户端,此时客户端边知道本身上一次发送的ACK确认报文没有发送到服务器端,因而客户端会再次向服务器端发送ACK确认报文,并将等待计时器重置。
若是在2MSL的时间内没有接受到服务器端发送回来的FIN报文,说明客户端最后发送的ACK确认报文已经被服务器端接收到,能够关闭链接。所以,事实上客户端会比服务器端更晚进入CLOSED状态。
3)若是已经创建了链接,可是客户端出现故障了怎么办?
若是客户端发送错误,服务器端还一直保持链接,这个链接并不会传送数据,而且占用了服务器端的资源。服务器端有一个“保活计时器”,服务器端每收到一次客户端的请求后都会从新复位这个计时器,时间一般是设置为2小时,若2小时尚未收到客户端的任何数据,客户端就会发送一个探测报文段,之后每隔75秒钟发送一次。若一连发送10个探测报文客户端仍然没反应,服务器端就认为客户端出了故障,接着就关闭链接。