自动检测SOCKET链接断开
有些网络应用在网线断开后重新连上的情况下 tcp socket 连接保持 ESTABLISH 状态不变
- 1.假如应用程式不使用
tcp
的keepalive
,在网线断开之后,以前建立的socket
链接仍然会保持在ESTABLISH
状态不会改变。实际上tcp
协议对这部分是有所处理的,需要服务端程式,在配置socket
属性时,使用keepalive option
,一 旦有此配置,这些长时间无数据的链接会根据tcp
的keepalive
内核属性,在(tcp_keepalive_time (tcp_keepalive_probes * tcp_keepalive_intvl))
所对应的时间(单位为秒)之后,断开这些链接。
关于keep alive
,无论windows,还是linux,keepalive就三个参数:
1 | sk->keepalive_probes //探测次数 |
对于一个已经建立的tcp连接。如果在keepalive_time
时间内双方没有任何的数据包传输,则开启keepalive
功能的一端将发送keepalive
数据包,若没有收到应答,则每隔keepalive_intvl
时间再发送该数据包,发送keepalive_probes
次。一直没有 收到应答,则发送rst包关闭连接。若收到应答,则将计时器清零。
例如★:
1 | sk->keepalive_probes = 3; |
意思就是说对于tcp连接,如果一直在socket上有数据来往就不会触发keepalive,但是如果30秒
一直没有数据往来,则keep alive开始工作:发送探测包,受到响应则认为网络,是好的,结束探测;如果没有相应就每隔1秒发探测包,一共发送3次,3次后仍没有相应,就关闭连接,也就是从网络开始断到你的socket能够意识到网络异常,最多花33秒
。但是如果没有设置keep alive,可能你在你的socket(阻塞性)
的上面,接收: recv会一直阻塞不能返回,除非对端主动关闭连接,因为recv不知道socket断了。发送:取决于数据量的大小,只要底层协议站的buffer能放下你的发送数据,应用程序级别的send就会一直成功返回。 直到buffer满,甚至buffer满了还要阻塞一段时间试图等待buffer空闲。所以你对send的返回值的检查根本检测不到失败。开启了keep alive功能,你直接通过发送接收的函数返回值就可以知道网络是否异常。
设置的方法(应用层):
1 | int keepalive = 1; // 开启keepalive属性 |
2.select和keep alive的关系
select是为单个线程使用多个socket而设计的,跟检测连接无关,如果只是检测一个socket的话,没有必要使用select。开了keepalive机能的话,每次调用recv或send
时检查返回值
,判断是否出错或为0.如果出错,再检查errno
查资料,看哪个或哪几个错误号表示链接断了或不存在就可以了。
另外,谁想定期检查连接状况,谁就启用keep alive。另一端可以不起,只是被动地对探测包进行响应,这种响应是tcp协议的基本要求,跟keep alive无关。并不需要客户端和服务器端都开启keep alive。3.测试结果
按照例★的值在一端的socket上开启keep alive,然后阻塞在一个recv或者不停的send,这个时候拔了网线,测试从拔掉网线到recv/send返回失败的时间。
在linux kernel
里头的测试发现,对于阻塞型
的socket,当recv的时候,如果没有设置keep alive,即使网线拔掉或者ifdown
,recv很长时间不会返回,最长达17分钟
,虽然这个时间比linux的默认超时时间()短了很多。但是如果 设置了keep alive,基本都在keepalive_time + keepalive_probes * keepalive_intvl
=33秒内返回错误。但是对于循环不停send的socket,当拔掉网线后,会持续一段时间send返 回成功(0~10秒左右,取决 于发送数据的量),然后send
阻塞
,因为协议层的buffer满了,在等待buffer空闲,大概90秒左右后才会返回错误。由此看来,send的时 候,keep alive似乎没有起到作用,这个原因至今也不清楚。后来通过给send之前设置timer来解决的。