我有两个socket管理对象,一个监听对象[A]用来监听多个客户端的新连接,一个数据处理对象[B]用来管理socket连接。
1.A监听到新的连接,创建socket,将连接socket传递给B,B将新的socket连接放到一个fd_set结构体中。
2.B对象中一个线程循环对fd_set的副本用select函数进行读操作测试,如果fd_set副本中有socket,并且有数据到来,则select肯定会返回大于零的值,然后就对相应的socket进行数据接收处理。
3.有客户端断开连接后,则从fd_set集合中删除对应的socket。
4.问题出现了,在程序正常运行一段时间后,客户端继续发送数据,select函数的判断结果却是没有数据可读,如果此时用telnet连接一下服务器(忘记连接哪个端口了),则B对象“被激活后”继续接收数据,可读取到已经“堆积”的数据。请各位有经验的、有思路的、有创新的兄弟姐妹们,帮忙分析下到底是怎么回事,为什么数据会阻塞住,我已经无奈了。

解决方案 »

  1.   

    “管理对象”是什么意思?是跨进程传递socket吗?
      

  2.   

    4.问题出现了,在程序正常运行一段时间后,客户端继续发送数据,select函数的判断结果却是没有数据可读,如果此时用telnet连接一下服务器(忘记连接哪个端口了),则B对象“被激活后”继续接收数据,可读取到已经“堆积”的数据。
    没有遇到过,可把相关代码贴出来看看。
    //无限期等待套接字满足条件
     int nRet = select(0, &readfd, &writefd, NULL, NULL);
     if (nRet > 0)
     {
     //遍历所有套接字集合
     for (int i = 0; i < allSockfd.fd_count; i++)
     {
     //存在可读的套接字
     if (FD_ISSET(allSockfd.fd_array[i], &readfd))
     {
     //接受客户端连接请求 
     if (allSockfd.fd_array[i] == sListen)//sListen是监听套接字
     {
     SOCKADDR_IN addrClient;
     int nAddrLen = sizeof(addrClient);
     SOCKET sClient = accept(sListen, (sockaddr*)&addrClient, &nAddrLen);
     //新建一个 CClient类实例
     CClient *pClient = new CClient(sClient, pServer);
     //加入客户端管理链表中
     pServer->AddClient(pClient);
     //加入套接字集合
     FD_SET(sClient, &allSockfd);
     //更新界面信息
     pServer->ShowClientNumberInfor(allSockfd.fd_count);  
     }
     else //接收客户端数据
     {
     //得到CClient类的实例
     CClient* pClient = pServer->GetClient(allSockfd.fd_array[i]);
     if (pClient != NULL)
     {
     //接收数据
     BOOL bRet = pClient->RecvData();
     //接收数据错误或者客户端关闭套接字
     if (FALSE == bRet)
     {
     //取出套接字
     SOCKET sTemp = allSockfd.fd_array[i];
     //从集合中删除
     FD_CLR(allSockfd.fd_array[i], &allSockfd);
     //从客户端管理链表中删除该客户端
     pServer->DeleteClient(sTemp);
     //更新界面信息
     pServer->ShowClientNumberInfor(allSockfd.fd_count);
     }  
     }  
     }//else  
     }//if
      

  3.   

    “select函数的判断结果却是没有数据可读, ”
    select返回值==0吗?
    “如果此时用telnet连接一下服务器(忘记连接哪个端口了),则B对象“被激活后”继续接收数据,可读取到已经“堆积”的数据。”
    B对象“被激活后”是什么意思?是不是你的线程在运行一段时间后,哪里堵塞了,所以B对象没有继续接收数据呢?
      

  4.   

    看了半天都不明白 select的用法,,,,汗~~~
      

  5.   

    你不妨判断一下 rRet 的值, 给你一个代码你参考一下:
    // 1)初始化一个套节字集合fdSocket,添加监听套节字句柄到这个集合
    fd_set fdSocket; // 所有可用套节字集合
    FD_ZERO(&fdSocket);
    FD_SET(sListen, &fdSocket);
    while(TRUE)
    {
    // 2)将fdSocket集合的一个拷贝fdRead传递给select函数,
    // 当有事件发生时,select函数移除fdRead集合中没有未决I/O操作的套节字句柄,然后返回。
    fd_set fdRead = fdSocket;
    int nRet = ::select(0, &fdRead, NULL, NULL, NULL);
    if(nRet > 0)
    {
    // 3)通过将原来fdSocket集合与select处理过的fdRead集合比较,
    // 确定都有哪些套节字有未决I/O,并进一步处理这些I/O。
    for(int i=0; i<(int)fdSocket.fd_count; i++)
    {
    if(FD_ISSET(fdSocket.fd_array[i], &fdRead))
    {
    if(fdSocket.fd_array[i] == sListen) // (1)监听套节字接收到新连接
    {
    if(fdSocket.fd_count < FD_SETSIZE)
    {
    sockaddr_in addrRemote;
    int nAddrLen = sizeof(addrRemote);
    SOCKET sNew = ::accept(sListen, (SOCKADDR*)&addrRemote, &nAddrLen);
    FD_SET(sNew, &fdSocket);
    printf("接收到连接(%s)\n", ::inet_ntoa(addrRemote.sin_addr));
    }
    else
    {
    printf(" Too much connections! \n");
    continue;
    }
    }
    else
    {
    char szText[256];
    int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), 0);
    if(nRecv > 0) // (2)可读
    {
    szText[nRecv] = '\0';
    printf("接收到数据:%s \n", szText);
    }
    else // (3)连接关闭、重启或者中断
    {
    ::closesocket(fdSocket.fd_array[i]);
    FD_CLR(fdSocket.fd_array[i], &fdSocket);
    }
    }
    }
    }
    }
    else
    {
    printf(" Failed select() \n");
    break;
    }
    }
      

  6.   

    你的 select 设置的是可写,并不是可读。。
      

  7.   

    不要使用SELECT 性能不好,请使用WSAEventSelect模型开发