Linux下的套接口选项SO_LINGER
Linux提供了一个套接口选项SO_LINGER,可以改变在套接口上执行close()函数时的默认行为,对于改变之后的行为如何,网上有大量的资料人云亦云的对其进行了说明,但是事实上却并非如此。
选项SO_LINGER用到的相关参数主要是一个linger结构体:
Filename : \linux-3.4.4\include\linux\socket.h struct linger { int l_onoff; /* Linger active */ int l_linger; /* How long to linger for */ };
注释很清楚,字段l_onoff标记是否启用Linger特性,非0为启用,0为禁用(即内核对close()函数采取默认行为);字段l_onoff为非0的情况下,字段l_linger生效,如果它的值为0,则导致所有数据丢失且连接立即中止;如果字段l_linger的值为非0(假定为t秒),那么此时函数close()将被阻塞(假定为阻塞模式)直到:
1)待发送的数据全部得到了对端确认,返回值为0;
2)超时返回,返回值为-1,errno被设置为EWOULDBLOCK。
上面两点是很多介绍TCP/IP协议的经典书(比如Richard Steven的《Unix网络编程》)上所描述的,但是却并不适合Linux系统上的实现(《Unix网络编程》应该是根据BSD上的实现来讲的,所以有些结论不适合Linux系统上的实现,这很正常)。在Linux系统上,应该是函数close()将被阻塞(假定为阻塞模式)直到:
1)待发送的数据全部得到了对端确认,返回值为0;
2)发生信号中断或异常(比如意外收到对端发送过来的数据)或超时,返回值为0;
也就是说,在Linux系统上,针对SO_LINGER选项而言,不论哪种情况,函数close()总是返回0(注意我所针对的情况,我并没有说在Linux系统上,函数close()就总是返回0,如果你关闭一个无效的描述符,它同样也会返回-EBADF的错误),并且对于情况2),Linux内核不会清空缓存区,更加不会向对端发送RST数据包,即执行close()函数的后半部分代码时不会因此发送任何特别的流程变化(当然,因为close()函数阻塞了一段时间,在这段时间内,套接口相关字段可能被TCP协议栈修改过了,所以导致相关判断结果发生变化,但这并不是由于情况2)直接导致)。
你可以说这是Linux内核实现的BUG,但从Linux 2.2+ 开始,它就一直存在,但从未被修复,个人猜测原因有二:
第一,基本不会有“通过检测close()返回值来判断待发送数据是否发送成功”这种需求,检测close()返回值更多的是用来判断当前关闭的描述符是否有效等;
第二,即便判断出数据没有发送成功,此时套接口的相关资源已经释放(当然,也可以实现对资源先不释放,但如果这样完全保留,那么将导致系统不必要的资源浪费),应用程序也无法做出更多补救措施,除了打印一条错误日志以外。更重要的是,实现“判断待发送数据是否成功发送”的需求有更好的不深度依赖Linux内核的应用层实现方式,即shutdown()函数,至于close()函数,做好套接口关闭这一单独的功能就好。
所以,即便Linux内核对启用SO_LINGER选项的套接口调用函数close()的各种情况统一返回0也并无特别严重之处。
转载请保留地址:http://lenky.info/archives/2012/10/07/1963 或 http://lenky.info/?p=1963
备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。
法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以或书面等方式告知,本站将及时删除相关内容或链接。