共计 3331 个字符,预计需要花费 9 分钟才能阅读完成。
TCP的三次握手与四次挥手
TCP的三次握手
- 第一次握手:
客户端将TCP报文标志位SYN置为1,随机产生一个序号值seq=X,保存在TCP首部的序列号(Sequence Number)字段里,并将该数据包发送给服务器端,发送完毕后,客户端进入SYN_SENT
状态,等待服务器端确认。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。这个三次握手中的开始。表示客户端想要和服务端建立连接。 - 第二次握手:
服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将TCP报文标志位SYN和ACK都置为1,ack=X+1,随机产生一个序号值seq=Y,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD
状态。 - 第三次握手:
客户端收到确认后,检查ack是否为X+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=Y+1,并将该数据包发送给服务器端,服务器端检查ack是否为Y+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED
状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。 - 为什么要三次握手,两次不行么
第一次握手a发给b,此时b知道ab是通的,但a还不知道。
第二次握手是b发给a,此时a知道ab和ba都是通的,但b只知道ab通,不知道ba通不通。
第三次握手是a发给b,此时b也知道ba通的,完毕。
举例:
客户端发送了第一个连接的请求报文,但是由于网络不好或者发送完关闭客户端,这个请求没有立即到达服务端,而是在某个网络节点中滞留了,直到某个时间才到达服务端,本来这已经是一个失效的报文,但是服务端接收到这个请求报文后,还是会向客户发出确认的报文,表示同意连接。
假如不采用三次握手,那么只要服务发出确认,新的建立就连接了,但其实这个请求是失效的请求,客户端是不会理睬服务的确认信息,也不会向服务端发送确认的请求,因为客户端关闭了,或者认为没连上服务端。但是服务端认为新的连接已经建立起来了,并一直等待客户端发来数据,这样服务端的很多资源就没白白浪费掉了,采用三次握手就是为了防止这种情况的发生,服务端会因为收不到确认的报文,就知道客户端并没有建立连接。这就是三次握手的作用。
TCP的四次挥手
四次挥手即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。由于TCP连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
挥手请求可以是Client端,也可以是Server端发起的,我们假设是Client端发起:
- 第一次挥手:
Client端发起挥手请求,向Server端发送标志位是FIN=1,seq=u的报文段,此时,Client端进入FIN_WAIT_1
状态,这表示Client端没有数据要发送给Server端了。TCP规定,FIN报文段即使不携带数据,也要消耗一个序号
- 第二次挥手:
Server端收到了Client端发送的FIN报文段,向Client端返回一个标志位是ACK=1,ack=u+1的报文段,Client端进入FIN_WAIT_2
状态,Server端告诉Client端,我确认并同意你的关闭请求。
TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT
状态持续的时间。客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2
(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)
- 第三次挥手:
Server端向Client端发送标志位是FIN=1,ack=u+1,的报文段,请求关闭连接,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK
(最后确认)状态,等待客户端的确认。
- 第四次挥手 :
Client端收到Server端的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT
(时间等待)状态。注意此时TCP连接还没有释放,必须经过**2MSL**
(最长报文段寿命)的时间后则证明Server端已正常关闭,那好,Client端也可以关闭连接了。
- 为什么挥手需要四次
为了确保数据能够完成传输。关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。可能有人会有疑问,tcp握手的时候为何ACK(确认)和SYN(建立连接)是一起发送。挥手的时候为什么是分开的时候发送呢。因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭 SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步挥手。
- 客户端突然挂掉了怎么办
正常连接时,客户端突然挂掉了,如果没有措施处理这种情况,那么就会出现客户端和服务器端出现长时期的空闲。解决办法是在服务器端设置保活计时器,每当服务器收到客户端的消息,就将计时器复位。超时时间通常设置为2小时。若服务器超过2小时没收到客户的信息,他就发送探测报文段。若发送了10个探测报文段,每一个相隔75秒,还没有响应就认为客户端出了故障,因而终止该连接。
- 为什么要等待2MSL
MSL:报文段最大生存时间,它是任何报文段被丢弃前在网络内的最长时间。
有以下两个原因:
- 保证TCP协议的全双工连接能够可靠关闭
由于IP协议的不可靠性或者是其它网络原因,导致了Server端没有收到Client端的ACK报文,那么Server端就会在超时之后重新发送FIN,如果此时Client端的连接已经关闭处于CLOESD
状态,那么重发的FIN就找不到对应的连接了,从而导致连接错乱,所以,Client端发送完最后的ACK不能直接进入CLOSED
状态,而要保持TIME_WAIT
,当再次收到FIN的收,能够保证对方收到ACK,最后正确关闭连接。 - 第二点:保证这次连接的重复数据段从网络中消失
如果Client端发送最后的ACK直接进入CLOSED
状态,然后又再向Server端发起一个新连接,这时不能保证新连接的与刚关闭的连接的端口号是不同的,也就是新连接和老连接的端口号可能一样了,那么就可能出现问题:如果前一次的连接某些数据滞留在网络中,这些延迟数据在建立新连接后到达Client端,由于新老连接的端口号和IP都一样,TCP协议就认为延迟数据是属于新连接的,新连接就会接收到脏数据,这样就会导致数据包混乱。所以TCP连接需要在TIME_WAIT状态等待2倍MSL,才能保证本次连接的所有数据在网络中消失。