Временно остановить сокет от прослушивания - PullRequest
0 голосов
/ 10 июня 2018

Я пишу сервер на C ++ с использованием API-сокетов POSIX.

Это выполняется как часть приложения с графическим интерфейсом, которое должно иметь возможность останавливать и запускать сервер для прослушивания и отправки данных.для клиентов.

Основная часть сервера выглядит в основном так (я исключил много кода, потому что часть его не относится к этому вопросу.)

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. Установить fds для вызова select ()
  3. Вызов select () с помощью stdin, все клиентские сокеты подключения,и канал, получающий любые данные, отправленные из GUI
  4. Если активен stdin, отправьте введенные данные всем клиентам
  5. Если прослушиватель активен, добавьте этого нового клиента в список клиентов.
  6. Если канал данных активен, то либо запустите, либо остановите сервер на основе имеющихся у него данных.
  7. Если клиент отправил данные, отправьте эти данные всем клиентам или, если ониотключили, затем удалите их изent list.
  8. Перейти к 2.

Моя проблема

Мне нужно, чтобы сервер мог в основном stop , пока я не скажуэто начать снова.Чтобы уточнить, что я имею в виду под остановкой , является:

  • Не прослушивает ни одного клиента
  • Невозможно получить данные от любых клиентов
  • Все подключенные клиенты отключены.

То, что я пробовал

Моей первой мыслью было отследить, должен ли он работать или нет, с логическим значением serving.

В начале каждого цикла while (true) я делаю это:

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

Но это не сработало вообще.Он работал так плохо, что трудно даже сказать, что он сделал вместо этого, так что извините за это.

Другая мысль состояла в том, чтобы использовать вызов close(listener_fd), но, конечно, это простоfd больше не может использоваться.

Другой вариант, который я рассмотрел, перед тем, как принимать соединение, сначала проверял, установлен ли serving.Если это так, то примите соединение.То же самое касается получения и отправки данных.Этот вид работал, но не информировал клиентов о том, что я не обслуживаю.

1 Ответ

0 голосов
/ 10 июня 2018

Сокеты не поддерживают «временную» деактивацию, как то, что вы просите.Вам нужно либо:

  • close() прослушивающий сокет, а затем создать новый прослушивающий сокет, когда будете готовы начать снова.

  • keepпрослушивающий сокет открывается и работает нормально, но немедленно close() любой новый клиент, который принят.

Что касается клиентов, которые были приняты, вам необходимо shutdown() + close()по отдельности, при необходимости сначала отправляя им сообщение до свидания, в зависимости от того, позволяет ли это ваш протокол.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...