TCP的可靠性与故障模式

TCP真的可靠么?
我们都说TCP是可靠的,但是TCP真的就那么可靠么?
1、当TCP的发送端调用write(或send)函数,发送数据的时候,只是把数据拷贝到发送缓冲区中,数据怎么发送什么时候发送,是由TCP/IP协议控制的。如果发送端调用完write函数后,崩溃了,那么发送缓冲区中的数据就丢失了。当发送缓冲区中的数据发送出去后,接收方返回的ACK报文到达后,发送缓冲区中的数据就会删除了,接收方的应用程序是否已经处理,对于发送方来说是无法感知到的。
2、当接收方接收到数据的时候,是把数据存放在接收缓冲区里的,如果应用程序奔溃,那么接收缓冲区的数据将会清空。可见接收方也没法保证经过ACK的数据,就一定能够被应用程序处理。
由此可见TCP的可靠性只能保证端到端的通信的可靠,并不能保证应用程序一定处理正确。为此如果需要在应用层实现可靠性,需要应用程序自己实现,如显示的消息确认。

TCP的故障模式

TCP连接建立后,感知链路的异常方式是有限的,一种是通过read调用一种是通过write调用。我把TCP的异常按照如下的图片划分。
TCP的可靠性与故障模式

对端无FIN包之网络故障

很多情况会导致网络中断,网络中断后,在静默的连接上TCP是感知不到连接异常的,除非网络中的设备通过ICMP告知网络不可达或主机不可达。这种特例比较少见。更多的是如下的情况:
1、只用read调用,如果是堵塞socket,在没有设置超时时间的情况下,那么很不幸,程序会一直堵塞在read调用上面。如果设置了超时时间,read会返回超时。对于非堵塞socket会返回EAGAIN或者EWOULDBLOCK错误;
2、第一次write调用后,linux会不断的重传12次,大约9分钟(1+2+4+8+16+32+64+128+256=511)后,如果再次调用write会导致发送SIGPIPE信号,但如果调用的是read,则会返回超时TIMEOUT错误。

程序崩溃,如断电,断电后没有重启

与网络中断一样

程序崩溃,如断电,断电后重启

1、无write,只有read。与网络中断的情况一致;
2先write调用后,会收到RST报文,如果此时再次调用write会导致发送SIGPIPE信号,如果调用的是read,会返回连接重置Connection reset by peer错误。

对端有FIN包(如程序崩溃),对端收到FIN报文

1、当read函数读完缓冲区的数据后,会返回0;
收到FIN报文后,再次调用write函数,会收到RST报文,如果再次调用write函数,会导致内核发送SIGPIPE信号或者连接重置Connection reset by peer错误,但是如果调用的是read函数,则会返回连接重置Connection reset by p