2017-02-12 26 views
0

Я писал небольшой многопоточный TCP-сервер в C, используя unix-сокеты и pthreads, но у меня возникают проблемы с accept(). Он зависает во втором запросе, который приходит, и только разблокируется, когда предыдущий поток завершается.Многопоточный TCP-сервер с C-сокетами и 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() разблокирует ПОСЛЕ того, как первый клиент отключится. Я также знаю, что мой код не закрывает дескриптор файла сокета сервера или не меняет его.

Любые советы по отладке? Я не могу понять.

EDIT: Здесь находится 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; 
} 
+0

Не следует 'pthread_create()' pass 'server_add_client' вместо' thread_entry_server'? – alk

+0

Упс, я забыл включить thread_entry_server. Я добавлю. –

+0

accept() не возвращается, пока не будет получено входящее TCP-соединение; что такое connect() для порта, который принимает accept()? –

ответ

0

Это происходит потому, что accept() будет блокировать (если не сконфигурировано иначе) до соединения клиента доступно.

См Documentation, в котором упоминается -

Если очереди ничего запросов на подключение и O_NONBLOCK является не устанавливается на файловый дескриптор сокета, принимает() будет блокировать , пока соединение не присутствует. Если в очереди 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))) 
{ .. } 

Дайте ему попробовать.

+0

Я знаю об этом, и это то поведение, которое я хочу. Проблема в том, что при повторном подключении (при первом открытии первого соединения) accept продолжает блокироваться. Он только разблокируется, когда первый клиент отключается. –

+0

Я должен был быть более ясным в своем посте, обновляя его. –

0

Я тестировал веб-узел javascript, но я не делал никаких попыток установления связи, поэтому он не завершал соединение. Тестирование с помощью telnet.

 Смежные вопросы

  • Нет связанных вопросов^_^