Select()有时不会等待

Select() sometimes does not wait

本文关键字:等待 Select      更新时间:2023-10-16

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

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程序中,每当我想发送消息时,我都会先发送一个传输请求,然后等待确认(对于确认数据包,我需要再次调用receive)。现在这是不等待的步骤。我的ReceiveAck函数调用Receive(),Receive直接运行在整个代码上。我可以通过创建一个客户端而不创建服务器来测试这一点。如果我在没有服务器的地方发送消息,它应该等待100秒才能得到确认,然后超时。但是,我一按回车键,它就说超时了。

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

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

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

Select在应用程序被信号击中时也会提前返回(即,没有任何具有数据的套接字)。因此,如果你的应用程序在不同的线程中使用了很多usleep()和朋友,你可能会大吃一惊。

select()应始终用于循环中。您必须检查其退货是否存在三种情况:

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

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

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

还要注意,仅仅因为你收到了一个数据报并不意味着你收到了你想要的数据报。你需要一种方法来检查你是否没有得到一些随机数据报,这些数据报恰好在网络上浮动(它确实发生了)。沿着这些线,确保TOTALPACKETSIZE是65536,因为这在理论上(大约)是随机数据包的大小