前言
在前一章说过TCP的“三次握手”是建立连接的过程,那么“四次挥手”就是断开连接的过程。
西瓜籽:“如果把TCP连接当作是数据传输的一个管道的话,那么我们直接断开不就好了,为什么非要通过四次挥手断开呢?”
大西瓜:“把TCP当作是一个传输数据的管道是没啥问题,不过它不同于我们日常生活中的那个管道,TCP连接的管道是双工的,也就是说数据可以在两个方向上独立传递。所以我们不能靠仅仅断开一端来关闭TCP连接,而是两端都要单独断开。”
西瓜籽:“我懂了!!!”
大西瓜:"厉害厉害,不过我们还是再过一遍吧!”
TCP四次挥手
- 客户端应用进程调用断开TCP连接的请求,向服务器端发送一个终止标志位FIN=1,seq=u的报文,客户端进入FIN_WAIT_1状态。 表示客户端已经将数据传输完毕,将不再传输数据(报文中将不包含数据),将进行主动关闭链路的操作。
- 服务器端在收到这个FIN后,向客户端发送一个ACK=1,seq=v,ack=u 1的报文,服务器端进入CLOSE_WAIT状态,客户端收到该ACK报文后将进入FIN_WAIT_2状态。此时TCP处于半关闭状态,该状态发生于客户端,所以客户端将无法发送数据,但是可以接受数据。
- 在服务器将需要发送的数据全部发送给客户端后,将会发送一个终止标志位FIN=1,ACK=1,seq=w,ack=u 1的报文给客户端,服务器进入LAST_ACK(最后确认)状态,等待客户端最终确认关闭链路操作。
- 客户端收到该FIN报文后,发送一个ACK=1,seq=u 1,ack=w 1的报文给服务器端,表明接收到服务器端的断开连接的请求并准备断开服务器端到客户端的链路,此时客户端进入TIME_WAIT状态,服务器端在收到ACK后进入CLOSE状态。不过此时TCP连接还没有释放,客户端需要在TIME_WAIT状态停留等待2MSL(2倍报文最大生存周期),因为客户端需要确保服务器端收到自己的ACK报文,然后客户端才会进入CLOSE状态。
我们用个分手的例子,形象点来解释下什么是四次挥手: 女:我已经不喜欢你了,我们分手吧,你还有没有什么要对我说的。 男:我知道了,其实我早就知道了,让我最后再给你说些话吧,(...此处省略xx字)。 男:嗯,我说完了,那我们结束吧。 女:好的,我们到这就结束吧,我给你发了一句话,你收到后也不必回复我,因为我可能已经把你拉黑了!
有关FIN的两个点:
- 半关闭状态:FIN的发送方将不再发送数据,但是还可以接受数据。
- 收到一个FIN只是意味着在这一方向上将没有数据传输。客户端执行主动关闭并进入TIME_WAIT是正常的,服务端通常执行被动关闭,不会进入TIME_WAIT状态。在socket编程中,任何一方执行close()操作即可产生挥手操作,注意这里调用close()并不是发送一个FIN,只是将socket描述符的引用计数减1,只有在计数为0时才会关闭套接字。调用shutdown()才会发送FIN。
常见问题汇总
1.SYN洪水攻击
“SYN Flood是一种常见的DDoS攻击方式。SYN Flood就是利用TCP连接的漏洞来恶意消费服务器资源。我们都知道在TCP建立连接的“三次握手”的过程中,会有SYN/ACK报文的发送和接收,在第二次握手时,当Server向Client发送了ACK SYN报文后,如果无法在限定时间内接收到来自Client的ACK确认报文,即该连接的定时器超过重传时间(Server的ACK SYN报文丢,Client的ACK报文丢失,虚假Client),服务器就会对报文进行重传,只有当重传次数超过最大限定次数,服务器才会认为该TCP连接超时,销毁并释放该TCP。但是如果攻击者用快于服务器TCP连接超时的速度,在短时间内连续向目标服务器发送伪造的SYN报文,就会导致 Server 端大量的链接处在 SYN_RCVD状态,占用大量的Server资源,进而影响其他正常请求的建连。 ”
解决方案:
- 缩短SYN Timeout(连接等待超时)时间这是最简单直接的方式,既然SYN Flood攻击的基本原理就是耗尽主机的半开放连接资源,那么我们就通过缩短从接收SYN报文到确认该报文无效并关闭该TCP连接的时间,使得因攻击被占用的资源快点得到释放,太短也不行,太短的话会影响到正常访问。不过由于一般攻击量很大,所以这种做法的效果并不明显。
- SYN CookieSYN Cookie是对TCP服务器端的三次握手做一些修改,专门用来防范SYN Flood攻击的一种手段。它的原理是,在TCP服务器接收到TCP SYN包并返回TCP SYN ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。这个cookie作为将要返回的SYN ACK包的初始序列号。当客户端返回一个ACK包时,根据包头信息计cookie,与返回的确认序列号(初始序列号 1)进行对比,如果相同,则是一个正常连接,然后,分配资源,建立连接。否则判断为SYN攻击包,放弃。
2.为什么非要三次握手,而不是一次、两次,三次、四次或者更多?
要想回答这个问题,我们就要弄清三次握手的目的,这个我在上篇已经讲到了,这里就简述一下它们的作用:第一次握手:确定了客户端的发送能力 第二次握手:确定了服务器端的接收能力,和发送能力 第三次握手:确定了客户端的接收能力 所以可以总结三次握手的作用是:
- 是为了确保连接的两端都做好了发送和接收数据的准备工作
- 确保每方都具有双方传输数据的序列号
“为了实现可靠数据传输,TCP 协议的通信双方,都必须维护一个序列号,以标识发送出去的数据包中,哪些是已经被对方收到的。三次握手的过程即是通信双方相互告知序列号起始值,并确认对方已经收到了序列号起始值的必经步骤。 如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认。 ”
有了上面这些概念后,我们来对比下,如果我们采用别的握手次数来建立连接:
一次握手:
对于建立双工的TCP连接而言,一次握手显然是不够用的,比如Client向Server发送一个连接消息,但是由于只有一次连接,那么它就不能收到来自Server的回应,也就无法确定Server是否收到了连接的请求,自然就无法确定是否连接成功。
两次握手:
既然一次握手由于接收不到Server的回应,导致无法确认连接,那么两次握手呢?先说答案吧,不行!因为二次握手很容易导致死锁的发生,造成资源浪费。我们都知道网络中的报文是有丢失的风险的,如果一个Client想建立连接,给Server发送了一个SYN报文,但是它丢失了,没有及时到达服务器,而客户端由于在限定时间内并未收到来自Server的ACK报文,那么就会重新向服务器发送一个SYN报文,这次的连接过程很顺利,Server收到了来自Client的SYN报文,Client也收到了来自Server的ACK报文。但是那个走丢的SYN报文,在某个时刻终于到了服务器,那么服务器也将会为这个SYN报文申请相应的资源,然后返回一个ACK报文。但是由于这个SYN对于Client而言已经失效了,所以客户端将不再理会这个ACK报文,但是服务器却会以为Client是没有收到而再次发送,导致死锁,造成资源浪费。
三次握手:
“在谢希仁著《计算机网络》第四版中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。 在另一部经典的《计算机网络》(Andrew S.Tanenbaum著,第四版)一书中讲“三次握手”的目的是为了解决“网络中存在延迟的重复分组”的问题。 ”
这两句话其实表明的是同一个道理。两次握手无法确认SYN是否有效,那么三次握手是如何解决这个问题的呢?其实第三次握手就是客户端向服务器端回复第二次握手,这时服务器就会等待第三次握手的到来,如果第三次握手一直都不到来,那么服务器端就可以将这个SYN判为无效,从而释放相应的资源。还有一种情况就是,由于出现了某种问题,导致服务器端一直无法收到第三次握手,那么服务器端就会向客户端发送RST报文,客户端收到RST报文就知道第三次握手没有成功,重新发起连接。
多次握手:
综上可以推导出,奇数次握手与三次握手相同,偶数次握手与两次握手相同。所以没必要多次握手,三次就好。