TCP状态转换图解析和应用

来源:互联网 发布:ios5.0.1软件下载 编辑:程序博客网 时间:2024/05/29 15:10


TCP状态转换分服务端和客户端两部分:

一、服务端状态变迁:​

服务端​创建套接字之后调用listen函数将套接字有一个未连接的主动套接字转换为被动套接字,指示内核应接受指向该套接字的连接请求,套接字状态由CLOSE转换为LISTEN,等待客户端连接。所以服务端是被动接收连接的,服务端会先收到SYN,收到之后会立马发送一个SYN+ACK(同一个报文),此时状态转换到SYN_RCVD并等待客户端回复ACK,此时套接字处于未完成连接队列中,如果收到ACK状态会转换到ESTABLISHED,套接字处于已完成连接队列中,注意的是未完成连接队列和已完成连接队列之和不能超过listen设置的最大连接个数。这时服务端和客户端可以进行数据交互,客户端接收完数据之后主动close套接字,此时服务端会收到FIN并回复ACK,状态转换到LOSE_WAIT,当服务端的应用层也close套接字时服务端会发生一个FIN状态转换到LAST_ACK然后会收到客户端回复的ACK,状态转换到CLOSED。

二、客户端状态变迁:

客户端创建套接字之后会connect服务器,这时客户端会发送一个SYN到服务器,状态转换到SYN_SENT并等待服务器的回复,收到服务端的回复SYN+ACK(同一个报文)之后​​客户端会回复ACK此时状态转换到ESTABLISHED,正常数据交互完成之后客户端会close套接字此时发送一个FIN报文,状态转换到FIN_WAIT_1,同时等待服务端的回复,此时有三种情况:

(1)收到服务端的ACK但此时服务端没有关闭套接字。状态转换到了FIN_WAIT_2,然后再等待服务端关闭套接字发出的FIN,如果收到则回复ACK,状态转换到TIME_WAIT状态,等待2MSL超时之后自动转换为CLOSED状态。

​(2)服务端同时也在关闭套接字,此时客户端会收到SYN并发出ACK,状态转换到CLOSING,之后等待服务端回复ACK,若收到ACK则转到TIME_WAIT状态。

(3)服务器在收到客户端FIN之后立马关闭套接字,此时客户端会收到一个ACK和FIN并发出ACK,状态​转换到TIME_WAIT状态。

以上提到的是一般正常的情况的TCP状态转换,当然还有很多异常情况这里不再一一描述,值得一提的是​ESTABLISHED状态之后,服务端也可以主动close套接字,转换到FIN_WAIT_1状态,此时服务端和客户端的状态转换就正好和上面描述相反了。

​三、同时打开

同时打开时两端几乎在同时发送SYN,并进入SYN_SENT状态。当某一端收到SYN时,状态变为SYN_RCVD状态,收到SYN之后发出SYN+ACK,当双方都收到对端发送的SYN+ACK之后,套接字进入ESTABLISHED状态。同时打开链接需要交换4个报文比正常的3次握手多一个,而且双方既是客户端也是服务器,此种情况为双方同时connect对端。

​四、同时关闭

同时关闭时双方都是主动关闭,双方状态变迁一样。以某一方为例:发送一个FIN,进入FIN_WAIT_1状态,接收一个FIN​同时发送一个ACK进入CLOSING状态,最后接收到对方回复的ACK进入TIME_WAIT状态。

​TIME_WAIT状态的问题及解决办法:

TIME_WAIT状态是描述TCP状态时不得不提的一种情况,这种状态下系统会自动的等待时间1分钟到4分钟不等然后转换到CLOSED状态,这种状态的套接字过多会暂用系统的资源使得新的连接无法正常响应。需要注意的是只有主动关闭段会经历TIME_WAIT状态,被动关闭不会产生此状态,当套接字处于TIME_WAIT状态时此套接字的插口对将不能在被使用(包括本地IP,本地端口,远端IP,远端端口的四元组),对本地而言套接字使用的本地端口不能被绑定使用,当然可以在创建套接字时使用SO_REUSEADDR选项这样可以绑定处于TIME_WAIT状态的端口。但是TCP不允许一个新的连接建立在相同的插口对上,但是现实中的很多系统支持此类连接的建立,只要求新替身的序号大于该链接前一个替身的最后序号。

这个是TCP/IP的设计者规定的,主要出于以下两个方面的考虑:

​1、防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)

​2、可靠的关闭TCP连接。在主动关闭方发送的最后一个 ACK(FIN) ,有可能丢失,这时被动方会重新发FIN, 如果这时主动方处于 CLOSED 状态 ,就会响应 RST 而不是 ACK。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。​

用套接字SO_LINGER属性可以解决此问题,但是​SO_LINGER可能会导致数据丢失,在使用时需要谨慎。

struct linger{

int l_onoff;

int l_linger;​

}​;

​(1)l_onoff为0时,本属性关闭,套接字close之后立即返回

(2)​l_onoff为非0时,,本属性生效,如果l_linger值为0则套接字close之后TCP会丢弃保留在套接字发送缓冲区的任何数据,并发送一个RST给对端,没有了通常的四次分手,这样可以避免套接字的TIME_WAIT状态,然而这样会将还未来得及发送的数据丢弃导致对端接收数据不完整,这时可以将l_linger值设置为一个正值,这样内核会在close之后等待l_linger秒直到数据发送完成或者l_linger秒超时后才会丢弃缓冲区中的数据。当然以上总结仅限阻塞套接字,如果是非阻塞套接字它将不等待close完成。但是避免了TIME_WAIT状态可能导致在2MSL时间内创建的另一个同一个端口上的链接接收到上个链接还残留在网络中的数据。


0 0
原创粉丝点击