8

我正在使用 Linux 系统(带有 2.6.20 内核的 Ubuntu 7.04 服务器)。

我有一个程序,它有一个线程(thread1)等待选择一个 UDP 套接字变得可读。我正在使用 select(我的套接字作为单个 readfd 和单个 exceptfd),而不是只调用 recvfrom,因为我想要超时。

从另一个线程,我关闭并关闭了套接字。如果我在 thread1 在 recvfrom 中阻塞时执行此操作,则 recvfrom 将立即终止。如果我在线程 1 在超时的选择中阻塞时执行此操作,则选择不会立即终止,但最终会正确超时。

谁能告诉我为什么一旦套接字关闭,选择就不会退出?那不是例外吗?我可以看到它在哪里不可读(显然),但它已关闭,这似乎是例外。

这是套接字的打开(所有错误处理都被删除以保持简单):

m_sockfd = socket(PF_INET, SOCK_DGRAM, 0);
struct sockaddr_in si_me;
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(port);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(m_sockfd, (struct sockaddr *)(&si_me), sizeof(si_me)) < 0)
{
// deal with error
}

这是 thread1 执行的 select 语句:

struct timeval to;
to.tv_sec = timeout_ms/1000;// just the seconds portion
to.tv_usec = (timeout_ms%1000)*1000;// just the milliseconds 
                                    // converted to microseconds

// watch our one fd for readability or
// exceptions.
fd_set  readfds, exceptfds;
FD_ZERO(&readfds);
FD_SET(m_sockfd, &readfds);
FD_ZERO(&exceptfds);
FD_SET(m_sockfd, &exceptfds);

int nsel = select(m_sockfd+1, &readfds, NULL, &exceptfds, &to);

更新:显然(如下所述),关闭套接字不是例外情况(从选择的角度来看)。我想我需要知道的是:为什么?而且,这是故意的吗?

我真的很想了解这种选择行为背后的想法,因为它似乎与我的期望背道而驰。因此,我显然需要调整我对 TCP 堆栈如何工作的想法。请给我解释一下。

4

6 回答 6

4

UDP 是一种无连接协议。由于没有连接,所以没有一个可以断开,所以消费者不知道生产者永远不会再发送。

您可以让生产者发送“流结束”消息,并让消费者在收到它时终止。

于 2009-01-19T18:25:18.437 回答
4

也许您应该使用其他东西来唤醒选择。也许是管道或类似的东西。

于 2009-01-26T19:54:10.570 回答
3

您能否不向线程发送信号(例如 USR2),从而导致 select() 返回 EINTR?然后在信号处理程序中设置一个标志告诉它不要重新启动 select()?

这将消除等待多个文件描述符的需要,并且似乎比使用管道杀死它要干净得多。

于 2009-07-27T10:15:06.920 回答
2

我认为最明显的解决方案是关闭不被视为例外情况。我认为问题的根源在于,您并没有真正接受select. 你到底为什么要在另一个线程中摆弄套接字,这听起来像是灾难的秘诀。

于 2009-01-22T10:34:04.313 回答
2

我想说不同之处在于 recvfrom 正在积极尝试从单个套接字读取消息,其中 select 正在等待消息到达,可能在多个句柄上,而不一定是套接字句柄。

于 2009-01-26T19:46:37.433 回答
0

您的代码从根本上被破坏了。这个错误的变化很常见,并且在过去导致了严重的错误,具有巨大的安全隐患。这就是你所缺少的:

当您关闭套接字时,您根本无法知道另一个线程是否被阻塞select或即将阻塞select。例如,考虑以下情况:

  1. 线程去 call select,但没有被安排。
  2. 你关闭套接字。
  3. 在线程中,您的代码不知道(可能是平台内部内存管理或日志记录内部结构的一部分)库打开一个套接字并获得与您关闭的套接字相同的标识符。
  4. 线程现在进入select,但它select在库打开的套接字上。
  5. 灾难降临。

当另一个线程正在或可能正在使用它时,您不得尝试释放资源。

于 2017-01-24T18:51:56.400 回答