2009-01-19 31 views
8

我正在使用Linux系统(使用2.6.20内核的Ubuntu 7.04服务器)。选择UDP套接字在套接字关闭时不会结束 - 我做错了什么?

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

从另一个线程中,我关闭并关闭了套接字。 如果我在recvfrom中阻止thread1时执行此操作,recvfrom将立即终止。 如果我这样做,而thread1被阻塞在一个超时选择,那么选择不会立即终止,但最终会正常超时。

任何人都可以告诉我为什么选择不会在套接字关闭时退出?这不是例外吗?我可以看到它不可读(显然),但是它是封闭的,这似乎是可以忽略的。

这里的插座的开口(处理去除所有的错误让事情变得简单):

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 
} 

这里的SELECT语句线程1执行:

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); 

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

我真的很想理解这个选择行为背后的想法,因为它似乎与我的期望相反。因此,我显然需要调整我对TCP堆栈如何工作的思考。请给我解释一下。

回答

4

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

+0

这将是一个很好的解决方案。在套接字和管道上都选择等待,另一个线程将写入管道以使选择返回。 – 2009-01-26 19:57:49

4

UDP是无连接协议。由于没有联系,所以没有任何联系可以被破坏,所以消费者不知道生产者永远不会再发送。

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

+1

正确的,真实的和可行的解决办法,但:当我关闭套接字recvfrom的DOES终止。为什么不选择?我真的很困惑,为什么关闭套接字不会产生终止select的异常。 – 2009-01-19 18:29:50

+0

我相信你误解了这个问题:两个线程并没有操纵两个UDP套接字。有**只有一个UDP套接字**。 – curiousguy 2011-10-09 01:24:33

2

我认为最明显的解决方案是关闭不被视为特殊情况。我认为问题的根源在于,你并不真正拥抱select的理念。你为什么在另一个线程中摆弄插座,这听起来像是一场灾难。

+0

我从另一个线程处理套接字的唯一方法就是关闭它,用于停止正在进行的等待。这样我可以让我的程序在选择超时到期之前退出。 – 2009-01-26 18:56:20

2

我会说不同的是,recvfrom的积极尝试从单一的插座,其中选择等待消息的到达,可能在多个句柄读邮件,而不必套接字句柄。

3

你能不发送信号(例如USR2),以这将导致选择()与EINTR返回线程? 然后在信号处理程序中设置一个标志,告诉它不要重新启动select()?

这将消除需要等待多个文件描述符,而且似乎比使用管道将其杀死清洁了许多。

0

您的代码基本上已损坏。这种错误的变化是常见的,过去曾造成严重的错误,并带来巨大的安全隐患。这里是你缺少的:

当你关闭套接字时,根本没有办法知道另一个线程是被阻塞在select还是要阻塞在select。例如,请考虑以下几点:

  1. 线程去调用select,但没有得到调度。
  2. 您关闭套接字。
  3. 在一个线程代码是不知道的(也许是该平台的内部内存管理或记录内部的一部分)库打开插座,并得到相同的标识符,你关闭了套接字。
  4. 线程现在进入select,但它是荷兰国际集团select由库开在插座上。
  5. 灾难降临。

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