Многопоточный TCP-сервер с сокетами C и pthreads - почему accept() блокирует второй запрос?
Я писал небольшой многопоточный TCP-сервер на C, используя unix-сокеты и pthreads, но у меня возникли проблемы с accept(). Он зависает при втором поступающем запросе и разблокируется только при выходе из предыдущего потока.
Вот как я настроил сокет сервера.
int server_start(server_t *server, int port) {
int fd;
struct sockaddr_in server_addr;
// Socket file descriptor.
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
perror("socket failed");
return 1;
}
// Socket address.
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
// Bind.
if (bind(fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {
perror("bind failed");
return 1;
}
server->fd = fd;
listen(server->fd, server->clients_len);
pthread_create(&(server->thread), NULL, thread_entry_server, server);
return 0;
}
Вот мой код add_client. Это порождает отдельный поток для клиента.
client_t *server_add_client(server_t *server) {
int iter,
fd,
status;
client_t *client;
printf("before\n");
fd = accept(server->fd, NULL, 0);
printf("after\n");
if (fd == -1) {
perror("accept");
return NULL; // Connection failed.
}
// Find an empty spot.
client = server->get_empty_spot();
client->fd = fd;
// Start the new thread.
status = pthread_create(
&(client->thread),
NULL,
thread_entry_client,
client
);
if (status != 0) {
perror("pthread_create");
close(client->fd);
return NULL;
}
client->active = 1;
return client;
}
А вот моя функция входа для клиентского потока:
void *thread_entry_client(void *void_client) {
client_t *client = void_client;
int len;
while (1) {
len = recv(client->fd, client->recv_buffer, RECV_BUFFER_LEN, 0);
if (len < 0) {
perror("recv");
client->active = 0;
close(client->fd);
return NULL;
}
if (len == 0) { // Client disconnected.
client->active = 0;
close(client->fd);
printf("disconnect\n");
return NULL;
}
if (len > 0) {
//printf("%s\n", client->recv_buffer);
printf("msg\n");
}
}
return NULL;
}
Итак, что я делаю, чтобы проверить это - установление двух связей. Первое соединение проходит и работает нормально, а второе - нет, вместо этого поток висит на accept(). Я знаю это по моим printfs (которые я там оставил), и я знаю, что accept () разблокирует ПОСЛЕ первого отключения клиента. Я также знаю, что мой код не закрывает дескриптор файла сокета сервера и не изменяет его.
Любой совет по устранению этого? Я не могу понять это.
РЕДАКТИРОВАТЬ: Вот thread_entry_server.
void *thread_entry_server(void *void_server) {
server_t *server = void_server;
client_t *client;
while (1) {
client = server_add_client(server);
if (client == NULL) // Server is full or connection failed.
continue;
}
return NULL;
}
2 ответа
Это потому что accept()
будет блокироваться (если не настроено иначе), пока клиентское соединение не станет доступным.
Смотрите документацию, в которой упоминается -
Если очередь прослушивания пуста от запросов на соединение, а O_NONBLOCK не задан в файловом дескрипторе для сокета, accept() будет блокироваться, пока не будет установлено соединение. Если очередь listen() пуста от запросов на подключение и в дескрипторе файла для сокета установлено значение O_NONBLOCK, метод accept () завершится с ошибкой и для errno будет задано значение [EAGAIN] или [EWOULDBLOCK].
Также, server->get_empty_spot();
должен всегда возвращать новый client
экземпляр в противном случае это будет означать, что вы передаете тот же client->thread
в pthread_create
,
Я обычно предпочитаю создавать новые темы самостоятельно, что-то вроде -
**pthread_t newListner;**
ThreadArgs thread_args;
thread_args.client_socket = client_socket;
int rc;
if ((rc = pthread_create(&newListner, NULL, startListener, &thread_args)))
{ .. }
Попробуйте.
Я тестировал с помощью javascript websocket, но я не делал никаких рукопожатий, поэтому он не завершал соединение. Тестирование с помощью telnet работает.