代码之家  ›  专栏  ›  技术社区  ›  Vi.

如何从另一个线程在UDP套接字上可移植且可靠地解除阻止/中断“recv”?

  •  1
  • Vi.  · 技术社区  · 7 年前

    有一个线程正在运行 recv 在循环中的阻塞UDP套接字上。我们需要停止该线程并关闭该套接字。

    例子:

    #include <unistd.h>
    #include <sys/socket.h>
    
    #include <thread>
    #include <atomic>
    
    std::atomic<int> the_socket;
    
    void threadf() {
        for(;;) {
            char c;
            int ret = recv(the_socket, &c, 1, 0);
            if(ret == 0 || ret == -1) {
                break;
            }
        }
        close(the_socket);
    }
    
    int main() {
        the_socket = socket(AF_INET, SOCK_DGRAM, 0);
        std::thread *t = new std::thread(threadf);
        usleep(200000);
        shutdown(the_socket, SHUT_RDWR); // ENOTCONN
        t->join();
        delete t;
        return 0;
    }
    

    1. 它创建UDP套接字
    2. 它启动另一个线程,同步地从该套接字接收数据包。
    3. delete t 这是个坏主意。 close(the_socket) shutdown 虽然本身失败了,但这项工作:
    [pid 22289] nanosleep({tv_sec=0, tv_nsec=200000000},  <unfinished ...>
    [pid 22290] <... set_robust_list resumed> ) = 0
    [pid 22290] recvfrom(3,  <unfinished ...>
    [pid 22289] <... nanosleep resumed> NULL) = 0
    [pid 22289] shutdown(3, SHUT_RDWR)      = -1 ENOTCONN (Transport endpoint is not connected)
    [pid 22290] <... recvfrom resumed> "", 1, 0, NULL, NULL) = 0
    

    问题

    • 该方案的可移植性和可靠性如何? 不,根本不是便携式的。
    • 是的确切行为 在多线程环境中有什么记载?
    • 是的影响 关机 在某处记录的非连接/UDP套接字上?
    • 可以对套接字执行其他操作来中断吗 在相邻线程上可靠且可移植?

    使现代化 close 然而,在那里工作。。。

    1 回复  |  直到 7 年前
        1
  •  3
  •   Remy Lebeau    7 年前

    假设只有UDP套接字本身,唤醒线程的唯一真正可移植选项是:

    1. 将套接字切换到非阻塞模式,然后让线程使用 select() (e)poll()

    2. sendto() 将专用UDP数据包发送到套接字的侦听端口以唤醒阻塞 recv() / recvfrom() connect() 静态地将UDP套接字与对等IP/端口关联,因为这样做会限制 recv() / 从其他来源接收任何数据包。在您提供的示例中,情况并非如此,但这是您应该注意的。

    在许多平台上,您可以通过 setsockopt(SO_RCVTIMEO) SO_RCVTIMEO .

    选择() recv() 在主插座上,除非实际上有东西可以读取。当你想唤醒线程时,连接/发送到第二个管道/插座。