下面是我对问题排查的过程,作为个人记录,也供后人查阅
1. TCP_QUIKACK
TCP有个延迟确认机制设置TCP_QUIKACK,每次收到数据后分别测试打开和关闭这个值是否有效果
http://www.360doc.com/content/13/0913/15/13047933_314201280.shtml
问题依旧
2. TCP_CORK和Nagle,采用CORK机制立即发送小包
搜索linux头文件里TCP_QUIKACK的具体位置include/linux/tcp.h,include/netinet/tcp.h两个头文件有定义所有setsockopt支持的选项,网上一个个搜索具体作用。
/* TCP socket options */
#define TCP_NODELAY 1 /* Turn off Nagle's algorithm. */
#define TCP_MAXSEG 2 /* Limit MSS */
#define TCP_CORK 3 /* Never send partially complete segments */
#define TCP_KEEPIDLE 4 /* Start keeplives after this period */ 保活机制
#define TCP_KEEPINTVL 5 /* Interval between keepalives */ 保活机制
#define TCP_KEEPCNT 6 /* Number of keepalives before death */ 保活机制
#define TCP_SYNCNT 7 /* Number of SYN retransmits */
#define TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */
#define TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */
#define TCP_WINDOW_CLAMP 10 /* Bound advertised window */
#define TCP_INFO 11 /* Information about this connection. */
#define TCP_QUICKACK 12 /* Block/reenable quick acks */
#define TCP_CONGESTION 13 /* Congestion control algorithm */
#define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */
#define TCP_COOKIE_TRANSACTIONS 15 /* TCP Cookie Transactions */
#define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/
#define TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */
保活机制我用过
看到有个TCP_CORK,于是百度,后来搜索到一个叫Nagle的算法,默认linux采用该算法,来提高吞吐率,当发送小数据时(几byte),协议栈把他缓存下来,直到达到某些触发条件才发送(比如超过MTU),这种方式对于文件传输服务器特别有效
关于Nagle
http://baike.baidu.com/link?url= ... Ub_Z1-SSJ5BenlVzUsK
TCP_CORK可以说与他对着干,它是个塞子,发送前拧上塞子,把数据存入协议栈,之后打开塞子,塞子里的数据就无论多少一起发送。大概代码
int val;
val = 1;//拧上塞子
setopt(fd, IPPROTO_TCP, TCP_CORK, &val, sizeof(int));
send(...)
val = 0;//打开塞子
setopt(fd, IPPROTO_TCP, TCP_CORK, &val, sizeof(int));
按照上面的代码逻辑,每次发送前后都拧上、打开塞子,希望能强制发送。
测试问题依旧,
自己写另一个测试代码,将所有的数据都放入塞子,
不打开,并且数据只有2byte,但接收方还是能收到,
暂时不去管它。
3. 强制发送1500Byte(MTU)以上数据跳出Nagle机制
如果说数据发送不出去是linux为了提高网络吞吐量,对小数据延迟发送,那么我故意连续发送10次心跳回应,每个心跳回应40byte,client发起心跳间隔6s,所以24S以后必定能操果1500byte,导致服务器强制发送。
测试问题依旧
4. 写个模拟client的应用程序
client应用程序制作两件事情,
发送心跳,
接收服务器的业务查询指令并返回信息
网络拓扑同上,一个server,另个client,其中一个client是真的电路板,另一个是用其他应用程序模拟的。
模拟结果无任何问题。
5. Wireshark抓取与三层交换机端口镜像功能结合抓包
网络拓扑同上,不过这里的client全是真的电路板。
至于什么是端口镜像读者自行百度
当初自掏腰包900多的H3C最便宜的网关交换机,就是为了解决这种问题。
具体的包分析在
http://bbs.csdn.net/topics/391890983依旧说清楚,不再阐述。
其中有一个现象没在CSDN里说,端口镜像我拦截流过 【A端口】 的所有数据,正常情况只有server与其中一台client的数据(
192.168.0.254 <--> 192.168.0.20)但是我居然发现偶尔(大约1%的概率)有另一他client(192.168.0.5)的数据,数据方向是从server到client。当时没在意就过去了。
当初怀疑是交换机的ARP表过期了(理应不应该),又或者交换机故障(公司自己抄别人交换机的电路板,估计芯片设计有缺陷)将数据发错端口。
于是我弄了市场买的交换机TP-Link微调网络拓扑做测试。
问题依旧掉线。
但这一次我也抓到192.168.0.5的流量,
发现192.168.0.5与192.168.0.20的MAC是一样的。
恍然大悟,这样一切都可以解释成功了,所以server不是没有回应client心跳,
而是被转发错了,也导致Wireshark弹出TCP Previous segment not capture(包错位)错误。
26124、27337数据包显示:两者的MAC地址都是
00:80:e1:33:38:33
server将192.168.0.5非法连接复位RST,同时序列号是个随机数,窗口Win、长度Len都设置成0
有趣的是server下查看arp,cat /proc/net/arp看到的确arp是冲突的,但多次执行该命令,
偶尔会不冲突(还好是偶尔不冲突,要是偶尔冲突就麻烦了),现象表明client的MAC地址有跳动的可能,并且跳动导致冲突。
接着将两块client的电路板分别接入电脑查ARP,不错的确冲突,曾经项目刚开始初期我们是对client的芯片STM32 + RT-Thread 读取STM32的某几位96bit世界唯一ID(虽然网上流传该ID是ST公司出厂人为写入的,但我相信出厂已经是唯一的,我自己做产品也验证过),MAC地址是否冲突讨论过的,【当初我也是发现MAC地址在跳动】,后来也是同样的接入电脑查看ARP表,很长一段时间都没冲突。
只能说这次运气好,碰到一个大多数时候(一上电)就冲突的芯片。
以前运气差,MAC地址只是偶尔冲突,找不到证据说服同事检查代码。
现在叫同事
手动写死两套程序分配不同的MAC地址
再次联调一切正常
虽然问题解决了,但我还有疑问:
只有当server以1s为间隔查询client业务时才出现断网,当空闲状态两个client都会发送心跳,为什么不以为MAC冲突导致断开连接?
本帖最后由 lzwml 于 2016-1-14 19:08 编辑