Java面试程序员

谈谈TCP的连接状态

2018-02-01  本文已影响52人  1d96ba4c1912

最近折腾科学上网的时候看了一些计算机网络方面的知识,没有做太深入的研究,但已经把之前一些没想清楚的地方弄明白了,还是有一些收获。写篇文章总结一下,整理之后发现需要分成两篇来写,这篇文章先简单总结一下TCP三次握手四次挥手方面的知识。

一、TCP头部概述

网上或者书本上很多关于TCP握手挥手的图一般都会出现ACK,ack,SYN,seq四个小标识,因此在谈握手跟挥手时先来整理一下这四个小标识的含义。

首先上一张TCP头部的格式图:


TCP协议头信息

可以看出头部内容很多,不过这里我们只需要关心这四个小标识就可以了,根据上图依次解释一下:

  1. ACK:对应上图的第12个字节中大写的ACK,它只有一个bit位,用于表示消息的类型。该bit位设置为1表示该消息是一条确认消息。

  2. SYN:与ACK相同,它表示消息的类型,1表示是一条请求连接的消息。

  3. seq:该字段对应上图的Sequence number字段,它表示发送方发送消息的起始序列。

  4. ack:该字段对应上图的Acknowledgment number字段,它只有在该条消息的类型是确认消息时才需要设置,表示期望收到后续消息的起始序号。

综上:ACK,SYN大写字母表示的是消息类型,seq,ack表示的是消息序号。

注:三次握手建立连接中的一个重要内容就是双方交换seq起始序列号,用于后续确认消息是否到达。

我们知道TCP是能够保证消息到达的,这个保证就是依赖seq跟ack实现的,比如发送方连续发送了4个50字节的包,起始序列是1,如果接收方都收到了,返回的ACK(表示消息类型)消息中ack(表示确认序号)的值应该是201,表示前面的200字节消息已经全部收到,接下来期望收到的消息是从第201个字节起,这样就表示没有丢包。但如果发送方最后收到的ACK消息中ack序号101,那表示第三个包丢掉了,需要进行重发。

二、TCP三次握手状态转换详解

注:由于seq跟ack两个值只是用来确认发送消息的起始序号以及期望收到后续消息的起始序号,对TCP连接本身的状态转换并没有任何实际意义,因此下图中只标识了ACK跟SYN消息类型而忽略了ack跟seq。

先上图,再解释:


TCP三次握手状态图

首先server端需要启动tcp服务来让client端进行连接,以Java代码为例:

ServerSocket ss = new ServerSocket(8888);
ss.accept();

这样server端就进入了LISTEN状态,表示监听,等待客户端的连接,这是第一步,很好理解。

第二步,client端进行连接的时候,需要先向server端发一条SYN消息,该消息发出以后,client端就进入了SYN-SENT状态,表示SYN消息已经发送,这个也很好理解。

第三步,当server端收到client端发来的SYN消息以后,知道有client希望建立连接,此时server端会回复一条SYN + ACK消息,其中ACK用来表示client的SYN消息已经收到了,SYN是通知client自己的初始消息序号。此时server端状态变为SYN-RECEIVED,表示SYN收到,等待最后的ACK,不难理解。

第四步,client端收到server端发来的SYN + ACK的消息以后,知道了自己发的SYN消息server端已经知道了,同时自己也知道了server端的SYN消息,然后会回一条ACK消息,表示server端的SYN消息已经收到,此时client的状态变为ESTABLISHED,表示连接已经建立。

第五步,server端收到client端的ACK消息以后,状态会变为ESTABLISHED,此时连接就建立成功了。

总体来说三次握手中的状态变换是很直观很容易理解的。

三、TCP四次挥手状态转换详解

四次挥手相对于三次握手状态转换就要复杂一些了,下面一起看下:

注:server端跟client端都是可以自由发起关闭连接的请求的,这个是重点。

TCP四次挥手状态转换图

首先我们看下最常规的四次挥手过程。

第一步,client端发送FIN消息给server端,此时状态变为FIN_WAIT_1。

第二步,server端收到client端发来的FIN消息以后,会发送一条ACK消息给client端,表示断开连接的请求已经收到。之后server端的状态变为CLOSE_WAIT。该状态表示它在等待server端发送完剩余数据已经进行关闭来连接的操作。

第三步,client端收到ACK消息以后,知道自己关闭连接的请求已经生效,状态会变为FIN_WAIT_2。表示接下来是等待server端关闭连接的请求。

第四步,接下来server端也想关闭连接,则发一条FIN消息给client端,此时状态切换为LAST_ACK,表示等待client发回的确认消息。

第五步,client端收到server端发来的FIN消息以后会发送一条ACK消息回去,此时状态变为TIME_WAIT,因为它需要确认server端收到自己发的这条ACK消息。

第六步,server端收到client发来的ACK消息后,知道关闭连接的请求已经生效,此时状态变为CLOSED。

第七步,在等待时间结束以后,client端知道自己发送的ACK已经被接收,状态变为CLOSED,连接关闭。

再次强调,断开连接的操作可以由client端发起也可以由server端发起,因此上面的状态图完全可以反过来。

这里解释一下TIME_WAIT这个状态的含义,首先server端发送了FIN消息,如果client端回复的ACK消息没有到达,根据TCP协议的规定,此时server端会重新发送FIN消息进行关闭连接的请求。如果没有TIME_WAIT状态,当client发送ACK以后直接进入CLOSED状态,这时如果这条ACK消息丢失了,然后client端又因为其他请求重新跟server建立起了连接(注:这是一条新的连接),此时server端由于没有之前的FIN消息没有收到client端的ACK回复,那么它重新发送了FIN消息,而这条FIN消息就作用在了新建立的连接上,表示server端请求关闭,这时逻辑就变的混乱了,因此TIME_WAIT这个状态时很有必要存在的。

弄明白上面总结的一种最常规的关闭连接的过程以后,我们再来看看另外两种特殊情况。

由于TCP连接的client端跟server端都可以在任意时刻断开连接,那么我们下面就来看下server端跟client端同时发起FIN的情况。

同时关闭连接状态图
注:上图为了避免重叠把两条FIN消息分开画,并不表示是server先收到client端的FIN消息以后再发送自己的FIN消息,它表示双方都没有收到对方FIN消息的情况下都向对方发送了FIN消息。

可以看出上图新出现了一个CLOSING状态,它表示发送方发送FIN消息之后,在收到对方回复的ACK消息之前,先收到了对方发来的FIN消息,这就表示这时候双方都在请求关闭连接,因此该状态为CLOSING,表示连接正在关闭。

这里为什么没有写出server端的状态呢?因为它们之间的状态是一直的,还是那句话,关闭连接是谁都可以发起的,因此在关闭连接的过程中没有哪个状态是client端或者server端特有的。

关闭连接时还存在下面一种情况,就是同时收到对方发来的FIN跟ACK消息:


同时收到FIN跟ACK状态图

这里的状态都是上面出现过的,就不多做描述了。

四、TCP后续

除了本文总结的三次握手四次挥手以外,TCP协议还有很多内容很有意思,比如滑动窗口,拥塞控制以及Nagle算法,而且这些知识在实际工作或者面试中也都会或多或少的用到,感兴趣的同学可以自己去查阅一下,都不是很难理解。

上一篇下一篇

猜你喜欢

热点阅读