tcp短连接TIME_WAIT问题解决方法大全(2)——SO_LINGER

SO_LINGER是一个socket选项,通过setsockopt API进行设置,使用起来比较简单,但其实现机制比较复杂,且字面意思上比较难理解。
解释最清楚的当属《Unix网络编程卷1》中的说明(7.5章节),这里简单摘录:
SO_LINGER的值用如下数据结构表示:
struct linger {
     int l_onoff; /* 0 = off, nozero = on */
     int l_linger; /* linger time */

};


其取值和处理如下:
1、设置 l_onoff为0,则该选项关闭,l_linger的值被忽略,等于内核缺省情况,close调用会立即返回给调用者,如果可能将会传输任何未发送的数据;
2、设置 l_onoff为非0,l_linger为0,则套接口关闭时TCP夭折连接,TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方,
   而不是通常的四分组终止序列,这避免了TIME_WAIT状态;
3、设置 l_onoff 为非0,l_linger为非0,当套接口关闭时内核将拖延一段时间(由l_linger决定)。
   如果套接口缓冲区中仍残留数据,进程将处于睡眠状态,直 到(a)所有数据发送完且被对方确认,之后进行正常的终止序列(描述字访问计数为0)
   或(b)延迟时间到。此种情况下,应用程序检查close的返回值是非常重要的,如果在数据发送完并被确认前时间到,close将返回EWOULDBLOCK错误且套接口发送缓冲区中的任何数据都丢失。
   close的成功返回仅告诉我们发送的数据(和FIN)已由对方TCP确认,它并不能告诉我们对方应用进程是否已读了数据。如果套接口设为非阻塞的,它将不等待close完成。
   
第一种情况其实和不设置没有区别,第二种情况可以用于避免TIME_WAIT状态,但在Linux上测试的时候,并未发现发送了RST选项,而是正常进行了四步关闭流程,
初步推断是“只有在丢弃数据的时候才发送RST”,如果没有丢弃数据,则走正常的关闭流程。
查看Linux源码,确实有这么一段注释和源码:
=====linux-2.6.37 net/ipv4/tcp.c 1915=====
/* As outlined in RFC 2525, section 2.17, we send a RST here because
* data was lost. To witness the awful effects of the old behavior of
* always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
* GET in an FTP client, suspend the process, wait for the client to
* advertise a zero window, then kill -9 the FTP client, wheee...
* Note: timeout is always zero in such a case.
*/
if (data_was_unread) {
/* Unread data was tossed, zap the connection. */
NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, sk->sk_allocation);

另外,从原理上来说,这个选项有一定的危险性,可能导致丢数据,使用的时候要小心一些,但我们在实测libmemcached的过程中,没有发现此类现象,
应该是和libmemcached的通讯协议设置有关,也可能是我们的压力不够大,不会出现这种情况。


第三种情况其实就是第一种和第二种的折中处理,且当socket为非阻塞的场景下是没有作用的。
对于应对短连接导致的大量TIME_WAIT连接问题,个人认为第二种处理是最优的选择,libmemcached就是采用这种方式,
从实测情况来看,打开这个选项后,TIME_WAIT连接数为0,且不受网络组网(例如是否虚拟机等)的影响。
### setsockopt函数的功能与参数详解 `setsockopt()` 函数用于配置套接字的行为,允许应用程序通过指定选项来调整底层协议栈的工作方式。以下是该函数的详细说明及其用法。 #### 函数原型 ```c int setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen); ``` - **s**: 表示要设置选项的目标套接字描述符[^1]。 - **level**: 定义选项的作用范围或层次结构。常见的取值有 `SOL_SOCKET`, `IPPROTO_TCP`, 和 `IPPROTO_IP` 等[^2]。 - **optname**: 指定具体的选项名称,例如 `SO_REUSEADDR`, `TCP_NODELAY` 或者 `SO_LINGER` 等[^3]。 - **optval**: 提供一个指向存储实际数据的缓冲区地址,这些数据定义了所选选项的具体行为。 - **optlen**: 描述由 `optval` 所指向的数据区域大小(通常以字节计数),确保传递给系统的内存块不会越界访问。 #### 使用实例分析 下面给出几个典型的例子展示如何调用此 API 来改变网络通信特性: ##### 1. 启用 Nagle 算法延迟发送小包 当希望减少频繁的小消息传输时可以启用Nagle算法,默认情况下它已经开启;如果关闭则立即发送每一个分组而不等待更多数据积累成较大帧再发出。 ```c #include <stdio.h> #include <string.h> // memset() #include <sys/socket.h> void disable_nagle(int sockfd){ int flag = 1; setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag)); } ``` 此处我们将第三个参数设为了`IPPROTO_TCP`表示我们要修改的是TCP层面上的一些属性而不是更高或者更低层面的东西;第四个参数选择了代表是否激活nodelay模式的一个布尔标志位——即true/false形式表达出来的整数值1/0. ##### 2. 设置超时时间限制读写操作完成期限 有时候我们可能需要限定某个连接上的I/O活动持续多久之后如果没有成功就自动放弃尝试继续下去. ```c struct timeval timeout={5,0}; // five seconds max wait time per call socklen_t len=sizeof(timeout); if(setsockopt(sock,SOL_SOCKET, SO_RCVTIMEO,(const char *)&timeout,len)==-1) { perror("Error setting receive timeout"); exit(EXIT_FAILURE); } // Similar code snippet would apply to sending data with sendto() or write(). ``` 这里展示了怎样利用timeval类型的结构体成员变量sec和usec分别记录秒级单位以及微秒级别的时限长度,并将其赋值到目标socket对象关联起来实现自定义化的收发过程中的最大容忍度控制机制. ##### 3. 配置 Linger 时间处理未决数据 Lingering option controls how the system handles pending data when closing a socket that has unsent information still buffered locally but not yet transmitted over network links. ```c struct linger ling; ling.l_onoff =1 ; /* enable lingering */ ling.l_linger =5 ; /* maximum amount of time(seconds) allowed before discarding remaining bytes */ if(-1==setsockopt(sck,SOL_SOCKET ,SO_LINGER ,(char *)(&ling),sizeof(ling))){ printf("\nFailed To Set Lingering Option On Socket\n"); close(sck); return -1 ; }else { printf("\nsuccessfully applied settings for lingering behavior upon termination.\n");} ``` 在这个片段里边设置了两个字段l_onoff用来开关这个特性而另一个则是具体规定了多少秒钟之内都应该努力把那些残留下来尚未完全送达目的地的内容尽力推送出去直到耗尽设定好的这段时间为止。 ### 注意事项 尽管可以通过上述方法灵活定制各种不同的工作条件但是也应该注意到不当使用某些特定组合可能会带来意想不到的结果甚至破坏整个程序逻辑架构因此建议开发者们务必仔细查阅官方文档了解清楚每种可能性背后隐藏的风险后再做决定实施相应策略.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值