简体   繁体   English

暂时停止收听套接字

[英]Temporarily stop a socket from listening

I'm writing a server in C++, using the POSIX sockets api. 我正在用C ++编写服务器,使用POSIX套接字api。

This is running as part of a GUI application, which needs to be able to stop and start the server from listening and sending data to clients. 这是作为GUI应用程序的一部分运行的,该应用程序需要能够停止并启动服务器监听和向客户端发送数据。

The main part of the server looks basically like this (i've exluded lots of the code because some of it wasn't relevant to this question.) 服务器的主要部分基本上看起来像这样(我已经排除了很多代码,因为其中一些代码与此问题无关。)

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
        }
    }
}

Or in other words: 或者换句话说:

  1. Make socket listen for connections 让socket监听连接
  2. Setup fds for select() call 为select()调用设置fds
  3. Call select() with stdin, all connect client sockets, and a pipe getting any data sent from the GUI 使用stdin调用select(),所有连接客户端套接字,以及获取从GUI发送的任何数据的管道
  4. If stdin is active, send the typed data to all clients 如果stdin处于活动状态,则将键入的数据发送到所有客户端
  5. If the listener is active, append this new client to the list of clients. 如果侦听器处于活动状态,请将此新客户端附加到客户端列表中。
  6. If the data pipe is active, then either start or stop the server based on the data which it has. 如果数据管道处于活动状态,则根据其拥有的数据启动或停止服务器。
  7. If a client sent data, send that data to all clients, or if they have disconnected, then remove them from the client list. 如果客户端发送数据,将该数据发送到所有客户端,或者如果它们已断开连接,则将其从客户端列表中删除。
  8. Goto 2. 转到2。

My problem 我的问题

I need to be able to make the server basically stop , until I tell it to start again. 我需要能够使服务器基本停止 ,直到我告诉它重新开始。 To clarify, what I mean by stopping is: 澄清一下, 停止的意思是:

  • Not listening for any clients 不听任何客户
  • Not able to receive data from any clients 无法从任何客户端接收数据
  • Any connected clients are disconnected. 任何连接的客户端都已断开

What I've tried 我试过的

My first thought was to keep track of whether it should be running or not with a boolean serving . 我的第一个想法是用布尔serving来跟踪它是否应该运行。

At the beginning of each loop of the while(true), I do this: 在while(true)的每个循环开始时,我这样做:

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

But that didn't work at all. 但那根本不起作用。 It worked so badly that it's hard to even say what it did do instead, so sorry about that. 它的工作非常糟糕,甚至很难说它做了什么,所以很抱歉。

Another thought was to use the close(listener_fd) call, but of course that just makes the fd not useable anymore. 另一个想法是使用close(listener_fd)调用,但当然这只会使fd不再可用。

Another option I considered was, before accepting a connection, first checking if serving was set. 我考虑的另一个选择是,在接受连接之前,首先检查serving已设置。 If it is, then accept the connection. 如果是,则接受连接。 Same goes for receiving and sending data. 接收和发送数据也是如此。 This kind of worked, but didn't inform the clients that I was not serving. 这种方式有效,但没有通知客户我没有服务。

Sockets do not support "temporary" deactivation like what you are asking for. 套接字不支持“临时”停用,就像你要求的那样。 You need to either: 你需要:

  • close() the listening socket, and then create a new listening socket when ready to start again. close()侦听套接字,然后在准备重新启动时创建一个新的侦听套接字。

  • keep the listening socket open and running normally, but immediately close() any new client that is accepted. 保持侦听套接字打开并正常运行,但立即close()任何接受的新客户端。

As for clients that have been accepted, you need to shutdown() + close() them individually, optionally sending them a goodbye message first, depending on whether your protocol allows that. 对于已被接受的客户端,您需要单独shutdown() + close()它们,可选择首先向它们发送一个再见消息,具体取决于您的协议是否允许。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM