socket的keep-alive

系统管理员 2022-05-16 05:24 148阅读 0赞

第一部分

【需求】
不影响服务器处理的前提下,检测客户端程序是否被强制终了。

【现状】
服务器端和客户端的Socket都设定了keepalive属性。
服务器端设定了探测次数等参数,客户端、服务器只是打开了keepalive机能
服务器端起了一个监视线程,利用select来检测socket是否被关闭。

下面这是我的一点肤浅理解。

1. 关于keep alive

  1. 无论windows还是linuxkeepalive就三个参数:
  2. sk->keepalive_probes:探测次数
  3. sk->keepalive_time 探测的超时
  4. sk->keepalive_intvl 探测间隔
  5. 于一个已经建立的tcp连接。如果在keepalive_time时间内双方没有任何的数据包传输,则开启keepalive功能的一端将发送 keepalive数据包,若没有收到应答,则每隔keepalive_intvl时间再发送该数据包,发送keepalive_probes次。一直没有 收到应答,则发送rst包关闭连接。若收到应答,则将计时器清零。例如★:
  6. sk->keepalive_probes = 3;
  7. sk->keepalive_time = 30;
  8. sk->keepalive_intvl = 1;
  9. 思就是说对于tcp连接,如果一直在socket上有数据来往就不会触发keepalive,但是如果30秒一直没有数据往来,则keep alive开始工作:发送探测包,受到响应则认为网络,是好的,结束探测;如果没有相应就每隔1秒发探测包,一共发送3次,3次后仍没有相应,则发送RST包关闭连接,也就是从网络开始到你的socket能够意识到网络异常,最多花33秒。但是如果没有设置keep alive,可能你在你的socket(阻塞性)的上面,接收: recv会一直阻塞不能返回,除非对端主动关闭连接,因为recv不知道socket断了。发送:取决于数据量的大小,只要底层协议栈的buffer能放下你的发送数据,应用程序级别的send就会一直成功返回。 直到buffer满,甚至buffer满了还要阻塞一段时间试图等待buffer空闲。所以你对send的返回值的检查根本检测不到失败。开启了keep alive功能,你直接通过发送接收的函数返回值就可以知道网络是否异常。设置的方法(应用层):
  10. int keepalive = 1; // 开启keepalive属性
  11. int keepidle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测
  12. int keepinterval = 5; // 探测时发包的时间间隔为5 秒
  13. int keepcount = 3; // 探测尝试的次数。如果第1次探测包就收到响应了,则后2次的不再发。
  14. setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive ));
  15. setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle ));
  16. setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval ));
  17. setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount ));

2.select和keep alive的关系

  1. select 是为单个进程使用多个socket而设计的,跟检测连接无关,如果只是检测一个socket的话,没有必要使用select。开了keepalive机能 的话,每次调用recvsend时检查返回值,判断是否出错或为0。如果出错,再检查errno查资料,看哪个或哪几个错误号表示链接断了或不存在就可 以了。
  2. 另外,**谁想定期检查连接状况,谁就启用keep alive**。**另一端可以不起,只是被动地对探测包进行响应**,**这种响应是tcp协议的基本要求,跟keep alive无关。并不需要客户端和服务器端都开启keep alive。**

3.测试结果

  1. 按照例★的值在一端的socket上开启keep alive,然后阻塞在一个recv或者不停的send,这个时候拔了网线,测试从拔掉网线到recv/send返回失败的时间。
  2. linux kernel里头的测试发现,对于阻塞型的socket,当recv的时候,如果没有设置keep alive,即使网线拔掉或者ifdownrecv很长时间不会返回,最长达17分钟,虽然这个时间比linux的默认超时时间()短了很多。**但是如果设置了keep alive,基本都在keepalive\_time +keepalive\_probes\*keepalive\_intvl =33秒内返回错误。**
  3. 但是对于循环不停sendsocket,当拔掉网线后,会持续一段时间send返回成功(0~10秒左右,取决 于发送数据的量),然后send阻塞,因为协议层的buffer满了,在等待buffer空闲,大概90秒左右后才会返回错误。由此看来,send的时候,keep alive似乎没有起到作用,这个原因至今也不清楚。后来通过给send之前设置timer来解决的。

第二部分

  1. 我们知道TCP连接关闭时,需要连接的两端中的某一方发起关闭动作,如果某一方突然断电,另外一端是无法知道的。tcpkeep\_alive就是用以检测异常的一种机制。
  2. 有三个参数:
  • 发送心跳消息的间隔
  • 未收到回复时,重试的时间间隔
  • 重试的次数

  如果是Linux操作系统,这三个值分别为

  1. huangcheng@ubuntu:~$ cat /proc/sys/net/ipv4/tcp_keepalive_time
  2. 7200
  3. huangcheng@ubuntu:~$ cat /proc/sys/net/ipv4/tcp_keepalive_intvl
  4. 75
  5. huangcheng@ubuntu:~$ cat /proc/sys/net/ipv4/tcp_keepalive_probes
  6. 9
  7. 也就意味着每隔7200s(两个小时)发起一次keepalive的报文,如果没有回应,75秒后进行重试,最多重试9次即认为连接关闭。
  8. **这三个选项分别对应TCP\_KEEPIDLETCP\_KEEPINTLTCP\_KEEPCNT的选项值,通过setsockopt进行设置。但是,tcp自己的keepalive有这样的一个bug:**正常情况下,连接的另一端主动调用colse关闭连接,tcp会通知,我们知道了该连接已经关闭。**但是如果tcp连接的另一端突然掉线,或者重启断电,这个时候我们并不知道网络已经关闭。而此时,如果有发送数据失败,tcp会自动进行重传**。重传包的优先级高于keepalive,那就意味着,我们的keepalive总是不能发送出去。 而此时,我们也并不知道该连接已经出错而中断。在较长时间的重传失败之后,我们才会知道。
  9. 为了避免这种情况发生,我们要在tcp上层,自行控制。对于此消息,记录发送时间和收到回应的时间。如果长时间没有回应,就可能是网络中断。如果长时间没有发送,就是说,长时间没有进行通信,可以自行发一个包,用于keepalive,以保持该连接的存在。

发表评论

表情:
评论列表 (有 0 条评论,148人围观)

还没有评论,来说两句吧...

相关阅读

    相关 KeepAlive详解

    > 干货好文,转载保存,以备后看。借此也分享给大家。 目录 什么是KeepAlive? TCP的KeepAlive 1.为什么要有KeepAlive? 2.怎么开启K

    相关 vue keepalive

    keep-alive是vue内置的一个组件,而这个组件的作用就是能够缓存不活动的组件,我们能够知道,一般情况下,组件进行切换的时候,默认会进行销毁,如果有需求,某个组件切换后不

    相关 Keepalive 安装

    keepalived介绍:Keepalived的作用是检测web服务器 的状态,如果有一台web服务器死机,或工作出现故障,Keepalived将检测到,并将有故障的web 服