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状态查看以及故障的排查的主要内容,如果未能解决你的问题,请参考以下文章