[求助] linux TCP服务器连接时发不出数据,断开瞬间发送全部数据

lzwml   2016-1-12 19:44 楼主
服务器和两个客户端连接,采用epoll非阻塞方式,客户端分别发送心跳包(6s一次),服务器发送心跳回应,接收在一个线程,人为操作服务器,每隔1S查询一次客户端业务情况,在另一个线程发送,此线程与接收线程未作互斥同步(Socket可能同时被两线程“写”) 所以服务器的两个线程大约每隔6一次数据冲突。(这是诱因抓包发现的确出现有一定概率的数据冲突。 冲突后,接着服务器关闭1S的查询业务,网络上只心跳包和心跳回应包。 代码上看:
  • 每次客户端发心跳包后,服务器返回心跳回应都成功,这两种数据包都不大,都是40byte,不应该导致协议栈满
  • 问题是客户端收不到任何数据(这是我关注的问题)
抓包显示此时只有服务器端的TCP协议栈返回受到数据的ACK应答 ACK应答里并不包含心跳应答数据内容??? 4.JPG 这种包好奇怪,命名负载在TCP头尾部,但wiresharp解析却放在Ethernet头尾部,对于padding第一次听说。 整个包长度60byte,其中6byte全0填在末端,54byte是Ethernet+IP+TCP头
  • 过了1min后客户端断开连接
抓包显示10次供400byte的心跳应答一次性发完 同事对最后一次发送的分析是: TCP断开会有半连接状态,主动断开放问对方是否有数据要发,有就一次性发送。 大致的网络数据流程是这样 unnamed0.jpg 本帖最后由 lzwml 于 2016-1-14 22:20 编辑

回复评论 (4)

问题解决了,参看我在CSDN发的帖子
《linux TCP发送数据卡在协议栈,发不出去》
http://bbs.csdn.net/topics/391890983
点赞  2016-1-13 19:56
下面是我对问题排查的过程,作为个人记录,也供后人查阅
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 reg.JPG server将192.168.0.5非法连接复位RST,同时序列号是个随机数,窗口Win、长度Len都设置成0 REG2.JPG 有趣的是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 编辑
点赞  2016-1-13 20:41
下面用Wireshark的分析Server与Client通信异常前后数据包状况
找到点线索,可能是TCP包错位【没有重传】导致 下面的图片是两组数据包处理重传的情况, 其中第一种处理了重传,另一个没有 54号数据(第14秒):序列号是117,下一个期望序列号(121) 1-1.JPG 66号数据(第22秒):错误(TCP Previous segment not capture)表示这个包(273)之前的包(121)没有收到,序列号错误。 121的数据包没有发送成功(该包可能路由走错了),序列号变成【273】不是(121) 同时67号数据(第22秒),纠正错误(重传TCP Retransmission 1.JPG 这样,通信一切正常 紧接着7S后又出现一次(TCP Previous segment not capture)错误 93号数据(第21秒):序列号421,下一个期望序列号(也是421,因为这个包是个【纯ACK】包) 104号数据(第27秒):(TCP Previous segment not capture)错误,序列号473,不是431 2.JPG 次后从115号数据(第33秒)到185号数据(第69秒)就是该帖子最开始描述的现象,server能收到client的心跳数据(整个帧长94byte,心跳内容40byte),server只返回纯ACK应答(帧长60byte),无心跳应答,持续30秒” 下面是拓扑图 3.JPG 我怀疑上面的两次数据错误是因为交换机数据转发错误, 因为在抓包端【偶尔】能收到192.168.1.254(服务器)发往192.168.0.6(另一台客户端)的流量 那么同样两次错误包也有可能将本该发往192.168.0.20(关注的客户端)的数据发往192.168.0.6(另一台客户端), 【并且】这之后还发生了什么????导致服务器不重发该数据包!!!! 本帖最后由 lzwml 于 2016-1-14 19:07 编辑
点赞  2016-1-14 08:42
支持楼主,感谢分享
点赞  2016-2-27 13:57
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复