Socket keepalive相关问题
【问题描述】
CTRL-VM为TCP Server(192.168.0.1),BRAS-VM 97为TCP Client1(192.168.0.66),BRAS-VM 99为TCP Client 2(192.168.0.67)。
当BRAS-VM 97和BRAS-VM 99与CTRL-VM分别建立TCP连接后,分别将BRAS-VM 97和BRAS-VM 99关闭电源,从CTRL-VM查看BRAS-VM 97的TCP连接断开,BRAS-VM 99的TCP连接仍然存在,二者现象不一致。
【问题定位】
到宿主机上进行抓包 tcpdump -i vnet5 -w vnet5.pcap
抓包如下
查看30012端口(server和client通信端口)报文,与66的连接是通过保活报文断开的,与67没有。初步怀疑是与66的连接设置了keep-alive,与67的没有设置?
【代码排查】
TCP Server端代码编写错误,将listen的fd设置了keepalive,而不是accept的fd设置keepalive
【疑问1】
现象并不是必现的,原因是什么?
答:TCP不感知链路断开,除非报文发送过程中断连或者正好发生保活探测,或者受到对端FIN报文。也就是说即使没有设置keppalive也有概率会响应断连
【疑问2】
TCP Server端代码是一致的,未区分与client的连接。为什么有的时候是66的连接没断开,有的时候是67的?
答:
1)有一个client能设置keepalive的原因:给listen fd设置Keepalive上,后续accept的fd是可以继承Keepalive属性的
2)有一个client未设置keepalive的原因:第一个accpet的fd是在给listen fd设置Keepalive之前,不会继承Keepalive属性
经验证,若有多个Client,也只会有第一个Client不会设置keepalive属性,其余的Client都可以继承Keepalive属性。
【总结】
查阅了一些资料得知
keepalive设置是socket相关的。
另外这些属性是sockt继承的,即listen的套接字设置该属性后,后面建立连接后的accept 套接字同样继承该属性(心跳属性)。
正常情况下,连接的另一端主动调用colse关闭连接,tcp会通知,我们知道了该连接已经关闭。但是如果tcp连接的另一端突然掉线,或者重启断电,这个时候我们并不知道网络已经关闭。而此时,如果有发送数据失败,tcp会自动进行重传。重传包的优先级高于keepalive,那就意味着,我们的keepalive总是不能发送出去。 而此时,我们也并不知道该连接已经出错而中断。在较长时间的重传失败之后,我们才会知道。所以应用层有时候会使用心跳包。