二、所有11种状态服务器
2.一、客户端独有的:(1)SYN_SENT (2)FIN_WAIT1 (3)FIN_WAIT2 (4)CLOSING (5)TIME_WAIT 。网络
2.二、服务器独有的:(1)LISTEN (2)SYN_RCVD (3)CLOSE_WAIT (4)LAST_ACK 。并发
2.三、共有的:(1)CLOSED (2)ESTABLISHED 。oracle
TCP状态迁移socket
你们对netstat -a命令很熟悉,可是,你有没有注意到STATE一栏呢,基本上显示着established,time_wait,close_wait等,这些究竟是 什么意思呢,在这篇文章,我将会详细的阐述。tcp
你们很明白TCP初始化链接三次握手吧:发SYN包,而后返回SYN/ACK包,再发ACK包,链接正式创建。可是这里有点出入,当请求者收到SYS /ACK包后,就开始创建链接了,而被请求者第三次握手结束后才创建链接。可是你们明白关闭链接的工做原理吗?关闭链接要四次握手:发FIN包,ACK 包,FIN包,ACK包,四次握手!!为何呢,由于TCP链接是全双工,我关了你的链接,并不等于你关了个人链接。测试
客户端TCP状态迁移:spa
|
服务器TCP状态迁移:blog
CLOSED->LISTEN->SYN收到 ->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED
当客户端开始链接时,服务器还处于LISTENING,
客户端发一个SYN包后,他就处于SYN_SENT状态,服务器就处于SYS收到状态,
而后互相确认进入链接状态ESTABLISHED.
当客户端请求关闭链接时,客户端发送一个FIN包后,客户端就进入FIN_WAIT_1状态,等待对方的确认包,
服务器发送一个ACK包给客户,客户端收到ACK包后结束FIN_WAIT_1状态,进入FIN_WAIT_2状态,等待服务器发过来的关闭请求,
服务器发一个FIN包后,进入CLOSE_WAIT状态,
当客户端收到服务器的FIN包,FIN_WAIT_2状态就结束,而后给服务器端的FIN包给以一个确认包,客户端这时进入TIME_WAIT,
当服务器收到确认包后,CLOSE_WAIT状态结束了,
这时候服务器端真正的关闭了链接.可是客户端还在TIME_WAIT状态下,
何时结束呢.我在这里再讲到一个新名词:2MSL等待状态,其实TIME_WAIT就是2MSL等待状态,
为何要设置这个状态,缘由是有足够的时间让ACK包到达服务器端,若是服务器端没收到ACK包,超时了,而后从新发一个FIN包,直到服务器收到ACK 包.
TIME_WAIT状态等待时间是在TCP从新启动后不链接任何请求的两倍.
你们有没有发现一个问题:若是对方在第三次握手的时候出问题,如发FIN包的时候,不知道什么缘由丢了这个包,然而这边一直处在FIN_WAIT_2状 态,并且TCP/IP并无设置这个状态的过时时间,那他一直会保留这个状态下去,愈来愈多的FIN_WAIT_2状态会致使系统崩溃.
上面我碰到的这个问题主要由于TCP的结束流程未走完,形成链接未释放。现设客户端主动断开链接,流程以下:
|
因为Server的Socket在客户端已经关闭时而没有调用关闭,
形成服务器端的链接处在“挂起”状态,而客户端则处在等待应答的状态上。
此问题的典型特征是:
一端处于FIN_WAIT2 ,而另外一端处于CLOSE_WAIT.
不过,根本问题仍是程序写的很差,有待提升
-------------------------------------------------------------------------
CLOSE_WAIT,TCP的癌症,TCP的朋友。
CLOSE_WAIT状态的生成缘由
首先咱们知道,若是咱们的服务器程序APACHE处于CLOSE_WAIT状态的话,说明套接字是被动关闭的!
由于若是是CLIENT端主动断掉当前链接的话,那么双方关闭这个TCP链接共须要四个packet:
Client ---> FIN ---> Server
Client <--- ACK <--- Server
这时候Client端处于FIN_WAIT_2状态;而Server 程序处于CLOSE_WAIT状态。
Client <--- FIN <--- Server
这时Server 发送FIN给Client,Server 就置为LAST_ACK状态。
Client ---> ACK ---> Server
Client回应了ACK,那么Server 的套接字才会真正置为CLOSED状态。
Server 程序处于CLOSE_WAIT状态,而不是LAST_ACK状态,说明尚未发FIN给Client,那么多是在关闭链接以前还有许多数据要发送或者其 他事要作,致使没有发这个FIN packet。
一般来讲,一个CLOSE_WAIT会维持至少2个小时的时间。若是有个流氓特意写了个程序,给你形成一堆的 CLOSE_WAIT,消耗你的资源,那么一般是等不到释放那一刻,系统就已经解决崩溃了。
只能经过修改一下TCP/IP的参数,来缩短这个时间:修改tcp_keepalive_*系列参数有助于解决这个 问题。
解决这个问题的方法是修改系统的参数,系统默认超时时间的是7200秒,也就是2小时, 这个太大了,能够修改以下几个参数:
|
而后,执行sysctl命令使修改生效。
链接进程是经过一系列状态表示的,这些状态有:
|
各个状态的意义以下:
LISTEN - 侦听来自远方TCP端口的链接请求;
SYN-SENT -在发送链接请求后等待匹配的链接请求;
SYN-RECEIVED - 在收到和发送一个链接请求后等待对链接请求的确认;
ESTABLISHED- 表明一个打开的链接,数据能够传送给用户;
FIN-WAIT-1 - 等待远程TCP的链接中断请求,或先前的链接中断请求的确认;
FIN-WAIT-2 - 从远程TCP等待链接中断请求;
CLOSE-WAIT - 等待从本地用户发来的链接中断请求;
CLOSING -等待远程TCP对链接中断的确认;
LAST-ACK - 等待原来发向远程TCP的链接中断请求的确认;
TIME-WAIT -等待足够的时间以确保远程TCP接收到链接中断请求的确认;
CLOSED - 没有任何链接状态;
TCP链接过程是状态的转换,促使发生状态转换的是用户调用:
|
传送过来的数据段,特别那些包括如下标记的数据段SYN,ACK,RST和FIN;
还有超时,上面所说的都会时TCP状态发生变化。
这个图n多人都 知道,它对排除和定 位网络或系统故障时大有帮助,可是怎样紧紧地将这张图刻在脑中呢?那么你就必定要对 这张图的每个状态,及转换的过程有深入地认识,不能只停留在只知其一;不知其二之中。下面对这张图的11种状 态详细解释一下,以便增强记忆!不过在这以前,先回顾一下TCP创建链接的三次握手过程,以及关闭链接的四次握手过程。
一、创建链接协议(三次握手)
(1)客户 端发送一个带SYN标志的TCP报文到服务器。这是三次握手过程当中的报文1。
(2) 服务器端回应客户端的,这是三次握手中的第2个报文,这个报文同时带ACK标志和SYN标 志。所以它表示对刚才客户端SYN报文的回应;同时又标志SYN给客户端,询问客户端是否准备好进行数据通 讯。
(3) 客户必须再次回应服务段一个ACK报文,这是报文段3。
二、链接终止协议(四次握手)
因为TCP连 接是全双工的,所以每一个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终 止这个方向的链接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP链接 在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另外一方执行被动关闭。
(1) TCP客 户端发送一个FIN,用来关闭客户到服务器的数据传送(报文段4)。
(2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一 样,一个FIN将占用一个序号。
(3) 服务器关闭客户端的链接,发送一个FIN给客户端(报文段6)。
(4) 客户段发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。
CLOSED: 这个没什么好说的了,表示初始状态。
LISTEN: 这个也是很是容易理解的一个状态,表示服务器端的某个SOCKET处 于监听状态,能够接受链接了。
SYN_RCVD: 这个状态表示接受到了SYN报 文,在正常状况下,这个状态是服务器端的SOCKET在创建TCP链接时的三次握手会话过程当中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特地写了一个客户端测试程序,故意将三次TCP握手 过程当中最后一个ACK报文不予发送。所以这种状态时,当收到客户端的ACK报文 后,它会进入到ESTABLISHED状态。
SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT链接时,它首先发送SYN报文,所以也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
ESTABLISHED:这个容易理解了,表示链接已经创建了。
FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报 文。而这两种状态的区别是:FIN_WAIT_1状态其实是当SOCKET在ESTABLISHED状态时,它想主动关闭链接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,固然在实际的正常状况 下,不管对方何种状况下,都应该立刻回应ACK报文,因此FIN_WAIT_1状态通常是比较难见到的,而FIN_WAIT_2状态还有时经常能够用netstat看到。
FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半链接,也即有一方要求close链接,但另外还告诉对方,我暂时还有点 数据须要传送给你,稍后再关闭链接。
TIME_WAIT: 表示收到了对方的FIN报 文,并发送出了ACK报文,就等2MSL后便可回到CLOSED可用状态了。若是FIN_WAIT_1状态下,收到了对方同时带FIN标 志和ACK标志的报文时,能够直接进入到TIME_WAIT状态,而无须通过FIN_WAIT_2状态。
CLOSING: 这种状态比较特殊,实际状况中应该是不多见,属于一种比较罕见的例外状态。正常状况下,当你发 送FIN报文后,按理来讲是应该先收到(或同时收到)对方的ACK报 文,再收到对方的FIN报文。可是CLOSING状态表示你发送FIN报文后,并无收到对方的ACK报 文,反而却也收到了对方的FIN报文。什么状况下会出现此种状况呢?其实细想一下,也不可贵出结论:那就是若是双方几乎在同时close一 个SOCKET的话,那么就出现了双方同时发送FIN报文的状况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET链接。
CLOSE_WAIT: 这种状态的含义实际上是表示在等待关闭。怎么理解呢?当对方close一 个SOCKET后发送FIN报文给本身,你系统毫无疑问地会回应一个ACK报文 给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正须要考虑的事情是察看你是否还有数据发送给对方,若是没有的话, 那么你也就能够close这个SOCKET,发送FIN报文给对方,也即关闭链接。因此你在CLOSE_WAIT状态下,须要完成的事情是等待你去关闭链接。
LAST_ACK: 这个状态仍是比较容易好理解的,它是被动关闭一方在发送FIN报 文后,最后等待对方的ACK报文。当收到ACK报文后,也便可以进入到CLOSED可用状态了。
最后有2个问题 的回答,我本身分析后的结论(不必定保证100%正确)
一、 为何创建链接协议是三次握手,而关闭链接倒是四次握手呢?
这是由于服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它能够把ACK和SYN(ACK起 应答做用,而SYN起同步做用)放在一个报文里来发送。但关闭链接时,当收到对方的FIN报文 通知时,它仅仅表示对方没有数据发送给你了;但未必你全部的数据都所有发送给对方了,因此你能够未必会立刻会关闭SOCKET,也即你可能还须要发送一些数据给对方以后,再发送FIN报文给对方来表示你赞成如今能够关闭链接了,因此它这里的ACK报文 和FIN报文多数状况下都是分开发送的。
二、 为何TIME_WAIT状态还须要等2MSL后才能返回到CLOSED状 态?
这是由于:虽然双方 都赞成关闭链接了,并且握手的4个报文也都协调和发送完毕,按理能够直接回到CLOSED状 态(就比如从SYN_SEND状态到ESTABLISH状态那样);可是由于咱们必需要假想网络是不可靠的,你没法保证你最后发送的ACK报 文会必定被对方收到,所以对方处于LAST_ACK状态下的SOCKET可能会由于超时未收到ACK报文,而重发FIN报 文,因此这个TIME_WAIT状态的做用就是用来重发可能丢失的ACK报 文,并保证于此。
断开链接的时候, 当发起主动关闭的左边这方发送一个FIN过去后,
右边被动关闭的这方要回应一个ACK,这个ACK是TCP回应的,而不是应用程序发送的,
此时,被动关闭的一方就处于CLOSE_WAIT状态了。
若是此时被动关闭的这一方再也不继续调用closesocket,那么他就不会发送接下来的FIN,致使本身总是处于CLOSE_WAIT。
只有被动关闭的这一方调用了 closesocket,才会发送一个FIN给主动关闭的这一方,同时也使得本身的状态变迁为LAST_ACK。
好比被动关闭的是客户端
当对方调用closesocket的时候,你的程序正在
|
不少人就是忘记了那句closesocket,这种代码太常见了。
个人理解,
当主动关闭的一方发送FIN到被动关闭这边后,被动关闭这边的TCP立刻回应一个ACK过去,同时向上面应用程序提交一个ERROR,
致使上面的SOCKET的send或者recv返回SOCKET_ERROR.
正常状况下,若是上面在返回SOCKET_ERROR后调用了closesocket, 那么被动关闭的者一方的TCP就会发送一个FIN过去,本身的状态就变迁到LAST_ACK.
服务器上出现大量的close_wait的例子和解决方法(例子从网上找的,基本差很少)
oracle 22725 oracle9i 3u IPv4 18621468 TCP RHEL3:6800 (LISTEN) oracle 22725 oracle9i 4u IPv4 18621469 TCP RHEL3:6800->RHEL3:2174 (CLOSE_WAIT) oracle 22725 oracle9i 8u IPv4 18621568 TCP RHEL3:6800->RHEL3:2175 (CLOSE_WAIT) oracle 22725 oracle9i 9u IPv4 18621578 TCP RHEL3:6800->RHEL3:2176 (CLOSE_WAIT) oracle 22726 oracle9i 3u IPv4 18621468 TCP RHEL3:6800 (LISTEN) oracle 22726 oracle9i 4u IPv4 18621469 TCP RHEL3:6800->RHEL3:2174 (CLOSE_WAIT) oracle 22726 oracle9i 8u IPv4 18621568 TCP RHEL3:6800->RHEL3:2175 (CLOSE_WAIT) oracle 22726 oracle9i 9u IPv4 18621578 TCP RHEL3:6800->RHEL3:2176 (CLOSE_WAIT) $ kill -9 22725# 22725, 22726就是使用该6800端口的进程号(PID)。 $ /usr/sbin/lsof -i | grep 6800
|
进程被kill时,会释放占用的全部连接句柄。
该问题的出现缘由网上处处都是,也就是Socket的Client端出现异常没有Close就退出了