2010-09-15 3 views
3

Я новичок в обеих розетках и резьбе. У меня есть этот код:Розетки и нитки с использованием C

listen(socket_fd, 20); 

/* Looooop */ 
    while (1) { 
    newsocket_fd = accept(socket_fd, 
          (struct sockaddr *) &client_addr, 
          &client_len); 
    if (newsocket_fd < 0) { 
     error("ERROR on accept"); 
    } 
    pthread_t thread; 
    pthread_create(&thread, NULL, run_thread, (void *) newsocket_fd); 
    pthread_join(thread, NULL); 
    } 

Как я могу начать новый поток для каждого нового соединения, а не для каждого запроса? Эти потоки должны запускаться, когда приходит новое соединение, и эти потоки должны ждать запросов, обрабатывать эти запросы и, наконец, возвращаться, когда соединение закрыто. Для каждого соединения должен быть один поток. Вот код для run_thread:

void 
*run_thread(void *ptr) { 
    char buffer[256]; 
    bzero(buffer, 256); 
    int n; 
    n = read((int) ptr, buffer, 255); 
    if (n < 0) error("ERROR Reading from socket"); 
    printf("%s\n\n**********\n\n", buffer); 

    /* Parse buffer and return result */ 
    char *result; 
    { 
    /* First, determine command, 4 characters */ 
    /* (much code) */ 
    } 

    n = write((int) ptr, result, strlen(result)); 
    if (n < 0) error("ERROR Writing to socket"); 
} 

Может кто-нибудь мне помочь? Благодарю.

+1

Использование потоков таким образом не очень хорошо масштабируется. Лучше использовать цикл 'select' для принятых соединений. – bstpierre

+0

Не забудьте закрыть дескриптор файла сокета перед завершением потока. –

ответ

7

Вы почти правильно поняли. Однако проблема заключается в том, что вы присоединяетесь к нити сразу после создания, а pthread_join на самом деле является блокирующим вызовом, который ждет завершения потока. Это означает, что вы не сможете принимать больше соединений, пока работает один поток. Чтобы решить эту проблему, вы можете использовать отдельные потоки. Вам не обязательно присоединяться к отдельным потокам. Для этого вам необходимо создать атрибуты потоков с помощью функции pthread_attr_init и передать эти атрибуты в pthread_create.

Помните, что если у вас слишком много клиентских подключений, у вашего приложения может быть нехватка ресурсов. Итак, в реальном мире вам нужно управлять пулом потоков. Но лучшим сценарием для серверных приложений TCP/IP является использование asynchronous I/O. Я не знаю о C, но в C++ есть очень хорошая библиотека для асинхронного ввода-вывода, называемого boost::asio.

+1

Для C, libev и libevent выглядят очень круто. Я лично одобряю либев. –

0

У Влада есть хороший совет.

Также обратите внимание, что ваша переменная newsocket_fd повторно используется для каждого нового соединения в вашем цикле accept, а затем указатель на нее передается каждому рабочему потоку. Это вызовет проблемы при одновременном подключении нескольких клиентов.

EDIT: Игнорируйте этот комментарий, я неправильно понял ошибку, которую вы делали. Другие дали правильные исправления для вашей обработки newsocket_fd.

+0

Он не передает адрес 'newsocket_fd', он передает значение, а затем переводит arg из' (void *) 'в' int' в funf. – bstpierre

5

Существует также другая критическая ошибка.

Вы вводите int (void *). Это не имеет смысла. Кроме того, вы не можете передать адрес напрямую, поскольку переменная может быть изменена при следующем вызове accept(), прежде чем поток сможет скопировать переменную в свой локальный стек. Один из способов, чтобы написать это было бы что-то вроде этого:

while (1) { 
    newsocket_fd = accept(socket_fd, 
          (struct sockaddr *) &client_addr, 
          &client_len); 
    if (newsocket_fd < 0) { 
     error("ERROR on accept"); 
    } 
    pthread_t thread; 
    int *newsock = malloc(sizeof(int)); 
    *newsock = newsocket_fd; 
    pthread_create(&thread, NULL, run_thread, newsock); 
    pthread_detach(thread); 
    } 

При таком подходе нить убеждается бесплатно() в newsock. Например простой

void *handler(void *thread_data) { 
    int fd = *(int *) thread_data; 
    free(thread_data); 
    .... 
} 

Кроме того, я полагаю, pthread_detach() в порядке, если основная программа не заботится о синхронизации с резьбой позже pthread_join().

+0

Размер указателя всегда равен или больше целого. Таким образом, нет необходимости в динамическом распределении памяти для передачи целого числа. – 2010-09-15 18:25:07

+1

Я все еще чувствую, что это довольно плохой вкус, чтобы отличать целые числа до (void *). Является ли стандарт сказать что-либо о int -> (void *) -> int-конверсии (или аналогичных конверсиях) гарантированно работать? – Maister

+0

Выполнение задания «int» на «void *» и обратно гарантировано. –

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