Многопоточный 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 работает.

Другие вопросы по тегам