TCP KEEP-ALIVE和TCP_USER_TIMEOUT机制

本文介绍了在TCP通信中如何使用Keep-Alive机制来判断对端是否存活,并结合TCP_USER_TIMEOUT选项来应对未收到ACK确认的情况。通过设置SO_KEEPALIVE及相关的TCP参数,可以有效检测网络异常和对端掉线。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

正常通信的情况下,send函数发送成功会返回发送数据的字节数。当有错误发生时,send返回-1,全局变量errno被设置。很多情况下,send返回-1是由于连接被对端关闭(对端发送了RST或者FIN包),这种情况errno会被设置为ECONNRESET(Connection reset by peer)。

可是在对端的网线被拔、网卡被卸载或者禁用的时候,对端没有机会向本地操作系统发送TCP RST或者FIN包来关闭连接。这时候操作系统不会认为对端已经挂了。所以在调用send函数的时候,返回的仍然是我们指定发送的数据字节数。当我们无法通过send的返回值来判断对端是否存活的情况下,就要使用TCP Keep-alive机制了。

 

在《Unix网络编程(卷一)》中提到,使用SO_KEEPALIVE套接字选项启用对套接字的保活(Keep-alive)机制。

给一个TCP套接口设置保持存活(keepalive)选项后,如果在2小时内在此套接口的任一方向都没有数据交换,TCP就自动给对方发一个保持存活探测分节(keepalive probe)。

TCP提供了这种机制帮我们判断对端是否存活,如果对端没有对KeepAlive包进行正常的响应,则会导致下一次对套接字的send或者recv出错。应用程序就可以检测到这个异常。

设置KeepAlive机制的选项如下代码所示:

int keep_alive = 1;
int keep_idle = 5, keep_interval = 1, keep_count = 3;
int ret = 0;
 
if (-1 == (ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keep_alive,
    sizeof(keep_alive)))) {
    fprintf(stderr, "[%s %d] set socket to keep alive error: %s", __FILE__,
        __LINE__, ERRSTR);
}
if (-1 == (ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle,
    sizeof(keep_idle)))) {
    fprintf(stderr, "[%s %d] set socket keep alive idle error: %s", __FILE__,
        __LINE__, ERRSTR);
}
if (-1 == (ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &keep_interval,
    sizeof(keep_interval)))) {
    fprintf(stderr, "[%s %d] set socket keep alive interval error: %s", __FILE__,
        __LINE__, ERRSTR);
}
if (-1 == (ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keep_count,
    sizeof(keep_count)))) {
    fprintf(stderr, "[%s %d] set socket keep alive count error: %s", __FILE__,
        __LINE__, ERRSTR);
}
 
  1. 设置SO_KEEPALIVE选项,将这个选项设置为1,代表打开KeepAlive机制。
  2. 设置TCP_KEEPIDLE选项,值为5秒,代表如果TCP连接上有五秒钟没有任何数据包传输,则启动保活机制,发送TCP Keep-alive机制。默认为2小时。
  3. 设置TCP_KEEPINTVL选项,值为1秒,代表如果启动保活机制,则每隔1秒发送一个Keep-alive包。默认为75秒。
  4. 设置TCP_KEEPCNT选项,值为3,代表如果对端对3次Keep-alive数据包都没有正常响应,则判断对端已经崩溃。默认为9。

这样断网的判断问题就解决了。

但是如果发送方发送的数据包没有收到接收方回复的ACK数据包,则TCP Keep-alive机制就不会被启动,而TCP会启动超时重传机制,这样就使得TCP Keep-alive机制在未收到ACK包时失效。在查阅这个问题时找到了stackoverflow上面的资料:http://stackoverflow.com/questions/5907527/application-control-of-tcp-retransmission-on-linux

根据排名第一的回答表示,Linux Kernel 2.6.37中增加了一个叫做TCP_USER_TIMEOUT的socket选项。答案大意是,TCP_USER_TIMEOUT选项是TCP层的socket选项,选项接受unsigned int类型的值。值为数据包被发送后未接收到ACK确认的最大时长,以毫秒为单位,例如设置为10000时,代表如果发送出去的数据包在十秒内未收到ACK确认,则下一次调用send或者recv,则函数会返回-1,errno设置为ETIMEOUT,代表connection timeout。

实现的代码应该如下:

unsigned int timeout = 10000;
if (-1 == setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout))) {
    fprintf(stderror, "set TCP_USER_TIMEOUT option error: %s", strerror(errno));
}

 

如上所述,使用TCP Keep-alive加上TCP_USER_TIMEOUT机制,就可以完美解决通信对端异常断网、掉电的情况下,连接被长期挂起的问题了。

转自http://blog.leeyiw.org/tcp-keep-alive/;

转载于:https://www.cnblogs.com/yanenquan/p/3866503.html

user nginx; worker_processes 1; #error_log /var/log/nginx/error.log notice; events { worker_connections 1024; } http { # Nginx 会根据mime type定义的对应关系来告诉浏览器如何处理服务器传给浏览器的这个文件,是打开还是下载 # 如果Web程序没设置,Nginx也没对应文件的扩展名,就用Nginx 里默认的 default_type定义的处理方式。 # mime type 文件扩展名的对应关系一般放在 mime.types这个文件里,然后用 include mime.types; 来加载 # default_type application/octet-stream; #nginx默认文件类型 include mime.types; default_type application/octet-stream; #tomcat端出现大量TIME_WAIT:(http://lanjingling.github.io/2016/02/27/nginx-tomcat-time-wait/) # Nginx作为反向代理,长连接配置主要有三项, # upstream中的keepalive设置单个worker最大请求数, # 参数proxy_http_version 1.1强制转换为http1.1协议(默认支持长连接), # proxy_set_header Connection将请求头部connection为空(http1.0请求默认connection头部为close)。 # NGINX + TOMCAT出现大量的TIME-WAIT状态的TCP连接解决 # http://nginx.org/en/docs/http/ngx_http_upstream_module.html # For HTTP, the proxy_http_version directive should be set to “1.1” and the “Connection” header field should be cleared: #tomcat端出现大量TIME_WAIT 解决方案 #测试1: # jmeter配置:线程数设置成5,Ramp-up时间(秒)设置成1,循环次数设置成永远 # 在没有配置任何相关的keepalive之前,服务端ngnix到tomcat大约3分钟TCP的TIME_WAIT的总数会增长到一万多; #测试2: # jmeter配置:线程数设置成5,Ramp-up时间(秒)设置成1,循环次数设置成永远 # 配置keepalive后,其中upstream proxy_tomcat中keepalive设置为2048,6分钟服务端ngnix到tomcat的TCP的TIME_WAIT的总数最高1000多; #测试3: # jmeter配置:线程数设置成5,Ramp-up时间(秒)设置成1,循环次数设置成永远 # 配置keepalive后,其中upstream proxy_tomcat中keepalive设置为4092,6分钟服务端ngnix到tomcat的TCP的TIME_WAIT的总数最高800左右; #参考连接: # # http://nginx.org/en/docs/http/ngx_http_upstream_module.html # 1.http://lanjingling.github.io/2016/02/27/nginx-tomcat-time-wait/ # 2.https://blog.youkuaiyun.com/LL845876425/article/details/97621365 # 3.https://blog.youkuaiyun.com/weixin_43944305/article/details/109487968 #参数说明(keepalive_timeout): #用途:保持客户端client(浏览器,需要http客户端打开浏览器keep-alive参数)到nginx的连接是长连接 #配置:设置keep-alive客户端连接在务器端保持开启的超时时间(默认75s);值为0会禁用keep-alive客户端连接 keepalive_timeout 120s; #参数说明(keepalive_requests): #用途:保持客户端client(浏览器,需要http客户端打开浏览器keep-alive参数)到nginx的连接是长连接 #配置: # 设置每个连接的最大请求次数,超过这个次数就会关闭该连接建立新的连接。默认是100.指一个 # keep alive建立之后,nginx就会为这个连接设置一个计数器,记录这个keep alive的长连接上已经 # 接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则nginx会强行关闭这个长连接, # 逼迫客户端不得不重新建立新的长连接。 keepalive_requests 10000; #upstream proxy_nodejs { #用server定义http地址 #server nodejs:9090; #参数说明(keepalive): #用途:保持nginx到server的连接是长连接 #配置:设置 worker 进程后端服务器之间保持空闲连接的最大值,如果空闲连接数大于这个值,将会关闭使用最少的连接,默认值为0 #keepalive 2048; #} # upstream proxyTomcat { #用server定义http地址 #server 192.168.10.105:8087; #参数说明(keepalive): #用途:保持nginx到server的连接是长连接 #配置:设置 worker 进程后端服务器之间保持空闲连接的最大值,如果空闲连接数大于这个值,将会关闭使用最少的连接,默认值为0 #keepalive 4092; #} server { listen 80; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With; add_header Access-Control-Allow-Methods GET,POST,OPTIONS; location /siweidjfa/ { proxy_pass http://$NGINX_TOMCAT_IPADDRESS; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } #location / #{ # #通过代理将请求发送给upstream命名的http服务 # proxy_pass http://$NGINX_TOMCAT_IPADDRESS; # # #参数说明(proxy_http_version): # #用途:保持nginx到server的连接是长连接 # #配置:设置 HTTP 请求协议,要确保是 HTTP 1.1 的长连接协议 # proxy_http_version 1.1; # # #参数说明(proxy_set_header): # #用途:保持nginx到server的连接是长连接 # #配置:清空 Connection 请求头,避免客户端传递短连接的请求头信息 # proxy_set_header Connection ""; # # #参数说明(keepalive_timeout): # #用途:保持nginx到server的连接是长连接 # #配置:设置keep-alive客户端连接在服务器端保持开启的超时时间(默认75s);值为0会禁用keep-alive客户端连接 # keepalive_timeout 150s; # # #参数说明(keepalive_requests): # #用途:保持nginx到server的连接是长连接 # #配置: # # 设置每个连接的最大请求次数,超过这个次数就会关闭该连接建立新的连接。默认是100.指一个 # # keep alive建立之后,nginx就会为这个连接设置一个计数器,记录这个keep alive的长连接上已经 # # 接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则nginx会强行关闭这个长连接, # # 逼迫客户端不得不重新建立新的长连接。 # keepalive_requests 100000; # #} } } 启动报错
最新发布
05-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值