Linux网络编程中,socket的选项很多,其中几个比较重要的选项有:SO_LINGER(仅仅适用于TCP,SCTP),SO_REUSEADDR。在默认情况下,当调用close关闭socke的使用,close会立即返回。但是,如果send buffer中还有数据,系统会试着先把send buffer中的数据发送出去,然后close才返回。
SO_LINGER选项则是用来修改这种默认操作的。于SO_LINGER相关联的一个结构体如下:
#include <sys/socket.h>
struct linger {
int l_onoff; //0=off, nonzero=on(开关)
int l_linger; //linger time(延迟时间)
}
当调用setsockopt之后,该选项产生的影响取决于linger结构体中 l_onoff 和 l_linger 的值:
l_onoff = 0, l_linger忽略
当l_onoff被设置为0的时候,将会关闭SO_LINGER选项,即TCP或则SCTP保持默认操作:close立即返回,底层会将未发送完的数据发送完成后再释放资源,也就是优雅的退出。
l_lineoff 值非0,l_linger = 0
当调用close的时候,TCP连接会立即断开。send buffer中未被发送的数据将被丢弃,并向对方发送一个RST信息。值得注意的是,由于这种方式,是非正常的4中握手方式结束TCP链接,所以,TCP连接将不会进入TIME_WAIT状态。这样就可以解决过多的的TIME_WAIT导致资源不足的问题,但是这样会导致新建立的可能和就连接的数据造成混乱。
3、设置 l_onoff 为非0,l_linger为非0
当套接口关闭时内核将拖延一段时间(由l_linger决定)。如果套接口缓冲区中仍残留数据,进程将处于睡眠状态,直 到(a)所有数据发送完且被对方确认,之后进行正常的终止序列(描述字访问计数为0)或(b)延迟时间到。此种情况下,应用程序检查close的返回值是非常重要的,如果在数据发送完并被确认前时间到,close将返回EWOULDBLOCK错误且套接口发送缓冲区中的任何数据都丢失。close的成功返回仅告诉我们发送的数据(和FIN)已由对方TCP确认,它并不能告诉我们对方应用进程是否已读了数据。如果套接口设为非阻塞的,它将不等待close完成。
如果socket被设置为O_NONBLOCK状态,程序将不会等待close返回,send buffer中的所有数据都将会被丢弃,也就是强制断开。所以,需要我们判断close的返回值。在send buffer中的所有数据都被发送之前并且延迟时间没有消耗完,close返回的话,close将会返回一个EWOULDBLOCK的error。
//-------------------------------------------------------------------------
因此,close一般有一下情况:
1.默认操作的close,close立即返回
2.设置SO_LINGER套接字选项且l_linger为正值时的close:【数据ack了,并收到FIN了】 才返回。这种情况下客户的close要到它的数据和FIN已经被服务器的TCP确认以后才会返回;
3.设置SO_LINGER套接字选项且l_linger为偏小正值时的close:时间到了 返回-1,EWOULDBLOCK错误。在服务端的确认到达之前,SO_LINGER套接字选项设置的延滞时间到,close将会返回EWOULDBLOCK错误,且套接字发送缓冲区中的任何残留数据被丢弃。
总结:设置SO_LINGER套接字选项以后,close的成功返回只是告诉我们先前发送的数据的FIN已经由对端TCP确认,而不能告诉我们对端应用进程是否已经读取数据,如果不设置该套接字选项,那么我们连对端TCP是否确认了数据都不知道。