免费注册 查看新帖 |

Chinaunix

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

郁闷的select出错:Bad file descriptor [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-06-14 17:03 |只看该作者 |倒序浏览
第一次select的是server的socket,accept一个连接的fd之后加入select,再select就出错了...

在select之前清空fdset,重新把可用的fd 放入fdset,以及select时maxfd+1等问题我都查过了,仍然找不到出错的原因....

论坛徽章:
0
2 [报告]
发表于 2007-06-14 17:08 |只看该作者

  1. int CMsgServer::Run()
  2. {
  3.     // 清空读fdset
  4.     FD_ZERO(&m_ReadSet);

  5.     // 获取当前的时间
  6.     m_nCurrTime = time(NULL);

  7.     // 查询超时的fd
  8.     CheckTimeoutFd();

  9.     // 轮询fdset
  10.     if (0 != Poll())
  11.     {
  12.         return -1;
  13.     }

  14.     // 处理fdset
  15.     ProcessFdSet();
  16.    
  17.     return 0;
  18. }

  19. void CMsgServer::CheckTimeoutFd()
  20. {
  21.     int nFd;

  22.     m_nMaxFd = m_nListenFd;

  23.     for (m_Iter1 = m_listCon.begin(), m_Iter2 = m_listCon.end(); m_Iter1 != m_Iter2;)
  24.     {
  25.         m_Iter3 = m_Iter1;
  26.         nFd = m_Iter3->nFd;
  27.         ++m_Iter1;
  28.         if (m_nCurrTime - m_Iter3->nStartTime > m_nTimeOutSec)
  29.         {
  30.             // 删除超时连接
  31.         close(nFd);
  32.             m_listCon.erase(m_Iter3);
  33.         }
  34.         else
  35.         {
  36.             // 不超时的fd加入fdset中
  37.             FD_SET(nFd, &m_ReadSet);
  38.             if (nFd > m_nMaxFd)
  39.             {
  40.                 m_nMaxFd = nFd;
  41.                 LogMsg("list size = %d, maxfd changed: %d", m_listCon.size(), m_nMaxFd);
  42.             }
  43.         }
  44.     }
  45. }

  46. int CMsgServer::Poll()
  47. {
  48.     int nFdNum, nEvent, nFd, nRet;

  49.     // 将server listen fd加入fdset中
  50.     FD_SET(m_nListenFd, &m_ReadSet);

  51.     // 轮询fd集合
  52.     while (true)
  53.     {
  54.         if (0 > (nFdNum = select(m_nMaxFd + 1, &m_ReadSet, NULL, NULL, NULL)))
  55.         {
  56.             if (EINTR == errno)
  57.             {
  58.                 continue;
  59.             }
  60.             else
  61.             {
  62.                 LogMsg("[%s] [%s] [%d]: select failed: %s, nListenFd = %d, nMaxFd = %d"
  63.                         , __FILE__, __FUNCTION__, __LINE__
  64.                         , strerror(errno)
  65.                         , m_nListenFd
  66.                         , m_nMaxFd);

  67.                 return -1;
  68.             }
  69.         }
  70.         else
  71.         {
  72.             return 0;
  73.         }
  74.     }
  75. }

  76. void CMsgServer::ProcessFdSet()
  77. {
  78.     int nFd;

  79.     for (m_Iter1 = m_listCon.begin(), m_Iter2 = m_listCon.end(); m_Iter1 != m_Iter2;)
  80.     {
  81.         m_Iter3 = m_Iter1;
  82.         ++m_Iter1;
  83.         nFd = m_Iter3->nFd;
  84.         if (FD_ISSET(nFd, &m_ReadSet))
  85.         {
  86.             // 处理fd
  87.             ProcessFd(nFd);

  88.             //
  89.             LogMsg("ProcessFd");

  90.             // 关闭fd
  91.             close(nFd);

  92.             // 处理完了之后删除fd
  93.             m_listCon.erase(m_Iter3);
  94.         }
  95.     }

  96.     // 如果还可以接收连接 检查监听socket是否可读
  97.     if (m_listCon.size() < m_nMaxCon && FD_ISSET(m_nListenFd, &m_ReadSet))
  98.     {
  99.         Accept();
  100.     }
  101. }

  102. int CMsgServer::Accept()
  103. {
  104.     int nFd;
  105.         //sock_addr tCntAddr;
  106.         //socklen_t nCntAddrLen;

  107.     //nCntAddrLen = sizeof(sock_addr);

  108.     //if (-1 == (nFd = accept(m_nListenFd, (struct sockaddr*)&tCntAddr, &nCntAddrLen)))
  109.     if (-1 == (nFd = accept(m_nListenFd, NULL, NULL)))
  110.     {
  111.         switch (errno) {
  112.             case EAGAIN:
  113. #if EWOULDBLOCK != EAGAIN
  114.             case EWOULDBLOCK:
  115. #endif
  116.             case EINTR:
  117.             case ECONNABORTED:
  118.                 break;
  119.             default:
  120.                 LogMsg("[%s] [%s] [%d]accept failed: %s"
  121.                         , __FILE__, __FUNCTION__, __LINE__
  122.                         , strerror(errno));
  123.         }
  124.         return -1;
  125.     }
  126.     else
  127.     {
  128.         LogMsg("accept fd: %d", nFd);        

  129.         Connection tCon;
  130.         tCon.nFd = nFd;
  131.         tCon.nStartTime = time(NULL);            

  132.         m_listCon.push_back(tCon);
  133.         //m_mapCon[nFd] = tCon;

  134.         // 设置为非阻塞IO
  135.         int nFdFl;
  136.         if (-1 == (nFdFl = fcntl(nFd, F_GETFL, 0)))
  137.         {
  138.             LogMsg("[%s] [%s] [%d]: fcntl(...) error: %s\n"
  139.                     , __FILE__
  140.                     , __FUNCTION__
  141.                     , __LINE__
  142.                     , strerror(errno));
  143.             return -1;
  144.         }
  145.         if (-1 == fcntl(nFd, F_SETFL, nFdFl | O_NONBLOCK))
  146.         {
  147.             LogMsg("[%s] [%s] [%d]: fcntl(...) error: %s\n"
  148.                     , __FILE__
  149.                     , __FUNCTION__
  150.                     , __LINE__
  151.                     , strerror(errno));
  152.             return -1;
  153.         }

  154.         return 0;
  155.     }
  156. }

  157. void CMsgServer::ProcessFd(int nFd)
  158. {
  159.     LogMsg("ProcessFd");

  160.     ssize_t nLen = recv(nFd, m_szRecvBuf, BUFF_SIZE, 0);   

  161.     if (0 < nLen)
  162.     {
  163.         m_szRecvBuf[nLen] = '\0';
  164.     }
  165.     else
  166.     {
  167.         LogMsg("[%s] [%s] [%d]recv failed: %s"
  168.                 , __FILE__, __FUNCTION__, __LINE__
  169.                 , strerror(errno));
  170.         return;
  171.     }

  172.     LogMsg("recv = %s", m_szRecvBuf);

  173.     int nRet = HandleRequest();

  174.     send(nFd, "test", 4, 0);
  175. }
复制代码

论坛徽章:
0
3 [报告]
发表于 2007-06-14 17:09 |只看该作者
server主循环在run函数中,外面调用这个类对象如果在run返回0的情况下就一直循环下去...

论坛徽章:
0
4 [报告]
发表于 2007-06-14 17:55 |只看该作者
难道一个类中不能同时有两个SELECT???

论坛徽章:
0
5 [报告]
发表于 2007-06-14 18:07 |只看该作者
原帖由 converse 于 2007-6-14 17:03 发表
第一次select的是server的socket,accept一个连接的fd之后加入select,再select就出错了...

在select之前清空fdset,重新把可用的fd 放入fdset,以及select时maxfd+1等问题我都查过了,仍然找不到出错的原因....



select 出错了返回什么, errno 是什么呢?

论坛徽章:
0
6 [报告]
发表于 2007-06-14 23:59 |只看该作者
楼主检查一下m_listCon用的stl类型,
如果用vector或deque,
先++m_Iter1,在m_listCon.erase(m_Iter3);后,m_Iter1指向的内容未必有效.

论坛徽章:
0
7 [报告]
发表于 2007-06-15 00:02 |只看该作者
原帖由 飞灰橙 于 2007-6-14 23:59 发表
楼主检查一下m_listCon用的stl类型,
如果用vector或deque,
先++m_Iter1,在m_listCon.erase(m_Iter3);后,m_Iter1指向的内容未必有效.


这个问题有人已经提到过了,确实是问题,但是貌似不是造成这个错误的原因。

我下一个改动要把list变成一般的动态分配的数组来作,方便多了。

论坛徽章:
0
8 [报告]
发表于 2007-06-15 00:05 |只看该作者
原帖由 converse 于 2007-6-15 00:02 发表


这个问题有人已经提到过了,确实是问题,但是貌似不是造成这个错误的原因。

我下一个改动要把list变成一般的动态分配的数组来作,方便多了。


也就是说现在出错误时用的已经是list ?

论坛徽章:
0
9 [报告]
发表于 2007-06-15 08:57 |只看该作者
在select前用一个循环将所有要监视的fd打印出来,还可以fstat查看,就可以找到已经无效的那个。

论坛徽章:
0
10 [报告]
发表于 2007-06-15 10:08 |只看该作者
原帖由 思一克 于 2007-6-15 08:57 发表
在select前用一个循环将所有要监视的fd打印出来,还可以fstat查看,就可以找到已经无效的那个。


在select出错之后用了fstat查询fd,也是没有报错,fstat返回0.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP