我在线程中使用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.因此,例如,我无法在不重新启动的情况下重新启动发送方线程(按照正确的顺序)。
只要首先启动发件人,一切似乎都能正常工作。
答案 0 :(得分:4)
Run
个帖子只构建read_fds
一次。
select
调用更新read_fds
以清除所有未准备好数据的描述符的所有位,并将其所有位设置为之前设置的数据并准备好数据。< / p>
因此,如果没有描述符准备好任何数据并且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
接口在某些方面更容易使用。)
答案 1 :(得分:0)
你是如何期待它的表现? select()
的要点是睡眠到超时,直到可以读取数据为止;在这种情况下,它将在1秒后超时并返回0.也许你真的不希望在流开始之前超时?