TCP状态机
在网络协议栈中, 目前只有tcp提供了一种面向连接的可靠性数据传输。 而可靠性,无非就是保证,我发给你的,你一定要收到。确保中间的通信过程中,不会丢失数据和乱序。 在TCP保证可靠性数据传输的实现来看, 超时重传、序列号及数据的应答 这三个特征 就是实现可靠性的最基本保证,而对于tcp窗口大小等等设置,也是保证可靠性的一个方面。所有的目的只为一个,保证传输数据的完整性。 为了解决传输线路的不稳定性造成数据包的丢失情况,tcp 使用了发送方超时重传和接收方数据应答的策略。概括而言,就是一种“状态协议“,保证通信双方数据收发的一致性。
tcp状态机
image.png下面简析一下 TCP的各个状态。
TCP_CLOSE:关闭状态, 一个新建的TCP socket 会处于该状态。
TCP_LISTEN: 监听状态,一般服务器端套接字在调用Listen系统调用后即处于该状态。
TCP_SYN_SENT:同步信号已经发送状态,这个状态一般是指客户端发送SYN(建立连接的同步)数据包后所处的状态(tcp三次握手的第一个包)。在接收到远端服务器端的应答后,即从该状态进入TCP_ESTABLISHED状态。
TCP_SYN_RECEIVED:同步信号已经接受状态,服务器端在接受到远端客户端SYN数据包后,进行相应的处理(创建通信套接字等),然后发送应答数据包(tcp三次握手的第二个包),并将新创建的通信套接字状态设置为TCP_SYN_RECEIVED,在接受到客户端的应答后,即进入TCP_ESTABLISED状态。
TCP_ESTABLISED:建立连接状态,这是双方进行正常通信所处的状态。
TCP_FIN_WAIT_1:本地发送FIN(用于结束连接的)数据包后即可进入该状态,等待对方的应答。一般一端发送完其所要发送的数据后,即可发送FIN数据包,此时发送通道被关闭,但仍可继续接受远端发送的数据包。在接受到远端发送的对于FIN数据包的应答后,将进入TCP_FIN_WAIT_2状态。
TCP_FIN_WAIT_2:进入该状态表示本地已经接受到远端发送的对于本地之前发送的FIN数据包的应答。进入该状态后,本地仍然可以继续接受远端发送给本地的数据包。在接受到远端发送的FIN数据包后(表示远端也已经发送完数据),本地将发送一个应答数据包,并进入TCP_TIME_WAIT状态。TCP_TIME_WAIT状态存在的时间被称为2MSL时间,这一方面是为避免本地发送的应答数据包丢失,另一方面避免一个新创建的套接字接收到旧套接字中遗留的数据包。
TCP_TIME_WAIT:该转状态呗称为2MSL等待状态。如果在此期间接收到远端发送的FIN数据包,则表示之前在TCP_FIN_WAIT_2状态发送的ACK应答数据包在传输中丢失或者长时间被延迟,从而造成了远端重新发送了FIN数据包,此时重复ACK应答数据包。一旦2MSL时间到期,则将进入TCP_CLOSED状态,即完成关闭操作。
TCP_CLOSE_WAIT:该状态存在于后关闭的一端。当接收到远端发送的FIN数据包后,本地发送一个ACK应答数据包,并将该套接字状态从TCP_ESTABLISED设置为TCP_CLOSE_WAIT。本地可以继续向远端发送数据包,在发送完所有的数据后,本地将发送一个FIN数据包关闭本地发送通道,并将状态设置为TCP_LAST_ACK状态,等待远端对FIN数据包的应答数据包。
TCP_CLOSING:如果通信双方同时发送FIN数据包,则同时进行关闭操作,则双方将同时进入TCP_CLOSING状态。具体的,本地发送一个FIN数据包以结束本地数据包发送,如果在等待应答期间,接收到远端发送的FIN数据包,则本地将状态设置为TCP_CLOSING状态。在接收到应答后,再继续装入到TCP_CLOSE_WAIT状态。
TCP_LAST_ACK:作为后关闭的一方,在发送FIN数据包后,即进入TCP_LAST_ACK状态。此时等待远端发送应答数据包,在接收到应答数据包后,即完成关闭操作,进入TCP_CLOSE状态。
关于tcp中time_wait状态的4个问题
1.time_wait状态是什么
简单来说:time_wait状态是四次挥手中server向client发送FIN终止连接后进入的状态。下图为tcp四次挥手过程
能够看到time_wait状态存在于client收到serverFin并返回ack包时的状态
当处于time_wait状态时,我们无法创建新的连接,由于port被占用。
2.为什么会有time_wait状态
time_wait存在的原因有两点
1.可靠的终止TCP连接。
可靠的终止TCP连接,若处于time_wait的client发送给server确认报文段丢失的话,server将在此又一次发送FIN报文段,那么client必须处于一个可接收的状态就是time_wait而不是close状态。
2.保证让迟来的TCP报文段有足够的时间被识别并丢弃。
保证迟来的TCP报文段有足够的时间被识别并丢弃,linux 中一个TCPport不能打开两次或两次以上。当client处于time_wait状态时我们将无法使用此port建立新连接,假设不存在time_wait状态,新连接可能会收到旧连接的数据。
time_wait持续的时间是2MSL,保证旧的数据能够丢弃。由于网络中的数据最大存在MSL(maxinum segment lifetime)
3.哪一方会有time_wait状态
time_wait状态是一般有client的状态。 并且会占用port,有时产生在server端,由于server主动断开连接或者发生异常
4.怎样避免time_wait状态占用资源
假设是client,我们一般不用操心,由于client一般选用暂时port。再次创建连接会新分配一个port。
除非指定client使用某port,只是一般不须要这么做。
假设是server主动关闭连接后异常终止。则由于它总是使用用一个知名serverport号,所以连接的time_wait状态将导致它不能重新启动。只是我们能够通过socket的选项SO_REUSEADDR来强制进程马上使用处于time_wait状态的连接占用的port。
通过socksetopt设置后,即使sock处于time_wait状态,与之绑定的socket地址也能够马上被重用。
此外也能够通过改动内核參数/proc/sys/net/ipv4/tcp_tw/recycle来高速回收被关闭的socket,从而是tcp连接根本不进入time_wait状态,进而同意应用程序马上重用本地的socket地址。