在 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 函数进行轮询