在 Windows 上使用 C++ 中的 Select 函数进行轮询

Posted

技术标签:

【中文标题】在 Windows 上使用 C++ 中的 Select 函数进行轮询【英文标题】:Polling using Select function in C++ on Windows 【发布时间】:2018-08-15 06:51:02 【问题描述】:

我是Socket Programming 的新手。我正在尝试创建一个使用Sockets 进行通信的应用程序。

我对 Receive 函数有疑问,因为有时它只是挂在 recvfrom 函数中。 我正在使用select 函数进行轮询。连接相机时它可以工作,但如果我移除相机,它不会显示错误消息。

我的投票代码:

FD_ZERO(&m_readFds);
FD_SET(Sock, &m_readFds);

m_timeInterval.tv_usec = 30;            //30 Microseconds for Polling
m_socketLength = sizeof(m_cameraInfo);

m_lastBlockId = -1;

while (m_acquiringThreadStatus)

    FD_CLR(Sock, &m_readFds);
    FD_SET(Sock, &m_readFds);

    m_receivingStatus = select(Sock + 1, &m_readFds, NULL, NULL, &m_timeInterval);
    if (m_receivingStatus < 0)
    
        std::cout << "No Data to Receive"<<std::endl;
    
    else
    
        if ((m_receivedBytes = recvfrom(Sock, m_packetBuffer, RECEIVING_BUFFER_SIZE, 0, (struct sockaddr*)&m_cameraInfo, &m_socketLength)) == SOCKET_ERROR)
        
            std::cout << "ERROR" << std::endl;
        
        else
        
            std::cout<<"Data Received"<<std::endl;
        
     

还有一个问题是,当我在某个时间后连续打印Data Received 语句时它会停止。那么如何增加SocketReceiving Buffer的大小。

提前致谢

编辑

SOCKET m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(m_sock == INVALID_SOCKET)

// Error

else

//In the else part bind the socket 

【问题讨论】:

【参考方案1】:

如果您阅读 select() 的文档,您会看到 select() 在错误时返回 -1,在超时时返回 0,在请求事件时返回 > 0。但是,这不是您处理返回值的方式。您将 -1 视为超时,将 >= 0 视为数据事件。因此,当没有可读取的内容时,您最终会调用recvfrom()。如果套接字处于阻塞模式(其默认模式),recvfrom() 将阻塞调用线程,直到数据实际可用。

试试这个:

m_lastBlockId = -1;

while (m_acquiringThreadStatus)

    FD_ZERO(&m_readFds);
    FD_SET(Sock, &m_readFds);

    m_timeInterval.tv_sec = 0;
    m_timeInterval.tv_usec = 30; //30 Microseconds for Polling

    m_receivingStatus = select(Sock + 1, &m_readFds, NULL, NULL, &m_timeInterval);

    if (m_receivingStatus == SOCKET_ERROR)
    
        std::cout << "ERROR" << std::endl;
        break;
    

    if (m_receivingStatus == 0)
    
        std::cout << "No Data to Receive" << std::endl;
        continue;
    

    m_socketLength = sizeof(m_cameraInfo);
    if ((m_receivedBytes = recvfrom(Sock, m_packetBuffer, RECEIVING_BUFFER_SIZE, 0, (struct sockaddr*)&m_cameraInfo, &m_socketLength)) == SOCKET_ERROR)
    
        std::cout << "ERROR" << std::endl;
        break;
    

    std::cout << "Data Received" << std::endl;

关于socket的接收缓冲区大小,可以通过setsockopt()使用SO_RCVBUF选项设置,例如:

int bufsize = ...;
setsockopt(Sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, sizeof(bufsize));

【讨论】:

为什么选择函数是0? @ChandrapalSinghJhala 原始问题中的某些内容让我最初认为代码针对的是 Winsock,它忽略了 select() 的第一个参数。但是再次阅读这个问题,我看不出是什么让我这么想,所以我将 0 替换为 Sock + 1,这在非 Windows 平台上是必需的。 @ChandrapalSinghJhala 唯一可能发生的情况是套接字没有接收到任何数据。 FWIW,30 微秒是用于套接字的非常短的超时。老实说,在那么小的时候,我会摆脱select() 并让recvfrom() 阻塞直到数据到达。可选地使用SO_RCVTIMEO 来超时阻塞读取。如果您要使用select(),通常首选更大的超时时间(而不是 的数量级)并在没有接收到数据时中断循环,因此您不会挂起您的无限期应用 Wireshark 显示相机正在发送数据包。我正在使用 57181 端口。首先我认为这是因为防火墙,但我关闭了防火墙,但即使我将 m_timeInterval.tv_sec = 0 替换为 1,我仍然会超时 @ChandrapalSinghJhala 那么您可能没有正确设置套接字。请在进入select 循环之前显示创建套接字的实际代码。

以上是关于在 Windows 上使用 C++ 中的 Select 函数进行轮询的主要内容,如果未能解决你的问题,请参考以下文章

在 Windows 上使用 C++ 中的 Select 函数进行轮询

Windows 上 C++ Mingw 中的崩溃报告

Windows 上 C++ 中的 libxl 库设置

Windows 上 C++ 中的位图操作

“Windows 无法访问指定的设备.....” C++ 中的错误

在 C++ 和 Windows 中使用 XmlRpc