当套接字关闭时取消阻止recvfrom

发布于 2024-11-15 20:59:28 字数 276 浏览 5 评论 0原文

假设我启动一个线程在端口上接收。套接字调用将在recvfrom 上阻塞。 然后,以某种方式在另一个线程中,我关闭了套接字。

在Windows 上,这将解除recvfrom 的阻塞,并且我的线程执行将终止。

在 Linux 上,这不会阻止接收,因此,我的线程永远什么都不做,并且线程执行不会终止。

谁能帮我解决 Linux 上发生的事情吗?当套接字关闭时,我希望 recvfrom 解除阻止

我继续阅读有关使用 select() 的信息,但我不知道如何在我的具体情况下使用它。

Let's say I start a thread to receive on a port. The socket call will block on recvfrom.
Then, somehow in another thread, I close the socket.

On Windows, this will unblock recvfrom and my thread execution will terminate.

On Linux, this does not unblock recvfrom, and as a result, my thread is sitting doing nothing forever, and the thread execution does not terminate.

Can anyone help me with what's happening on Linux? When the socket is closed, I want recvfrom to unblock

I keep reading about using select(), but I don't know how to use it for my specific case.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

时光无声 2024-11-22 20:59:29

在套接字上调用 shutdown(sock, SHUT_RDWR),然后等待线程退出。 (即pthread_join)。

您可能会认为 close() 会解锁 recvfrom(),但在 Linux 上却不然。

Call shutdown(sock, SHUT_RDWR) on the socket, then wait for the thread to exit. (i.e. pthread_join).

You would think that close() would unblock the recvfrom(), but it doesn't on linux.

蛮可爱 2024-11-22 20:59:29

下面是使用 select() 处理此问题的简单方法的草图:

// Note: untested code, may contain typos or bugs
static volatile bool _threadGoAway = false;

void MyThread(void *)
{
   int fd = (your socket fd);
   while(1)
   {
      struct timeval timeout = {1, 0};  // make select() return once per second

      fd_set readSet;
      FD_ZERO(&readSet);
      FD_SET(fd, &readSet);

      if (select(fd+1, &readSet, NULL, NULL, &timeout) >= 0)
      {
         if (_threadGoAway)
         {
            printf("MyThread:  main thread wants me to scram, bye bye!\n");
            return;
         }
         else if (FD_ISSET(fd, &readSet))
         {
            char buf[1024];
            int numBytes = recvfrom(fd, buf, sizeof(buf), 0);
            [...handle the received bytes here...]
         }
      }
      else perror("select");
   }
}

// To be called by the main thread at shutdown time
void MakeTheReadThreadGoAway()
{
   _threadGoAway = true;
   (void) pthread_join(_thread, NULL);   // may block for up to one second
}

更优雅的方法是避免使用 select 的超时功能,而是创建一个套接字对(使用 socketpair())并让主线程当它希望 I/O 线程离开时,在套接字对的一端发送一个字节;当 I/O 线程在套接字对另一端的套接字上接收到一个字节时,让 I/O 线程退出。不过,我会将其作为练习留给读者。 :)

将套接字设置为非阻塞模式通常也是一个好主意,以避免即使在 select() 指示套接字已准备好之后,recvfrom() 调用也可能阻塞的(小但非零)机会-read,如此处所述。但阻塞模式对于您的目的来说可能“足够好”。

Here's a sketch of a simple way to use select() to deal with this problem:

// Note: untested code, may contain typos or bugs
static volatile bool _threadGoAway = false;

void MyThread(void *)
{
   int fd = (your socket fd);
   while(1)
   {
      struct timeval timeout = {1, 0};  // make select() return once per second

      fd_set readSet;
      FD_ZERO(&readSet);
      FD_SET(fd, &readSet);

      if (select(fd+1, &readSet, NULL, NULL, &timeout) >= 0)
      {
         if (_threadGoAway)
         {
            printf("MyThread:  main thread wants me to scram, bye bye!\n");
            return;
         }
         else if (FD_ISSET(fd, &readSet))
         {
            char buf[1024];
            int numBytes = recvfrom(fd, buf, sizeof(buf), 0);
            [...handle the received bytes here...]
         }
      }
      else perror("select");
   }
}

// To be called by the main thread at shutdown time
void MakeTheReadThreadGoAway()
{
   _threadGoAway = true;
   (void) pthread_join(_thread, NULL);   // may block for up to one second
}

A more elegant method would be to avoid using the timeout feature of select, and instead create a socket pair (using socketpair()) and have the main thread send a byte on its end of the socket pair when it wants the I/O thread to go away, and have the I/O thread exit when it receives a byte on its socket at the other end of the socketpair. I'll leave that as an exercise for the reader though. :)

It's also often a good idea to set the socket to non-blocking mode also, to avoid the (small but non-zero) chance that the recvfrom() call might block even after select() indicated the socket is ready-to-read, as described here. But blocking mode might be "good enough" for your purpose.

固执像三岁 2024-11-22 20:59:29

不是答案,但 Linux close 手册页包含有趣的引用:

关闭文件描述符可能是不明智的
由同一进程中其他线程中的系统调用使用。自从一个文件
描述符可以被重用,有一些模糊的竞争条件
可能会导致意想不到的副作用。

Not an answer, but the Linux close man page contains the interesting quote:

It is probably unwise to close file descriptors while they may be in
use by system calls in other threads in the same process. Since a file
descriptor may be reused, there are some obscure race conditions that
may cause unintended side effects.

◇流星雨 2024-11-22 20:59:29

你正在要求不可能的事情。调用 close 的线程根本不可能知道另一个线程在 recvfrom 中被阻塞。尝试编写保证这种情况发生的代码,你会发现这是不可能的。

无论您做什么,对 close 的调用始终有可能与对 recvfrom 的调用竞争。对close的调用改变了套接字描述符所指的内容,因此它可以改变对recvfrom的调用的语义。

进入recvfrom的线程无法以某种方式向调用close的线程发出信号,表明它已被阻塞(而不是即将阻塞或刚刚进入系统)称呼)。因此,实际上没有可能的方法来确保 closerecvfrom 的行为是可预测的。

请考虑以下情况:

  1. 一个线程即将调用 recvfrom,但它被系统需要执行的其他操作抢占。
  2. 随后,线程调用close
  3. 由系统 I/O 库启动的线程调用 socket 并获取与您关闭相同的描述符。
  4. 最后,线程调用 recvfrom,现在它正在从库打开的套接字接收数据。

哎呀。

永远不要做这样的事,哪怕是远程的事。当另一个线程正在或可能正在使用资源时,不得释放该资源。时期。

You are asking for the impossible. There is simply no possible way for the thread that calls close to know that the other thread is blocked in recvfrom. Try to write code that guarantees that this happens, you will find that it is impossible.

No matter what you do, it will always be possible for the call to close to race with the call to recvfrom. The call to close changes what the socket descriptor refers to, so it can change the semantic meaning of the call to recvfrom.

There is no way for the thread that enters recvfrom to somehow signal to the thread that calls close that it is blocked (as opposed to being about to block or just entering the system call). So there is literally no possible way to ensure the behavior of close and recvfrom are predictable.

Consider the following:

  1. A thread is about to call recvfrom, but it gets pre-empted by other things the system needs to do.
  2. Later, the thread calls close.
  3. A thread started by the system's I/O library calls socket and gets the same decsriptor as the one you closed.
  4. Finally, the thread calls recvfrom, and now it's receiving from the socket the library opened.

Oops.

Don'd ever do anything even remotely like this. A resource must not be released while another thread is, or might be, using it. Period.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文