c++ - 在 linux 中关闭 udp 套接字后 recv 不返回

标签 c++ linux sockets

我正在尝试使用 udp 协议(protocol)编写客户端服务器应用程序,但我遇到了连接终止的问题。
我打开两个套接字(一个是“服务器”,另一个是“客户端”),当服务器从客户端异步接收时,客户端向他发送一条打印到控制台的简单消息。
经过一段时间的 sleep (以确保服务器将再次调用 recv),客户端和服务器套接字关闭。 此时我预计 recv 将返回 -1 并且异步将结束。
但实际发生的是 recv 永远卡住了*。
如果就在关闭套接字之前我发送了一个空包(代码中的 sendToMakeExit 变量设置为 true),recv 返回那个空包,并且只有在下一次调用之后它才返回 -1,尽管套接字在第一次调用时已关闭。

    const bool sendToMakeExit = false;
    const int port = 2000;
    const auto addr = "127.0.0.1";
    int serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    sockaddr_in target;
    target.sin_family = AF_INET;
    target.sin_port = htons(port);
    inet_pton(AF_INET, addr, &target.sin_addr);
    bind(serverSocket, (sockaddr *) &target, sizeof(target));
    auto readAsync = std::async(std::launch::async, [&serverSocket] {
        const int MAX_READ = 4096;
        char readBuf[MAX_READ];
        ssize_t actualRead;
        do {
            actualRead = recv(serverSocket, readBuf, MAX_READ, 0);
            if (actualRead > 0) {
                cout << readBuf << endl;
            }
        } while (actualRead != -1);
    });
    int clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    connect(clientSocket, (sockaddr *) &target, sizeof(target));
    this_thread::sleep_for(chrono::seconds(1));
    send(clientSocket, "test", 5, 0);
    this_thread::sleep_for(chrono::seconds(1));
    close(clientSocket);
    if (sendToMakeExit) {
        sendto(serverSocket, nullptr, 0, 0, (sockaddr *) &target, sizeof(target));
    }
    close(serverSocket);

*如果我在调试中运行此代码并在 recv 卡住时创建新断点,recv 会意外地返回 -1。
当我关闭套接字时,如何让 recv 返回 -1?

最佳答案

关闭套接字并不能保证仍在使用该套接字的另一个线程中的任何函数调用立即返回。如果您有等待数据传入的调用,例如 recv()select()poll(),您必须发送一些数据到套接字以便这些调用返回。您在代码中执行此操作,但在收到零长度 UDP 数据包时实际上并没有退出:将 while 循环的结尾更改为:

} while (actualRead > 0);

但是,我建议使用一个标志变量来指示线程是否应该继续运行,如下所示:

volatile bool running = true;

auto readAsync = std::async(std::launch::async, [&serverSocket, &running] {
    ...
    while (running) {
        ...recv()...
    }
});

...
running = false;
sendto(serverSocket, ...);
readAsync.wait();
close(serverSocket);

注意我在关闭套接字之前添加了一行等待readAsync完成,以防止任何意外发生:有一个小窗口,其中套接字无效,但是readAsync 可能仍会对其调用 recv()。如果你有更多的线程,也可能发生你在这个线程中关闭套接字,另一个线程打开一个新的套接字并获得与你刚刚关闭的相同的文件描述符编号,然后 readAsync线程会使用错误的套接字。

关于c++ - 在 linux 中关闭 udp 套接字后 recv 不返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52465608/

相关文章:

c++ - 在 .cpp 文件中定义 C++ 命名空间方法的正确方法

linux - 代理背后的基于文本的 FTP 客户端设置

python - Ubuntu 15.04 上 libboost_python-py34 的链接器错误

C++ 问题 : 'break' within nested for loops not working as expected?

java - 独立运行代码和 war 中的代码时的不同结果

linux - 如何同时运行多个 Go lang http 服务器并使用命令行测试它们?

android - 这是什么意思 ls = new LocalServerSocket ("mycomputer")?

即使积压队列已满,客户端也会连接到 TCP 迭代服务器

sockets - UDP Talker在sendto()调用中给出 “Bad value for ai_flags”

c++ - 在 std::thread 中运行时,C++ 成员变量的生命周期是多少?