Select()有时候不会等待

时间:2012-03-07 04:51:13

标签: c++ sockets udp chat chatroom

我正在写一个聊天程序,我的接收函数有时候根本不会等待。这是接收代码:重要的部分基本上是上半部分,但我已经添加了整个函数以防万一。 (编辑:评论是为了我自己,而不是你们这些人的笔记!对不起!)

ReceiveStatus Server::Receive(PacketInternal*& packetInternalOut)
{
    fd_set fds ;
    int n ;
    struct timeval tv ;

    // Set up the file descriptor set.
    FD_ZERO(&fds) ;
    FD_SET(*p_socket, &fds) ;

    // Set up the struct timeval for the timeout.
    tv.tv_sec = NETWORKTIMEOUTSEC ;
    tv.tv_usec = NETWORKTIMEOUTUSEC ;

    // Wait until timeout or data received.
    n = select ( *p_socket, &fds, NULL, NULL, &tv ) ;
    if ( n == 0)
    { 
        return ReceiveStatus::ReceiveTimeout;
    }
    else if( n == -1 )
    {
        return ReceiveStatus::ReceiveSocketError;   
    }

    //need to make this more flexible so it can support others
    sockaddr_in fromAddr;
    int flags = 0;
    int fromLength = sizeof(fromAddr);

    char dataIn[TOTALPACKETSIZE];
    int bytesIn = recvfrom(*p_socket, dataIn, TOTALPACKETSIZE, flags, (SOCKADDR*)&fromAddr, &fromLength);
    // Convert fromAddr into ip, port
    if(bytesIn == SOCKET_ERROR)
    {
        return ReceiveStatus::ReceiveSocketError;
    }
    if(bytesIn > 0)
    {
        memcpy(packetInternalOut,dataIn,bytesIn);
        return ReceiveStatus::ReceiveSuccessful;
    }
    else
    {
        return ReceiveStatus::ReceiveEmpty;
    }

}

是否有任何可能影响这是否有效的影响?我的聊天程序可以是服务器或客户端。他们都使用相同的代码。等待连接的服务器在Select()上持续100秒,因为NETWORKTIMEOUTSEC = 100.但在char程序中,每当我想发送消息时,我首先发送一个转移请求,然后我等待一个确认(对于确认包,我需要再次呼叫接收)。现在这是不等待的步骤。我的ReceiveAck函数调用Receive(),接收只是直接遍历整个代码。我可以通过创建客户端而不是服务器来测试它。如果我发送没有服务器的消息,它应该等待100秒进行确认,然后超时。但相反,一旦我点击进入,就会说它超时了。

我无法弄清楚是什么让它跳过这一步。我已经在服务器和客户端状态下调试了我的聊天程序。 tv和fds的值在两者中是相同的,但服务器将等待,客户端不会......

3 个答案:

答案 0 :(得分:4)

select()的第一个参数是一个大于的最后一个套接字。所以你需要:

n = select ( *p_socket + 1, &fds, NULL, NULL, &tv ) ;

答案 1 :(得分:0)

当应用程序被信号击中时,选择也会提前返回(即没有任何存在数据的套接字)。因此,如果您的应用程序在不同的主题中使用了很多usleep()和朋友,那么您可能会感到惊讶。

答案 2 :(得分:0)

select()应始终在循环中使用。您必须检查其返回的三个条件:

  • -1(错误),您必须评估以确定它是否致命。 EINTR是一个非致命错误的示例。
  • 一个零,在这种情况下,一些不确定的时间已经过去了,如果你关心它有多长时间,你需要单独检查时间。
  • 正值,在这种情况下,您应检查所有标记的描述符并对其进行操作。

在所有情况下,您应该检查是否存在任何可能使您想要退出循环的其他条件,例如实际传递的时间。

请注意,select()的第一个参数通常应为常量FD_SETSIZE。将它设置为其他任何东西都没有什么好处。

另请注意,仅仅因为您收到了数据报并不意味着您收到了您想要的数据报。您需要一种方法来检查您是否没有得到一些碰巧在网络上漂浮的随机数据报(它发生)。沿着这些方向,确保TOTALPACKETSIZE为65536,因为理论上(大约)随机数据包的大小。