Я пишу сервер на 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
}
}
}
Или другими словами:
- Заставить сокет прослушивать соединения
- Установить fds для вызова select ()
- Вызов select () с помощью stdin, все клиентские сокеты подключения,и канал, получающий любые данные, отправленные из GUI
- Если активен stdin, отправьте введенные данные всем клиентам
- Если прослушиватель активен, добавьте этого нового клиента в список клиентов.
- Если канал данных активен, то либо запустите, либо остановите сервер на основе имеющихся у него данных.
- Если клиент отправил данные, отправьте эти данные всем клиентам или, если ониотключили, затем удалите их изent list.
- Перейти к 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
.Если это так, то примите соединение.То же самое касается получения и отправки данных.Этот вид работал, но не информировал клиентов о том, что я не обслуживаю.