TCP状态查看以及故障的排查

Posted xiaozhihong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP状态查看以及故障的排查相关的知识,希望对你有一定的参考价值。

TCP三次握手过程中的服务端和客户端的各种状态:

TCP四次握手释放过程中的主动关闭端和被动关闭端的各种状态:

下图的两端可以是服务端也可以是客户端。

四次握手释放过程中,主动关闭这一端会处于TIME_WAIT,被动关闭这一端会马上处于CLOSE状态,处于TIME_WAIT的一端要等几十秒后会才会到CLOSE状态释放端口。

注意:

listen函数只是让套接字加上监听能力,不是使套接字开始进入监听状态。

让套接字由closed状态进入listen监听状态的是accept函数。

ESTABLISHED 数据可传输状态

能够在服务端或客户端捕获看到的状态

1、三次握手:

服务端的LISTEN

客户端的ESTABLISHED

服务端的ESTABLISHED

(1)只开启服务端multi_thread_server4,可以看到处于LISTEN监听状态

切换到root后,netstat -anp|grep 端口号

技术分享图片

(1)服务端和客户端连接成功

服务端处于ESTABLISHED 数据可传输状态

技术分享图片

客户端处于ESTABLISHED 数据可传输状态

技术分享图片

2、四次握手释放:

主动关闭这一端的FIN_WAIT2

被动关闭这一端的CLOSE_WAIT

主动关闭这一端的TIME_WAIT

(1)连接成功后关闭客户端,服务端没关闭

主动关闭的一端处于FIN_WAIT_2状态,被动关闭的一端(没关闭的一端)处于CLOSE_WAIT的状态。关闭服务端后这个CLOSE_WAIT状态要几十秒才会消失。

技术分享图片

客户端:

技术分享图片

服务端:

技术分享图片

(2)连接成功后关闭服务端,客户端没关闭

服务端:

技术分享图片

客户端:

技术分享图片

(3)四次握手释放过程中,先关闭客户端再关闭服务端

四次握手释放过程中,主动关闭这一端发送完ACK包后会处于TIME_WAIT,被动关闭这一端会马上处于CLOSE状态(这个状态捕获不到),处于TIME_WAIT的一端要等几十秒后会才会到CLOSE状态释放端口。

客户端:

技术分享图片

(4)先关闭服务端再关闭客户端

经常会遇到先关闭服务端再关闭客户端后,再启动服务端会出现下图情况,要等几十秒才能启动,这是因为服务端处于TIME_WAIT,端口还没释放(例程里的服务端端口是8888)。

技术分享图片

服务端:

技术分享图片

为什么要这个TIME_WAIT呢?

为了保证四次握手释放过程中安全地关闭端口。

为了保证四次握手释放过程中应答ACK包成功发给服务端。服务端主动关闭后,会发生FIN数据包给客户端,客户端会返回一个应答ACK包。如果这个应答包服务没有接受到则会认为FIN数据包发送失败,会再次发送发生FIN数据包。所以这里需要一个一个时间 2MSL来处于TIME_WAIT状态。

处于TIME_WAIT的时间为 2MSL,MSL表示最大片生存时间,这个时间大概是30s,2MSL就是60s。这个MSL就等于TTL(一个TCP数据包在路由间跳转的生存时间),设置为 2MSL是因为在需要重发FIN数据包的时候(ACK应答包丢失的情况),发送ACK数据包和重发FIN数据包的TTL时间是 2MSL,客户端为了保证重发的FIN数据包能够到达。如果没有这个TIME_WAIT,当服务端的有其他进程马上使用这个端口(例程里的服务端端口是8888)的时候,则重发的FIN数据包会发生给使用了该端口的进程,这时这个进程就不明白这个FIN包是为了什么,可能会导致不可预知的bug。也有可能某些在未过TTL时间的FIN数据包还在路由间跳转,只要服务端的TIME_WAIT持续时间大于MSL就能够保证不被干扰。

如果是客户端处于TIME_WAIT(客户端是主动关闭这一端)则客户端可以使用其他端口,不会出现端口被占用的警告。

客户端:

连接成功后,关闭客户端后,再开一个客户端连接,可能看到被关闭的客户端还在处于处于TIME_WAIT,而且和再开的客户端端口不同。

技术分享图片

上面说的FIN和ACK数据包是指下图红框的数据包

以上是顺利正常握手和释放的情况,下面是握手和释放过程中不顺利的情况。

三次握手不顺利的情况

服务端程序里有listen_fd和client_fd两种套接字,listen_fd用来监听来自客户端的连接请求,每次连接完一个客户端就回到accept这里继续阻塞等待监听,client_fd用来接收来自客户端的数据或者发送数据给客户端。

3、RST包

一开始服务端和客户端都处于CLOSED状态,服务端启动后listen_fd套接字处于监听状态LISTEN,监听来自客户端的SYN包后发送SYN+ACK包给客户端。

顺利的话客户端接收到后返回ACK包给服务端端后创建client_fd套接字和子进程或者子线程,子进程或者子线程里client_fd处于数据可传输状态ESTABLISHED,然后父进程或者主线程的listen_fd回到LISTEN状态。

但当不顺利时,在客户端发送SYN后客户端关闭,客户端处于CLOSED状态,服务端发送SYN+ACK包给处于CLOSED状态客户端时,客户端会返回一个RST包给服务端,listen_fd由SYN_RCVD状态变为LISTEN状态。

技术分享图片

4、

本来客户端A是调用connect函数发送SYN包去主动连接服务器的,是处于SYN_SEND状态的,但是这时有另外一个客户端B发送SYN包给客户端A,想要去连接客户端A,则客户端A会转变为SYN_RCVD状态。

5、

客户端发送SYN后,网络不通畅,客户端接收不到SYN+ACK包,超时后回到CLOSED状态

四次握手释放不顺利的情况

假设服务端和客户端的client_fd套接字处于ESTABLISHED状态。

技术分享图片

6、顺利四次握手释放的过程

主动关闭一端

主动关闭这一端调用close函数,发送FIN包给被动关闭这一端后处于FIN_WAIT_1,被动关闭这一端返回ACK包后,主动关闭这一端处于FIN_WAIT_2。当被动关闭这一端发FIN并且主动关闭这一端返回ACK给被动关闭这一端后,主动关闭这一端处于TIME_WAIT并持续2MSL后回到CLOSE状态。

被动关闭一端

被动关闭这一端接收到FIN包返回ACK包给主动关闭这一端后处于CLOSE_WAIT,等待被动关闭这一端调用close函数发送FIN包给主动关闭这一端后,被动关闭这一端处于LAST_ACK,之后被动关闭这一端会收到ACK包就处于CLOSE状态。

7、当两端都主动关闭

主动关闭这一端A端调用close函数,发送FIN包给被动关闭这一端B端后处于FIN_WAIT_1,本来被动关闭这一端B端是要返回ACK包的,但此时被动关闭这一端B也主动关闭了并发送FIN包给主动关闭这一端A端,B端就也处于FIN_WAIT_1并且B端返回ACK包给A端,A端就处于CLOSING并返回ACK包给B端,B端也处于CLOSING状态。接下来A端和B端会从CLOSING转为TIME_WAIT,持续2MSL后都转为CLOSE状态。

技术分享图片

8、FIN+ACK包

主动关闭这一端A端调用close函数,发送FIN包给被动关闭这一端B端后处于FIN_WAIT_1,本来被动关闭这一端接下来是要处于CLOSE_WAIT并返回ACK包,等到到被动关闭这一端调用了close函数时会再发送FIN包后处于LAST_ACK。但也有一种情况就是被动关闭这一端在接受到FIN包这一瞬间刚好调用了close函数,此时被动关闭这一端就跳过CLOSE_WAIT马上处于LAST_ACK,并发送了FIN+ACK包。主动关闭这一端本来还有一个FIN_WAIT_2,现在接收到FIN+ACK包后就跳到TIME_WAIT并发送ACK包给被动关闭这一端,主动关闭这一端持续2MSL后转为CLOSE状态,被动关闭这一端收到最后的ACK后也转为CLOSE状态。

技术分享图片

以上是关于TCP状态查看以及故障的排查的主要内容,如果未能解决你的问题,请参考以下文章

TCP连接的状态详解以及故障排查

TCP连接的状态详解以及故障排查

转载:TCP连接的状态详解以及故障排查

TCP连接的状态详解以及故障排查

TCP连接的状态详解以及故障排查

TCP连接状态变化