目录
1、TCP半关闭状态
通信的一端可以发送结束报文给对方,告诉它本端已经完成了数据的发送,但允许继续接受对端发送过来的数据,直到对端也发送结束报文段关闭连接。
linux下使用shutdown进行半关闭
#include <sys/socket.h>
int shutdown(int sockfd, int how);
sockfd: 需要关闭的socket的描述符
how: 允许为shutdown操作选择以下几种方式:
SHUT_RD(0): 关闭sockfd上的读功能,此选项将不允许sockfd进行读操作。
该套接字不再接受数据,任何当前在套接字接受缓冲区的数据将被无声的丢弃掉。
SHUT_WR(1): 关闭sockfd的写功能,此选项将不允许sockfd进行写操作。进程不能在对此套接字发出写操作。
SHUT_RDWR(2): 关闭sockfd的读写功能。相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR。
2、TCP半打开状态
如果一方已经关闭或者异常终止连接而另外一方却还不知道,这样的连接就称为半打开连接(Half open connection)。处于半打开的连接,如果双方不进行数据通信,是发现不了问题的
3、TCP连接建立和断开状态变化图
4、TIME_WAIT状态
MSL是TCP报文段在网络中的最大生存时间,RFC 1122的建议值是2min
存在原因:
1、可靠的关闭TCP连接
2、保证迟来的TCP报文段有足够的时间被识别并丢弃
第一个原因,假设用于确认报文段6的TCP报文段7丢失,那么服务器将重发结束报文段。因此客户端需要停留在某个状态以处理重复收到的结束报文段。否则,客户端将以RST报文段来回应服务器,服务器则认为这是一个错误。
第二个原因,处于TIME_WAIT状态的TCP端口,我们无法立即使用该连接占用着的端口来建立一个新的连接。反过来思考如果可以建立,这个新的连接可能接收到属于原来的连接的、携带应用程序数据的TCP报文段(或者迟到的FIN报文段),这显然是不应该发生的。
当然,我们可以使用SO_REUSEADDR来强制进程立即使用处于TIME_WAIT状态的连接占用的端口
5、CLOSE_WAIT状态
套接字被动关闭会进入CLOSE_WAIT状态,什么情况下TCP会长时间处于CLOSE_WAIT状态?
首先不考虑不调用close或者shutdown的情况,我们的程序处于CLOSE_WAIT状态,而不是LAST_ACK状态,说明还没有发FIN给Server,那么可能是在关闭连接之前还有许多数据要发送或者其他事要做,导致没有发这个FIN packet。
如何保证程序调用close以后立即关闭连接?SO_LINGER选项提供了异常终止一个连接的方法
#include <arpa/inet.h>
struct linger {
int l_onoff;
int l_linger;
};
l_onoff = 1; l_linger = 0;
close系统调用立即返回,TCP模块将丢弃关闭的socket对应的TCP发送缓冲区残留数据,同时给对端发送RST报文
6、TCP收到RST报文的几种情况
(1)访问不存在的端口
若端口不存,则直接返回RST,同时RST报文接收通告窗口大小为0
其实客户端向服务器的某个端口发起连接,如果端口被处于TIME_WAIT 状态的连接占用时,客户端也会收到RST。这种情况很常见。特别是服务器程序core dump之后重启之前连续出现RST的情况会经常发生。
(2)访问TIME_WAIT状态占用的端口
(3)异常终止连接
一方直接发送RST报文,表示异常终止连接。应用程序可以通过socket选项SO_LINGER来发送RST复位报文。而接收端收到RST包后,不必发送ACK包来确认。
(4)处理半打开连接
一方关闭了连接,另一方却没有收到结束报文(如网络故障),此时另一方还维持着原来的连接。而一方即使重启,也没有该连接的任何信息。这种状态就叫做半打开连接。而此时另一方往处于半打开状态的连接写数据,则对方回应RST复位报文。此时会出现connect reset by peer错误。
(5)请求超时
用setsockopt的SO_RCVTIMEO选项设置了recv的超时时间,sync 未完成时超时了,会发送RST终止连接。