tcp 保活
最近遇到一个tcp保活的问题,发现61850协议server端在网线断掉后,server无法检测到断开,而且更严重的问题是,一些任务还在持续向该tcp发送报文,例如report等,导致众多任务卡住,一些正常的响应都无法进行了。
遇到这个问题问题,首先想到的是这个tcp的socket不是non-block的,所以会阻塞住各个任务,查了一些socket 设置确实没有,于是把non-block设置上,卡住各任务的情况就没有了,但是server端还是检测不到网线的断开。
于是在网上查了很多资料,基本上最终的结论就是tcp keepalive方式+TCP_USER_TIMEOUT选项,该选项在一定内核版本后支持,但是现在一般的设备都是比较新的内核了,肯定是支持的。
之前一直没有想到的是keepalive方式是用在tcp空闲的时候,只有没有数据的时候才会启动keepalive方式,也就是client和server两端,只要有一端发keepalilve,在检测时间内有数据,另外一端实际上是不会发的,在我们这个例子上,是一直在发送report报文的,所以keepalive这种机制不生效
于是研究了TCP_USER_TIMEOUT选项,网上的资料上这个方法应该是生效的,于是看了内核代码,研究了一下TCP的定时器,在内核代码中三个定时器(tcp_keepalive_timer/tcp_retransmit_ timer/tcp_probe_timer)中都有对socket的isck_user_timeout变量的判断,并且在tcp_keepalive_timer函数中即使keepalive到期了,还是要判断isck_user_timeout是否到期,才能生效,所以从代码的角度来说应该是没有问题的,于是我在内核里面也加了打印,发现该走的判断没有走到,所以设置后还是不生效,所以从而怀疑设置错了。
本来认为就是一个socket的选项设置不应该出问题,后来加了打印,发现确实设置错误了,返回了错误码,根据错误码以及kernel的打印,才发现TCP_USER_TIMEOUT这个选项的值被61850的开源代码给重定义了,可能是为了在windows 操作系统使用,到了linux下就不能用了,把重定义去掉就好了,server就能在设置的tcp_user_timeout时间检测到断开了,但是由于tcp定时器机制比较复杂,各种情况下,实际断开的时间和设置的值在某些情况下还是有一些小差异的。哎,大意了。。。。
这么多年开发一直的毛病,重点都是在研究原理,分析代码,但是总是出现一些小错误,导致研究了一大堆,发现是一个小错误导致的,也正是因为这个毛病,导致自己看的代码越来越多,技术也就会越来越好了,原谅自己,保持好心态,才是解决技术问题的关键。