免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 10501 | 回复: 19
打印 上一主题 下一主题

[网络] 为什么wireshark抓到了client的请求包,但是server不回ACK [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-08-27 18:41 |只看该作者 |倒序浏览
大家好,发现一个很奇怪的问题,经过多次试验和网上搜索都没有找到答案,想让大家帮忙分析一下。
为了简化问题,我写了两个简单程序,Server和Client,Client根据输入参数建立指定数量的连接到Server,然后定时发送一个字节的指令到Server,Server接收指令后不做任何事情。
发现以下情况:
        1. Client建立1个连接到Server,每个连接每隔50ms发送一次指令,运行十几个小时没有发现问题。
        2. Client建立1个连接到Server,每隔约15ms发送一个字节的指令,大概运行一两个小时后就出现问题。
        3. Cleint建立4个或以上的连接,每个连接每隔50ms发送一个字节的指令,有时一两分钟,有时二三十分钟会出现问题,随着连接的数量增加,出现问题的频率也增加。
出现问题后的现象是:在Server端用wireshark能抓到Client发送的指令,但是Server没有回ACK,200ms后Client重传此包,Server这时才回复正常。检查此时收到的包的内容与之前正常情况下数据包的内容没有什么区别。但是运行netstat -s可以看到bad segments received的数量会增加一个。
环境:在Fedora10,CentOS6.3上都试过,换过多台机器,试过两台机网线直连,试过Server和Client运行在同一台机器上。都会出现上述现象。
请各位大佬们帮忙分析一下有什么可能的原因,谢谢。

论坛徽章:
17
处女座
日期:2013-08-27 09:59:352015亚冠之柏太阳神
日期:2015-07-30 10:16:402015亚冠之萨济拖拉机
日期:2015-07-29 18:58:182015年亚洲杯之巴勒斯坦
日期:2015-03-06 17:38:17摩羯座
日期:2014-12-11 21:31:34戌狗
日期:2014-07-20 20:57:32子鼠
日期:2014-05-15 16:25:21亥猪
日期:2014-02-11 17:32:05丑牛
日期:2014-01-20 15:45:51丑牛
日期:2013-10-22 11:12:56双子座
日期:2013-10-18 16:28:17白羊座
日期:2013-10-18 10:50:45
2 [报告]
发表于 2013-08-27 19:07 |只看该作者
回复 1# dyqiu


   设置一下socket选项 启用TCP_NODELAY(这会禁用Nagle)并禁用TCP_CORK(如果本来就没有设置它就不用设了)。你的问题应该是数量太小导致TCP阻塞控制中Nagle算法引起的。

论坛徽章:
0
3 [报告]
发表于 2013-08-27 19:17 |只看该作者
回复 2# myworkstation


    谢谢,Server和Client的NODELAY选项都已经设置了的。
   Server端已经抓到了Client发的数据包,所以应该不是Client的延迟发包导致。
   那会不会是因为Server端延迟ACK导致的呢,我在Server端增加定时50ms发送一个字节到Client的处理,发现在等待的200ms里面Server发送了4个包到Client,但是没有对之前Client的包进行确认,所以应该也不是延迟ACK导致的。
   下面的抓包的记录,其中第一行就是出问题的包172.16.129.3(client)发送到172.16.129.138(server)的包,最后一行是client重传的包:

   

论坛徽章:
0
4 [报告]
发表于 2013-08-27 19:34 |只看该作者
Server端的代码
  1. typedef struct client_t
  2. {
  3.         int             sock;
  4.         int             frameNO;
  5.         struct timeval  lastKeepAlive;
  6.         struct timeval  lasttime;
  7. }Client;

  8. int                     g_listenSock    = 0;
  9. list<Client*>           g_clientList;

  10. void SetFds(fd_set* set)
  11. {
  12.         FD_ZERO(set);
  13.         if(g_listenSock > 0)
  14.         {
  15.                 FD_SET(g_listenSock, set);
  16.         }

  17.         for(list<Client*>::iterator it = g_clientList.begin(); it != g_clientList.end(); it++)
  18.         {
  19.                 Client* cl      = *it;
  20.                 if(cl->sock > 0)
  21.                 {
  22.                         FD_SET(cl->sock, set);
  23.                 }
  24.         }
  25. }

  26. void SetSockOpt(int sock)
  27. {
  28.         Network::SetNonBlock(sock);
  29.         Network::SetKeepLive(sock, 6, 3, 2);
  30.         Network::SetNonDelay(sock);
  31. }

  32. bool ProceessClientCmd(Client* cl)
  33. {
  34.         unsigned char msgType   = 0;
  35.         int ret = Network::Readn(cl->sock, &msgType, 1);
  36.         if(ret != 1)
  37.         {
  38.                 printf("recv failed, sock = %d\n", cl->sock);
  39.                 return false;
  40.         }

  41.         printf("sock = %d, recv request, seq = %d, frame %d\n", cl->sock, msgType, ++cl->frameNO);

  42.         struct timeval  now;
  43.         gettimeofday(&now, NULL);
  44.         if(cl->lasttime.tv_sec  == 0)
  45.         {
  46.                 cl->lasttime    = now;
  47.         }
  48.         else
  49.         {
  50.                 long diff       = (now.tv_sec - cl->lasttime.tv_sec)* 1000000 + now.tv_usec - cl->lasttime.tv_usec;
  51.                 printf("sock = %d, now is %ld:%06d, wait %ld us\n", cl->sock, now.tv_sec, now.tv_usec, diff);
  52.                 if(diff > 200000)
  53.                 {
  54.                         usleep(500000);
  55.                         _exit(0);
  56.                         sleep(900000);  // stop
  57.                 }
  58.                 cl->lasttime    = now;
  59.         }

  60.         return true;
  61. }

  62. int main(int argc, char** argv)
  63. {
  64.         if(argc < 2)
  65.         {
  66.                 return 0;
  67.         }

  68.         g_listenSock    = Network::MakeInetServer(atoi(argv[1]));
  69.         if(g_listenSock < 0)
  70.         {
  71.                 printf("start listen failed\n");
  72.                 return -1;
  73.         }

  74.         while(1)
  75.         {
  76.                 fd_set  rfds;
  77.                 SetFds(&rfds);

  78.                 struct timeval  tv      = {2, 0};
  79.                 int ret = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
  80.                 if(ret > 0)
  81.                 {
  82.                         if(FD_ISSET(g_listenSock, &rfds))
  83.                         {
  84.                                 int sock        = Network::MakeAccept(g_listenSock);
  85.                                 if(sock > 0)
  86.                                 {
  87.                                         Client* cl      = new Client;
  88.                                         cl->frameNO     = 0;
  89.                                         cl->sock        = sock;
  90.                                                                                
  91.                                         SetSockOpt(sock);
  92.                                        
  93.                                                                                 gettimeofday(&(cl->lastKeepAlive), NULL);
  94.                                         cl->lasttime.tv_sec     = 0;
  95.                                         g_clientList.push_back(cl);
  96.                                         printf("new client, sock = %d\n", sock);
  97.                                 }
  98.                                 else
  99.                                 {
  100.                                         printf("make accpet failed\n");
  101.                                 }
  102.                         }

  103.                         for(list<Client*>::iterator it = g_clientList.begin(); it != g_clientList.end(); it++)
  104.                         {
  105.                                 Client* cl      = *it;
  106.                                 if(FD_ISSET(cl->sock, &rfds))
  107.                                 {
  108.                                         if(!ProceessClientCmd(cl))
  109.                                         {
  110.                                                 Network::Close(cl->sock);
  111.                                                 cl->sock = -1;
  112.                                         }
  113.                                 }
  114.                         }

  115.                         for(list<Client*>::iterator it = g_clientList.begin(); it != g_clientList.end();)
  116.                         {
  117.                                 Client* cl      = *it;
  118.                                 if(cl->sock < 0)
  119.                                 {
  120.                                         delete *it;
  121.                                         it = g_clientList.erase(it);
  122.                                 }
  123.                                 else
  124.                                 {
  125.                                         it++;
  126.                                 }
  127.                         }
  128.                 }
  129.         }
  130. }
复制代码

论坛徽章:
0
5 [报告]
发表于 2013-08-28 09:01 |只看该作者
网络读写函数
  1. ssize_t Network::Recvn(int fd, void* vptr, ssize_t n)
  2. {
  3.         ssize_t  nleft;
  4.         ssize_t  nread;
  5.         char        *ptr;
  6.         fd_set  fds;
  7.         struct  timeval tv;

  8.         ptr     = reinterpret_cast<char*>(vptr);
  9.         nleft   = n;
  10.         while(nleft > 0)
  11.         {
  12. #ifdef __linux__
  13.                 nread = read(fd,ptr,nleft);
  14. #elif _WIN32
  15.                 nread = recv(fd,ptr,nleft,0);
  16. #else
  17.                 nread = read(fd,ptr,nleft);
  18. #endif

  19. #ifdef __linux__
  20.                 if(nread < 0 )
  21. #elif _WIN32
  22.                 if(nread == SOCKET_ERROR )
  23. #else
  24.                 if(nread < 0 )
  25. #endif
  26.                 {
  27. #ifdef __linux__
  28.                         if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)
  29.                         {
  30.                                 return RET_ERROR;
  31.                         }
  32. #elif _WIN32
  33.                         int err = GetLastError();
  34.                         if(err != WSAEWOULDBLOCK)
  35.                         {
  36.                                 return RET_ERROR;
  37.                         }
  38. #else
  39.                         if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)
  40.                         {
  41.                                 return RET_ERROR;
  42.                         }
  43. #endif

  44.                         FD_ZERO(&fds);
  45.                         FD_SET(fd, &fds);
  46.                         tv.tv_sec  = 5;
  47.                         tv.tv_usec = 0;
  48.                         int nselect = Select(fd+ 1, &fds, NULL, NULL, &tv);
  49.                         if(nselect <= 0)
  50.                         {
  51.                                 return RET_ERROR;
  52.                         }
  53.                 }
  54.                 else if (nread == 0)
  55.                 {
  56.                         return RET_ERROR;
  57.                 }
  58.                 else
  59.                 {
  60.                         nleft -= nread;
  61.                         ptr          += nread;
  62.                 }
  63.         }
  64.         return (n - nleft);
  65. }


  66. ssize_t        Network::Sendn(int fd, const void* vptr, ssize_t n)
  67. {
  68.         ssize_t     nleft;
  69.         ssize_t     nwritten;
  70.         const char *ptr;

  71.         fd_set fds;
  72.         struct timeval tv;

  73.         ptr     = reinterpret_cast<const char*>(vptr);
  74.         nleft   = n;

  75.         while(nleft > 0)
  76.         {
  77. #ifdef __linux__
  78.                 nwritten = write(fd,ptr,nleft);
  79. #elif _WIN32
  80.                 {
  81.                         nwritten = send(fd,ptr,nleft,0);
  82.                 }
  83. #else
  84.                 nwritten = write(fd,ptr,nleft);
  85. #endif

  86. #ifdef __linux__
  87.                 if(nwritten < 0 )
  88. #elif _WIN32
  89.                 if(nwritten == SOCKET_ERROR )
  90. #else
  91.                 if(nwritten < 0 )
  92. #endif
  93.                 {
  94. #ifdef __linux__
  95.                         if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)
  96.                         {
  97.                                 return RET_ERROR;
  98.                         }
  99. #elif _WIN32
  100.                         int err = GetLastError();
  101.                         if(err != WSAEWOULDBLOCK)
  102.                         {
  103.                                 return RET_ERROR;
  104.                         }
  105. #else
  106.                         if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)
  107.                         {
  108.                                 return RET_ERROR;
  109.                         }
  110. #endif

  111.                         FD_ZERO(&fds);
  112.                         FD_SET(fd, &fds);
  113.                         tv.tv_sec  = 5;
  114.                         tv.tv_usec = 0;
  115.                        
  116.                         int nselect = Select(fd + 1,NULL,&fds,NULL,&tv);
  117.                         if(nselect <= 0)
  118.                         {
  119.                                 return RET_ERROR;
  120.                         }
  121.                 }
  122.                 else if(nwritten == 0)
  123.                 {
  124.                         return RET_ERROR;
  125.                 }
  126.                 else
  127.                 {
  128.                         nleft   -= nwritten;
  129.                         ptr     += nwritten;
  130.                 }
  131.         }
  132.         return (n-nleft);
  133. }
复制代码

论坛徽章:
0
6 [报告]
发表于 2013-08-28 10:09 |只看该作者
本帖最后由 dyqiu 于 2013-08-28 10:33 编辑

下图是相邻两个客户端的请求包,左边是正常的,右边是错误的,没看出来哪里不正常

论坛徽章:
17
处女座
日期:2013-08-27 09:59:352015亚冠之柏太阳神
日期:2015-07-30 10:16:402015亚冠之萨济拖拉机
日期:2015-07-29 18:58:182015年亚洲杯之巴勒斯坦
日期:2015-03-06 17:38:17摩羯座
日期:2014-12-11 21:31:34戌狗
日期:2014-07-20 20:57:32子鼠
日期:2014-05-15 16:25:21亥猪
日期:2014-02-11 17:32:05丑牛
日期:2014-01-20 15:45:51丑牛
日期:2013-10-22 11:12:56双子座
日期:2013-10-18 16:28:17白羊座
日期:2013-10-18 10:50:45
7 [报告]
发表于 2013-08-28 10:37 |只看该作者
回复 3# dyqiu


    看你的记录,我怀疑和这行代码相关“Network::SetKeepLive(sock, 6, 3, 2);”,虽然我不知道里面干了什么,但我想它肯定修改了KEEPALIVE的默认行为,默认是两个小时,这个代码是不是把TCP_KEEPIDLE给改了。

论坛徽章:
0
8 [报告]
发表于 2013-08-28 11:42 |只看该作者
SetKeepLive的代码如下。我刚才试过,把这行代码注释掉,问题仍然存在。
  1. int Network::SetKeepLive(int sockfd,int idle,int intvl,int cnt)
  2. {
  3.         int opt = 1;
  4.         if (setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,reinterpret_cast<char*>(&opt),sizeof(opt)) < 0)
  5.         {
  6.                 return RET_ERROR;
  7.         }

  8. #ifdef __linux__
  9.         if (setsockopt(sockfd,IPPROTO_TCP,TCP_KEEPIDLE, &idle, sizeof(idle)) < 0 ||
  10.             setsockopt(sockfd,IPPROTO_TCP,TCP_KEEPINTVL,&intvl,sizeof(intvl))< 0 ||
  11.             setsockopt(sockfd,IPPROTO_TCP,TCP_KEEPCNT,  &cnt,  sizeof(cnt)) < 0)
  12.         {
  13.                 return RET_ERROR;
  14.         }
  15. #elif _WIN32
  16.         unsigned long dw;
  17.         tcp_keepalive live,liveout;

  18.         live.onoff                                =        1;
  19.         live.keepalivetime                = idle * 1000;       
  20.         live.keepaliveinterval        = intvl * 1000;

  21.         if(WSAIoctl(sockfd,SIO_KEEPALIVE_VALS,&live,sizeof(live),&liveout,sizeof(liveout),&dw,NULL,NULL) < 0)
  22.         {
  23.                 return RET_ERROR;
  24.         }
  25. #else
  26.         if (setsockopt(sockfd,IPPROTO_TCP,TCP_KEEPIDLE, &idle, sizeof(idle)) < 0 ||
  27.             setsockopt(sockfd,IPPROTO_TCP,TCP_KEEPINTVL,&intvl,sizeof(intvl))< 0 ||
  28.             setsockopt(sockfd,IPPROTO_TCP,TCP_KEEPCNT,  &cnt,  sizeof(cnt)) < 0)
  29.         {
  30.                 return RET_ERROR;
  31.         }
  32. #endif

  33.         return RET_SUCCESS;
  34. }
复制代码

论坛徽章:
17
处女座
日期:2013-08-27 09:59:352015亚冠之柏太阳神
日期:2015-07-30 10:16:402015亚冠之萨济拖拉机
日期:2015-07-29 18:58:182015年亚洲杯之巴勒斯坦
日期:2015-03-06 17:38:17摩羯座
日期:2014-12-11 21:31:34戌狗
日期:2014-07-20 20:57:32子鼠
日期:2014-05-15 16:25:21亥猪
日期:2014-02-11 17:32:05丑牛
日期:2014-01-20 15:45:51丑牛
日期:2013-10-22 11:12:56双子座
日期:2013-10-18 16:28:17白羊座
日期:2013-10-18 10:50:45
9 [报告]
发表于 2013-08-28 12:39 |只看该作者
回复 8# dyqiu


    客户端和服务器都注释掉这个功能了吗?

论坛徽章:
0
10 [报告]
发表于 2013-08-28 13:33 |只看该作者
是,都注释了的
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP