2013-11-12 2 views
2

У меня есть приложение для клиентского/серверного приложения Linux. Я придумал странный сценарий, но я не знаю, есть ли какие-либо последствия с этим приложением. У меня есть серверная сторона, которая может принимать N соединений, например, этот сервер будет принимать 100 соединений. В этом сценарии я создаю сокет прослушивания в основном потоке, затем создаю 100 потоков, и каждый поток имеет независимые accept() и select() iomux, также каждый поток может принимать только одно соединение.Может ли дескриптор приложения N принимать соединения, и каждый из них принимает независимый поток

Мое беспокойство здесь, если два одновременных accept() хотят принять один и тот же сокет (соединение) из-за выбора, готов к чтению в одном и том же сокете, я не знаю, является ли одновременное принятие потоком безопасным в ядро, и только один прием может обрабатывать это входящее соединение, а другой будет ждать другого соединения?

Я пробовал это на моей машине RedHat, которая отлично работает, но я не знаю. Если мне повезет, чтобы избежать тупика!

Благодаря

rc = bind(sd, (struct sockaddr_in *)& groupSock, sizeof(struct sockaddr_in)); 
    CHECK_VALUE("Bind address error", rc, 0, goto cleanup); 

    rc = listen(sd, 10); 
    CHECK_VALUE("listen", rc, 0, goto cleanup); 

    for(; count< num_socks; count++){ 

      par_data[count].sd = sd; 
      par_data[count].thread_num = count; 
      par_data[count].err_chk = -1; 

      rc = pthread_create(&thread_id[count], NULL, accept_sock_thread, (void *)& par_data[count]); 
      CHECK_VALUE("pthread_create", rc, 0, goto cleanup); 


    } 

void * accept_sock_thread(void* atr){ 

    int      rc; 
    int      sock   = INVALID_SOCKET; 
    int      datalen   = config.traffic; 
    char     *databuf  = NULL; 
    struct thread_data  *data   = NULL; 
    struct sockaddr_in  tcp_remote; 
    struct timeval   t; 
    socklen_t    size; 
    fd_set     socks; 

    databuf = malloc(sizeof(char) * datalen); 
    memset(databuf, 0, datalen); 

    data = (struct thread_data*) atr; 
    DEBUG(my_debug_flags, ENTER_FUNCT, ("Enter Function accept_sock_thread thread_num %d \n", data->thread_num)); 


    FD_ZERO(&socks); 
    FD_SET(data->sd, &socks); 
    t.tv_sec = 25; 
    t.tv_usec = 0; 
    rc = select(data->sd + 1 , &socks, NULL, NULL,&t); 
    if(rc < 0){ 
      VL_MISC_ERR(("Error in select with Errno: %d", errno)); 
      goto cleanup; 
    } 
    else if(rc == 0){ 
      VL_MISC_ERR(("Accept Select returned a TIEMOUT.")); 
      goto cleanup; 
    } 


    size = sizeof(struct sockaddr_in); 
    sock = accept(data->sd, (struct sockaddr *)& tcp_remote, &size); 
    CHECK_NOT_EQUAL("tcp accept error", sock, INVALID_SOCKET, goto cleanup); 
cleanup: 
    //  sleep(2); /* avoid EOF */ 
    if(sock != INVALID_SOCKET){ 

      rc = close(sock); 
      if(rc != 0){ 
        data->err_chk = -1; 
      } 
    } 
      return NULL; 
} 
+0

Можете ли вы опубликовать код обработки соединения? Сценарий, который вы описываете, может быть хорошим, но он также зависит от реализации. –

+0

sd = socket (AF_INET, SOCK_STREAM, 0); rc = bind (sd, (struct sockaddr_in *) & groupSock, sizeof (struct sockaddr_in)); rc = listen (sd, 100); для (; count

+0

На самом деле это произошло, см. Мой ответ. –

ответ

2

accept() потокобезопасно и возвратного согласно стандарту POSIX.

Это означает, что два вызова accept на одном и том же дескрипторе не должны давать неопределенное поведение. Один из accept откроет сокет, а другой вернет ошибку.

Вы можете увидеть немного больше там:

Is accept() thread-safe? Are BSD/Posix sockets reentrant?

+0

Также см. Это для Apache с использованием мьютекса, чтобы гарантировать, что только один процесс обрабатывает accept(). Не уверен, что это относится и к потокам, а также http://stackoverflow.com/questions/15048701/why-does-apache-mpm-prefork-c-use-a-mutex-to-protect-accept?lq=1 –

+0

Thank you buddy :) –

1

только одна нить будет accept соединение. Ядро обеспечивает это. Так было в мире unix/posix очень долгое время.

+0

Ядро * гарантирует * это. Разве ядро ​​обещает компенсировать любой процесс, для которого это не так? – immibis

0

только одна нить будет accept(), до сих пор так хорошо ... - но перед этим все потоки будут срабатывать иметь select() возвращение, которое не могло бы быть то, что вы хотите.

Так что, если вы имели N нити спать в select() и один соединение приходит в все нити просыпался, но будет нужен только один , так как только один удастся accept() Инг.

+0

yes all selects вернется в готовность к чтению, а затем счастливое согласие получит соединение ... Спасибо –

+0

@GEEKS: С точки зрения производительности я бы счел этот тихий неэффективным. Также см. Этот ответ на вопрос, связанный * Richard Chambers * в его комментарии выше. – alk

0

Использовать accept() на одном и том же объекте сокета из нескольких потоков/процессов является не только стандартной, но и широко используемой стратегией. Из моих знаний apache делает это. nginx делает это тоже, но немного по-другому.

Do Примечание: select() может просыпаться несколько потоков, но только один из них будет принимать соединения, в то время как другие будут либо а) висят в accept() или б) возвращение -1 и установить errno в EAGAIN в случай неблокирующего ввода-вывода. Поскольку вы используете select() перед accept(), я предполагаю, что дескриптор сокета равен в неблокирующем режиме.Таким образом, это может привести к тому, что некоторые потоки никогда не будут обслуживать соединение.

Также я бы посоветовал никогда не закрывать один и тот же сокет в нескольких потоках. Это может привести к очень неприятным и трудным для отладки последствий. Либо используйте обертку, либо shared_ptr, либо назначьте одному из потоков роль «владельца сокета».

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