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

问题描述 投票:0回答:4

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

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

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

任何人都可以帮我解决 Linux 上发生的事情吗?当套接字关闭时,我想要recvfrom解除阻塞

我一直在阅读有关使用 select() 的内容,但我不知道如何将它用于我的具体情况。

c sockets recv posix-select
4个回答
20
投票

在套接字上调用

shutdown(sock, SHUT_RDWR)
,然后等待线程退出。 (即
pthread_join
)。

您可能会认为

close()
会解锁
recvfrom()
,但在 Linux 上则不然。


6
投票

这是使用 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 线程在套接字对另一端的套接字上接收到字节时退出。不过,我会将其作为练习留给读者。 :)

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


4
投票

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

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


4
投票

你正在要求不可能的事情。调用

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

无论您做什么,对

close
的调用始终可以与对
recvfrom
的调用同时进行。对
close
的调用改变了套接字描述符所指的内容,因此它可以改变对
recvfrom
的调用的语义。

进入

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

考虑以下因素:

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

哎呀。

永远不要做任何像这样远程的事情。当另一个线程正在或可能正在使用某个资源时,不得释放该资源。期间。

© www.soinside.com 2019 - 2024. All rights reserved.