关于TCP连接的Keepalive和reset
先来看一个现象,下面是测试代码:
Server: socat -dd tcp-listen:2000,keepalive,keepidle=10,keepcnt=2,reuseaddr,keepintvl=1 -
Client: socat -dd - tcp:localhost:2000,keepalive,keepidle=10,keepcnt=2,keepintvl=1
Drop Connection (Unplug Cable, Shut down Link(WiFi/Interface)): sudo iptables -A INPUT -p tcp --dport 2000 -j DROP
server监听在2000端口,支持keepalive, client连接上server后每隔10秒发送一个keepalive包,一旦keepalive包得不对对方的响应,每隔1秒继续发送keepalive, 重试两次,如果一直得不到对方的响应那么这个时候client主动发送一个reset包,那么在client这边这个socket就断开了。server上会一直傻傻的等,直到真正要发送数据了才抛异常。
假如client连接层是一个Java应用的连接池,那么这个socket断开后Java能感知吗?
Java对Socket的控制比较弱,比如只能指定是否keepalive,不能用特定的keepalive参数(intvl/cnt等),除非走JNI,不推荐。
如下图(dup ack其实都是keepalive包,这是因为没有抓到握手包导致wireshark识别错误而已)
如上图,client 21512在多次keepalive server都不响应后,发送了reset断开这个连接(server没收到),server以为还连着,这个时候当server正常发数据给client,如果防火墙还在就丢掉,server不停地重传,如果防火墙不在,那么对方os收到这个包后知道21512这个端口对应的连接已经关闭了,再次发送reset给server,这时候server抛异常,中断这个连接。
os层面目前看起来除了用socket去读数据感知到内核已经reset了连接外也没什么好办法检测到。