当前位置: 首页 > 知识库问答 >
问题:

暂时停止套接字监听

商皓
2023-03-14

我正在用C语言编写一个服务器,使用POSIX sockets 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. 使用stdin调用select(),所有连接客户端套接字,以及获取从GUI发送的任何数据的管道
  4. 如果stdin处于活动状态,则将键入的数据发送给所有客户端
  5. 如果侦听器处于活动状态,请将此新客户端附加到客户端列表中
  6. 如果数据管道处于活动状态,则根据其拥有的数据启动或停止服务器
  7. 如果客户机发送了数据,请将该数据发送给所有客户机,或者如果客户机已断开连接,则将其从客户机列表中删除
  8. 转到2

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

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

我的第一个想法是使用布尔服务来跟踪它是否应该运行。

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

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

但那根本不起作用。它效果如此之差,以至于很难说它到底做了什么,对此我很抱歉。

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

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


共有1个答案

张建树
2023-03-14

套接字不支持像您所要求的那样的“临时”停用。你需要:

>

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

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

    对于已被接受的客户端,您需要分别shutdown()close()向它们发送告别消息,这取决于您的协议是否允许。

  •  类似资料:
    • 1.若要暂停训练,请按下该按钮。显示暂停。若要继续训练,请点击绿色箭头图标。 2.若要停止训练,在记录训练期间或处于暂停模式时长按该按钮三秒钟,直至计数器清零。或者您可以点击并按住显示屏上的红色停止按钮。 如果在暂停后停止训练,则暂停后经过的时间不包括在总训练时间内。

    • 若要暂停训练 长按正面按钮 或 在训练视图中向右滑动,找出并轻触暂停图标。 通过向左滑动您可以在暂停模式中看到训练总结。 恢复暂停的训练 轻触绿色箭头图标。 停止训练 在训练期间或在暂停模式中,长按正面按钮直至绿色计时器一直倒数。 或 在暂停模式中,轻触并按住红色停止图标可结束记录。

    • 1.若要暂停训练,请按“返回”按钮。显示“记录已暂停”。若要继续训练,请按“开始”。 2.若要停止训练,在训练记录或处于暂停模式时长按“返回”按钮三秒钟,直到显示“记录已结束”。 如果在暂停后停止训练,则暂停后经过的时间不包括在总训练时间内。

    • 按下“返回”,暂停您的训练课。 要继续训练,请按“确定”。要停止训练课,长按 BACK(返回)。停止训练后,会立即从手表上获得训练总结。 如果在暂停后停止训练课,则暂停后经过的时间不包括在总训练时间内。

    • 按下“返回”,暂停您的训练课。 要继续训练,请按“确定”。要停止训练课,长按 BACK(返回)。停止训练后,会立即从手表上获得训练总结。 如果在暂停后停止训练课,则暂停后经过的时间不包括在总训练时间内。