Я пытаюсь написать простой сервер сокетов, для этого примера это эхо-сервер.Почему `select` продолжает думать, что мне нужно« принять »?
Это socket_server.c (обратите внимание на комментарий о том, где он застревает)
int fdmax, i, socket_descriptor;
fd_set master, read_fds;
struct timeval tv = {.tv_sec = 0, .tv_usec = 1000};
void socket_server_init(void)
{
FD_ZERO(&master);
FD_ZERO(&read_fds);
}
socket_server_socket socket_server_start(char *socket_path)
{
struct sockaddr_un local;
int len;
int socket_descriptor = socket(AF_UNIX, SOCK_STREAM, 0);
if (socket_descriptor == -1)
return (socket_server_socket) {.status = SOCKET_SERVER_START_SOCKET_FAILURE, .descriptor = -1};
local.sun_family = AF_UNIX;
strcpy(local.sun_path, socket_path);
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(socket_descriptor, (struct sockaddr *)&local, len) == -1)
return (socket_server_socket) {.status = SOCKET_SERVER_START_BIND_FAILURE, .descriptor = -1};
if (listen(socket_descriptor, 5) == -1)
return (socket_server_socket) {.status = SOCKET_SERVER_START_LISTEN_FAILURE, .descriptor = -1};
FD_SET(socket_descriptor, &master);
fdmax = socket_descriptor;
return (socket_server_socket) {.status = 0, .descriptor = socket_descriptor};
}
socket_server_socket socket_server_wait_for_connection(socket_server_socket server)
{
read_fds = master;
select(fdmax+1, &read_fds, NULL, NULL, &tv);
for (i = 0; i <= fdmax; i++)
{
if (FD_ISSET(i, &read_fds))
{
if (i == server.descriptor)
{
// It's getting stuck here.
socket_descriptor = accept(server.descriptor, (struct sockaddr *) NULL, NULL);
if (socket_descriptor > fdmax)
fdmax = socket_descriptor;
FD_SET(socket_descriptor, &master);
return (socket_server_socket) {.status = -1, .descriptor = socket_descriptor};
} else {
return (socket_server_socket) {.status = 0, .descriptor = i};
}
}
}
return (socket_server_socket) {.status = -1, .descriptor = -1};
}
int socket_server_update(socket_server_socket client)
{
char buffer[256];
int n = recv(client.descriptor, buffer, 256, 0);
if (n < 0)
return SOCKET_SERVER_UPDATE_RECV_FAILURE;
if (send(client.descriptor, buffer, n, 0) < 0)
return SOCKET_SERVER_UPDATE_SEND_FAILURE;
close(client.descriptor);
return 0;
}
Тогда в моей основной программе:
socket_server_init();
socket_server_socket server = socket_server_start(SOCKET_PATH);
while (1) {
printf("wait for conn\n");
socket_server_socket client = socket_server_wait_for_connection(server);
if (client.status == 0)
{
socket_server_update(client);
}
sleep(1);
printf("%d: Log !\n", (int)time(NULL));
}
Когда я запускаю программу, я наблюдаю:
- «Войдите!» выводится на консоль
- Я подключаюсь к серверу через розетку
- Продолжаю просмотр «Log!». на консоли
- Я послал некоторые данные от клиента
- Клиент видит данные эхо назад
- «Войти!» больше не рассматривается на консоли
- Любые последующие данные от клиента не отражаемый назад
Я ожидаю, что петля продолжать и «Log!» для продолжения вывода, но похоже, что моя программа застревает по вызову accept
, но только во второй раз.
Как я понимаю, select
должен только добавить дескриптор в read_fds
, если его необходимо принять или отобрать. Так что, кажется, происходит это:
select
добавляет дескриптор сервера вread_fds
- Он получает принято
select
снова добавляет дескриптор серверовread_fds
- Там ничто ждет, чтобы быть
accept
ред такaccept
зависает
Я проверял, что оба раза, когда accept
, дескриптор тот же. Поэтому я очень смущен.
Что я делаю неправильно? Я уверен, что он не должен бить accept
второй раз для того же соединения.
Это кажется близким ... Я не могу следовать вашему предложению, потому что это вызывает блокировку, пока он ждет соединения. Я использую тайм-аут, чтобы другие части моей программы могли продолжать работать. Я попробовал 'if (select (fdmax + 1, & read_fds, NULL, NULL, & tv) <0) return;', но потом я попал в ситуацию, когда 'select' просто продолжает возвращать -1 по какой-то причине. –
Имейте в виду, что вы должны иметь недопустимые (закрытые) сокеты 'FD_CLR' из вашего' fd_set'. , Попробуйте следовать примеру Beej для простого чата и сравнить его с вашей программой. Чтобы узнать, почему select возвращает -1, попробуйте напечатать последнюю ошибку с помощью 'perror'. –
@CameronBall, если select возвращает 0, у вас есть тайм-аут. Если он возвращает <0, у вас есть * ошибка *, и вам нужно проверить errno и/или использовать perror или что-то еще в irder, чтобы узнать, что такое ошибка. –