我在线程中使用 select() 来监视数据报套接字,但除非在线程启动之前将数据发送到套接字,否则 select() 将继续返回 0。
我混合了一点 C 和 C++;这是启动线程的方法:
bool RelayStart() {
sock_recv = socket(AF_INET, SOCK_DGRAM, 0);
memset(&addr_recv, 0, sizeof(addr_recv));
addr_recv.sin_family = AF_INET;
addr_recv.sin_port = htons(18902);
addr_recv.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sock_recv, (struct sockaddr*) &addr_recv, sizeof(addr_recv));
isRelayingPackets = true;
NSS::Thread::start(VIDEO_SEND_THREAD_ID);
return true;
}
停止线程的方法:
bool RelayStop() {
isSendingVideo = false;
NSS::Thread::stop();
close(sock_recv);
return true;
}
并且方法在线程中运行:
void Run() {
fd_set read_fds;
int select_return;
struct timeval select_timeout;
FD_ZERO(&read_fds);
FD_SET(sock_recv, &read_fds);
while (isRelayingPackets) {
select_timeout.tv_sec = 1;
select_timeout.tv_usec = 0;
select_return = select(sock_recv + 1, &read_fds, NULL, NULL, &select_timeout);
if (select_return > 0 && FD_ISSET(sock_recv, &read_fds)) {
// ...
}
}
}
问题是,如果在调用 RelayStart() 之前没有进程已经将 UDP 数据包发送到端口 18902,则 select() 将始终返回 0。因此,例如,我无法在不重新启动的情况下重新启动发送方线程(以正确的顺序。)
只要首先启动发送方,一切似乎都可以正常工作。
最佳答案
Run
线程只构造一次read_fds
。
select
调用更新 read_fds
以清除所有未准备好数据的描述符的所有位,并为之前和之前设置的描述符设置所有位准备好数据。
因此,如果没有描述符准备好任何数据并且 select
调用超时(并返回 0),则现在清除 read_fds
中的所有位。传递相同的全零位掩码的进一步调用将不会扫描任何文件描述符。
您可以在循环内的每次行程中重新构建读取集:
while (isRelayingPackets) {
FD_ZERO(&read_fds);
FD_SET(sock_recv, &read_fds);
...
}
或者使用带有位集拷贝的辅助变量:
while (isRelayingPackets) {
fd_set select_arg = read_fds;
... same as before but use &select_arg ...
}
(或者,当然,还有非select
接口(interface)在某些方面更容易使用。)
关于c++ - 如果数据已经发送,为什么 select 只将文件描述符显示为就绪?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10254397/