c++ - 暂时停止监听套接字

标签 c++ c sockets network-programming

我正在用 C++ 编写服务器,使用 POSIX 套接字 API。

这是作为 GUI 应用程序的一部分运行的,它需要能够 停止和启动服务器监听和向客户端发送数据。

服务器的主要部分基本上是这样的(我排除了很多代码,因为其中一些与这个问题无关。)

if (listen(listener_fd, backlog) < 0) {
    std::perror("listen");
    exit(EXIT_FAILURE);
}

while (true) {
    /* This part sets up the FD set */
    FD_ZERO(&read_fds);
    FD_SET(0, &read_fds); // stdin (for server commands)
    FD_SET(listener_fd, &read_fds);
    FD_SET(read_pipe, &read_fds);
    for (auto it = client_socks.begin(); it != client_socks.end(); it++) {
        FD_SET(*it, &read_fds); // listen on each of the open client-server sockets
    }

    max_fd = 0;
    if (client_socks.size() > 0) {
        max_fd = *std::max_element(client_socks.begin(), client_socks.end());
    }
    if (listener_fd > max_fd) max_fd = listener_fd;
    if (read_pipe > max_fd) max_fd = read_pipe;

    std::fill_n(in_buf, IN_BUF_LENGTH, 0);

    // wait for input on stdin or any of the sockets, with a timeout of 20s
    sel_ret = select(max_fd + 1, &read_fds, NULL, NULL, &read_timeout);
    if (sel_ret == -1) {
        std::perror("select");
        exit(EXIT_FAILURE);
    } 
    else if (sel_ret) {
        if (FD_ISSET(0, &read_fds)) { /* stdin */
            // send stdin to all clients
            // excl. for brev.
        } 
        else if (FD_ISSET(listener_fd, &read_fds) && serving) { /* listen(...) can accept, but only bother if the server is listening */
            int newclient_sock = accept(listener_fd, (struct sockaddr*)&addr, &addrlen);
            std::cout << "New client: " << newclient_sock << std::endl;
            client_socks.push_back(newclient_sock);
        }
        else if (FD_ISSET(read_pipe, &read_fds)) { /* someone sent some data down the pipe - probably GUI info, like a button press */
            // here i've got a message from the GUI, which
            // is either starting or stopping the server
            // excluded here for brevity
        }
        else if (serving) { /* one of the sockets, but only if the server is listening */
            // here i've got data from a client socket
            // this handles the message that they send to me
            // again, excluded here for brevity
        }
    }
}

或者换句话说:

  1. 让套接字监听连接
  2. 为 select() 调用设置 fds
  3. 使用标准输入调用 select(),所有连接的客户端套接字,以及获取从 GUI 发送的任何数据的管道
  4. 如果 stdin 处于事件状态,则将键入的数据发送给所有客户端
  5. 如果监听器处于事件状态,则将此新客户端添加到客户端列表中。
  6. 如果数据管道处于事件状态,则根据服务器拥有的数据启动或停止服务器。
  7. 如果客户端发送了数据,则将该数据发送给所有客户端,或者如果它们已断开连接,则将它们从客户端列表中删除。
  8. 转到 2。

我的问题

我需要能够让服务器基本上停止,直到我告诉它重新启动。澄清一下,我所说的停止是指:

  • 不倾听任何客户
  • 无法从任何客户端接收数据
  • 所有连接的客户端都已断开连接。

我尝试过的

我的第一个想法是使用 bool 值 serving 来跟踪它是否应该运行。

在 while(true) 的每个循环开始时,我这样做:

if (serving) {
    // listen(...), same as I did it originally
} else {
    shutdown(listener_fd, SHUT_RD);
}

但这根本不起作用。它的效果非常糟糕,甚至很难说它做了做了什么,对此深表歉意。

另一个想法是使用 close(listener_fd) 调用,当然这只会让 fd 不再可用。

我考虑的另一个选择是,在接受连接之前,首先检查是否设置了 serving。如果是,则接受连接。接收和发送数据也是如此。这种有点有效,但没有告知客户我没有在服务。

最佳答案

套接字不支持您要求的“临时”停用。您需要:

  • close() 监听套接字,然后在准备好再次启动时创建一个新的监听套接字。

  • 保持监听套接字打开并正常运行,但立即close()接受任何新客户端。

对于已经被接受的客户,你需要分别shutdown()+close()他们,可以选择先给他们发送一个再见消息,这取决于你是否协议(protocol)允许这样做。

关于c++ - 暂时停止监听套接字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50786439/

相关文章:

c - c中运算符的赋值和优先级

python-3.x - 强制sock.accept()引发测试异常

c# - NetworkStream.ReadTimeout 在应用夏令时和时钟向后移动时无法阻止 NetworkStream.Read(...)

c++ - MakeFile 使用隐式规则错误

c++ - 将提取的字符存储在字符串 vector 中

java - 如何在jni中创建CBTHook,然后在回调函数中调用java函数?

c# - 异步返回服务器消息到客户端

c++ - 安全地传递动态对象数组,而无需强制复制

c++ - 对某些 opencv 函数的 undefined reference

c++ - 如何在 OS X 中检查用户名/密码组合?