2019年10月21日星期一

拔掉网线不重连问题

前段时间我接手了一个项目,是一块嵌入式板卡,运行Linux系统,用于在以太网和CAN总线之间相互传输数据。当软件收到CAN总线的数据后,将其按照指定的格式,通过TCP连接发送到Server端,TCP Server端是Windows XPE系统;如果收到TCP Server端下发的数据,则提取出需要的部分,发送到CAN总线上。
之前开发这个软件的同事告诉我有个已知BUG————拔掉网线后不会自动重连。刚开始,极少有人插拔网线,偶尔出现了问题,把我叫过去,我ssh登录到板卡上,看到进程在跑,具体为什么出问题,由于没有日志,也没有打印输出,我也看不出是什么原因,只是在软件中加上了KEEP_ALIVE参数、接收超时等参数。后来他们测试TCP Server端的主备倒换功能,通过拔掉网线来触发主备倒换,这个问题就很明显了。问题上升到比较高的领导那里,需要立即解决。
我在我的Windows笔记本电脑上打开"网络调试助手“软件作TCP Server端,把板卡接上来,第一次拔掉笔记本电脑这端的网线再插上,问题就复现了。我发现在这种情况下,send函数正常返回,因为数据送到了内核;recv函数阻塞在那里,也不会返回。因此无法触发重连机制,就一直停在那里了,要等几十分钟才会超时。给recv加上一个比较短的超时设置可以解决这个问题,但是TCP Server端不会发送心跳,可能很长一段时间都没有数据。这个板卡是用在地铁行业的,夜里停止运营后几个小时都没有数据,难道要反复断开重连吗?还是要求TCP Server端发心跳?
我在笔记本电脑上打开WireShark看了一下,发现重新插回网线后,首先Linux板卡先进行ARP解析,然后发送了一条TCP报文,然后就没有然后了。Windows端没有给任何回复,Linux端也没有重发。我不是TCP/IP协议的专家,无法判断这种情况是否符合协议。
接下来上网搜索,中文领域讲这方面的文章一般都是说设置KEEP_ALIVE参数,这个我早就设了,为什么不管用呢?直到看到这篇文章https://blog.csdn.net/nui111/article/details/41652255
作者也遇到了和我类似的问题,通过设置TCP_USER_TIMEOUT参数解决了问题,在文章中也给出了SO_KEEPALIVE不管用的原因。这里我就不赘述了。
我用这种方法也解决了问题,但是遗憾的是,重新插回网线后,最多需要10秒才能恢复连接,达不到自己设置的超时值那么短。好在这个时间大家已能接受,我由于有其他很多事情加之不熟悉网络编程和TCP/IP协议,也没再深入分析。

没有评论:

发表评论