三次握手和四次挥手不少小伙伴老是记不住,包括我在内,以为ACK,SYN,ack 是啥吗,怎么记得住他们的状态,值为多少?这篇文章将带你清晰认识三次挥手和四次握手的过程。html
三次握手(Three-way Handshake)其实就是指创建一个TCP链接时,须要客户端和服务器总共发送3个包。进行三次握手的主要做用就是为了确认双方的接收能力和发送能力是否正常、指定本身的初始化序列号为后面的可靠性传送作准备。实质上其实就是链接服务器指定端口,创建TCP链接,并同步链接双方的序列号和确认号,交换TCP窗口大小信息。git
官话套话说完了,咱们来说一下它的过程。github
其实之因此要进行三次握手的缘由就是为了:客户端与服务端都必须确保本身和对方都能接发正常。面试
第一次握手(客户端-->服务端): 客户端什么都不用确认,服务端确认:对方发送正常。bash
第二次握手(服务端-->客户端): 客户端确认:本身发送正常、接收正常(经过本身发送接收可确认),对方发送正常、接收正常(客户端确认完毕);服务端确认:本身接收正常,对方发送正常。服务器
第三次握手(客户端-->服务端): 服务端确认:本身接发正常,对方接发正常。cookie
是否是这样理解就简单多了,而报文中的参数设置就是为了“确认”的保障。网络
如今咱们再来看看参数设置:并发
那一堆的大写参数,UCG、ACK、SYN等,当置为1时,即有效,为0时无效。spa
认真了,硬核来了!!!
第一次握手(SYN=1, seq=x):
客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算链接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。
发送完毕后,客户端进入 SYN_SEND 状态。
第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择本身 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。 发送完毕后,服务器端进入 SYN_RCVD 状态。
第三次握手(ACK=1,ACKnum=y+1):
客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,而且把服务器发来 ACK 的序号字段+1,放在肯定字段中发送给对方,而且在数据段放写ISN的+1
发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手结束。
上面讲了三次握手过程,咱们来设想一下,两次握手会发生什么?
只采起两次握手,则表现为当客户端发出请求后,收到确认就创建链接。
如客户端发出链接请求,但因链接请求报文丢失而未收到确认,因而客户端再重传一次链接请求。后来收到了确认,创建了链接。数据传输完毕后,就释放了链接,客户端共发出了两个链接请求报文段,其中第一个丢失,第二个到达了服务端,可是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到链接释放之后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的链接请求,因而就向客户端发出确认报文段,赞成创建链接,不采用三次握手,只要服务端发出确认,就创建新的链接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源。
服务器第一次收到客户端的 SYN 以后,就会处于 SYN_RCVD 状态,此时双方尚未彻底创建其链接,服务器会把此种状态下请求链接放在一个队列里,咱们把这种队列称之为半链接队列。
固然还有一个全链接队列,就是已经完成三次握手,创建起链接的就会放在全链接队列中。若是队列满了就有可能会出现丢包现象。
服务器发送完SYN-ACK包,若是未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。若是重传次数超过系统规定的最大重传次数,系统将该链接信息从半链接队列中删除。
注意,每次重传等待的时间不必定相同,通常会是指数增加,例如间隔时间为 1s,2s,4s,8s…
其实第三次握手的时候,是能够携带数据的。可是,第一次、第二次握手不能够携带数据
为何这样呢?你们能够想一个问题,假如第一次握手能够携带数据的话,若是有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。由于攻击者根本就不理服务器的接收、发送能力是否正常,而后疯狂着重复发 SYN 报文的话,这会让服务器花费不少时间、内存空间来接收这些报文。
也就是说,第一次握手不能够放数据,其中一个简单的缘由就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来讲,他已经创建起链接了,而且也已经知道服务器的接收、发送能力是正常的了,因此能携带数据也没啥毛病。
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,因此服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短期内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,因为源地址不存在,所以Server须要不断重发直至超时,这些伪造的SYN包将长时间占用未链接队列,致使正常的SYN请求由于队列满而被丢弃,从而引发网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。
检测 SYN 攻击很是的方便,当你在服务器上看到大量的半链接状态时,特别是源IP地址是随机的,基本上能够判定这是一次SYN攻击。在 Linux/Unix 上可使用系统自带的 netstats 命令来检测 SYN 攻击。
netstat -n -p TCP | grep SYN_RECV
常见的防护 SYN 攻击的方法有以下几种:
为何要叫“挥手”呢?
我猜,这个过程就跟人与人交流同样,须要挥手 say goodbye,hhh!
四次挥手过程比三次挥手要简单一点,由于很好理解,就像我和女友打电话同样。
① 我说:我说完了,你还有什么要跟我说的吗?
② 她说:我知道了。
③ 她接着叮嘱我:你记得想我哦。。。。。。(又说了一堆,)我说完了。
④ 我说:知道了,挂了哦!
复制代码
而后就结束了通话了。四次挥手就结束了,是否是就像通话过程。
官方一点的讲解来了:
终止一个链接要通过四次挥手(也有将四次挥手叫作四次握手的)。这由TCP的半关闭(half-close)形成的。所谓的半关闭,其实就是TCP提供了链接的一端在结束它的发送后还能接收来自另外一端数据的能力。
TCP 的链接的拆除须要发送四个包,所以称为四次挥手(Four-way handshake),客户端或服务器都可主动发起挥手动做。
刚开始双方都处于 ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥手的过程以下:
即发出链接释放报文段(FIN=1,序号seq=u),并中止再发送数据,主动关闭TCP链接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。
即服务端收到链接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的链接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的链接释放报文段。
即服务端没有要向客户端发出的数据,服务端发出链接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。
即客户端收到服务端的链接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,须要通过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。
收到一个FIN只意味着在这一方向上没有数据流动。客户端执行主动关闭并进入TIME_WAIT是正常的,服务端一般执行被动关闭,不会进入TIME_WAIT状态。
其实看了上面讲解,大致也知道为何了。
当服务端收到客户端的SYN链接请求报文后,能够直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。可是关闭链接时,当服务端收到FIN报文时,极可能并不会当即关闭SOCKET,因此只能先回复一个ACK报文,告诉客户端,“ 你发的FIN报文我收到了 ”。只有等到我服务端全部的报文都发送完了,我才能发送FIN报文,所以不能一块儿发送。故须要四次挥手。
TIME_WAIT状态也成为2MSL等待状态。每一个具体TCP实现必须选择一个报文段最大生存时间 MSL(Maximum Segment Lifetime),它是任何报文段被丢弃前在网络内的最长时间。这个时间是有限的,由于TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生存时间的TTL字段。
对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该链接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可以让TCP再次发送最后的ACK以防这个ACK丢失(另外一端超时并重发最后的FIN)。
这种2MSL等待的另外一个结果是这个TCP链接在2MSL等待期间,定义这个链接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个链接只能在2MSL结束后才能再被使用。
简单的来讲,就是,IP报文段限制了其生存时间,为了尽量的保证收到报文,咱们采起重传,因此须要2MSL。
MSL是Maximum Segment Lifetime的英文缩写,可译为“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
为了保证客户端发送的最后一个ACK报文段可以到达服务器。由于这个ACK有可能丢失,从而致使处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,从新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK以后直接释放关闭,一但这个ACK丢失的话,服务器就没法正常的进入关闭链接状态。
两个理由:
保证客户端发送的最后一个ACK报文段可以到达服务端。 这个ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认,服务端超时重传FIN+ACK报文段,而客户端能在2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,从新启动2MSL计时器,最后客户端和服务端都进入到CLOSED状态,若客户端在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段后当即释放链接,则没法收到服务端重传的FIN+ACK报文段,因此不会再发送一次确认报文段,则服务端没法正常进入到CLOSED状态。
防止“已失效的链接请求报文段”出如今本链接中。 客户端在发送完最后一个ACK报文段后,再通过2MSL,就可使本链接持续的时间内所产生的全部报文段都从网络中消失,使下一个新的链接中不会出现这种旧的链接请求报文段。
理论上,四个报文都发送完毕,就能够直接进入CLOSE状态了,可是可能网络是不可靠的,有可能最后一个ACK丢失。因此TIME_WAIT状态就是用来重发可能丢失的ACK报文。
如今是否是对于TCP链接的三次握手和四次挥手比较清晰了,hhh。 咱们也该挥手了,FIN = 1。