2011-12-22 2 views
1

Я создаю серверный демон в c, который принимает многочисленные одновременные соединения, и клиенты будут отправлять данные на сервер. В настоящее время у каждого клиентского соединения создается новый поток. Я вижу, что accept() иногда (не всегда) возвращает идентификатор существующего соединения, которое (очевидно) вызывает множество проблем, включая ошибки сегментации.accept возвращает существующее соединение, вызывая ошибку seg

Я даже отключил опцию сокета SO_REUSEADDR, чтобы убедиться, что это не так. Всякий раз, когда один клиент совершает многочисленные последовательные звонки, все в порядке (conid в моем коде ниже приращений - 5,6,7,8,9 и т. Д.). Но когда более одного клиента связывается с одновременным подключением, иногда происходит дублирование conid (пример из одного запуска: 5,6,7,7,8,9,10,10,10,11,12,12, ...) ,

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

Редактировать: отредактированный код, чтобы показать, что mystruct не был свободен в в то время как цикл и (надеюсь) обеспечивают более глубокое понимание.

Редактировать # 2: за запрос, я опубликовал полный источник моего примера.

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <pthread.h> 
#include <stdarg.h> 
#include <time.h> 
#include <errno.h> 
#include <netdb.h> 


//this is my test structure 
struct mystruct_ { 
    int id; //only id for testing 
}; 
typedef struct mystruct_ mystruct; 

//error logging function 
void merr(const char *msg, ...) { 
    //get the time 
    time_t t; 
    time(&t); 
    //grab this function's arguments 
    va_list args; 
    char buf[BUFSIZ]; 
    va_start(args,msg); 
    //build the message 
    vsprintf(buf,msg,args); 
    //output the message 
    printf(" ERROR :: %s\n",buf); 
    //that's it! 
    va_end(args); 
} 


//this function handles the threads 
void *ThreadedFunction(void *arg) { 
    //get the passed structure 
    mystruct *test = (mystruct *)arg; 
    //print conid -- this is where I am seeing the duplicates 
    printf("my connection id is %d\n",test->id); 
    // do some stuff, like: pull vars out of mystruct 
    int nbytes; 
    char buf[256]; 
    while(1) { 
     if((nbytes=recv(test->id, buf, sizeof buf, 0)) <= 0) { 
      //handle break in connection 
      close(test->id); 
     } else { 
      //for this example, just print out data from client to make my point 
      buf[nbytes] = 0; 
      printf("%s",buf); 
     } 
    } 
} 

//main just sets up the connections and creates threads 
int main(int argc, char *argv[]) 
{ 
    char *port = "1234"; 

    //get ready for connection 
    struct sockaddr_storage addr; 
    socklen_t addrsize = sizeof addr; 
    struct addrinfo hints, *res, *ai, *p; 
    int sockfd, conid, rv; 
    int yes = 1; 
    // 
    //load up address structs with getaddrinfo(): 
    memset(&hints, 0, sizeof hints); 
    hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever 
    hints.ai_socktype = SOCK_STREAM; 
    hints.ai_flags = AI_PASSIVE;  // fill in my IP for me 
    if((rv = getaddrinfo(NULL, port, &hints, &ai))!= 0) { 
     merr("failed to bind port '%s': %s\n",port,gai_strerror(rv)); 
     exit(1); 
    } 
    // 
    //bind the port 
    for(p=ai; p!=NULL; p=p->ai_next) { 
     sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); 
     if(sockfd<0) continue; 
     //setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); //commented for testing 
     if(bind(sockfd,p->ai_addr,p->ai_addrlen)<0) { close(sockfd); continue; } 
     break; 
    } 
    //if we don't have p, it means server didn't get bound 
    if(p==NULL) { merr("failed to bind port '%s' (reason unknown)",port); exit(2); } 
    freeaddrinfo(ai); //all done with this 
    // 
    // listen to the (now bounded) socket: 
    if(listen(sockfd,10)==-1) { merr("listen; errmsg: \"%s\"",strerror(errno)); exit(3); } 


    // bind(), listen(), etc... blah blah blah 

    mystruct test[1024]; //just for testing 
    printf("Ready and Listening...\n"); 
    while(1) { 
     conid = accept(sockfd, (struct sockaddr *)&addr, &addrsize);//get a connection 
     test[conid].id = conid; 
     pthread_t p; 
     pthread_create(&p,NULL,ThreadedFunction,&test[conid]); //create new thread 
    } 
} 
+0

Проверить, правильно ли вы связываете()? – shinkou

+0

Да, я проверял дважды. 'bind()' происходит правильно. Если это не так, я выхожу из программы, прежде чем принимать соединения. – cegfault

+0

Пожалуйста, разместите реальный код, что у вас там есть некорректное, в частности 'test' является указателем, но вы используете' test.conid'. Или, по крайней мере, сварить его до минимальной _compilable и runnable_ программы, которая обнаруживает проблему. – paxdiablo

ответ

1

Это сломана:

while(1) { 
    conid = accept(sockfd, (struct sockaddr *)&addr, &addrsize);//get a connection 
    test[conid].id = conid; 
    pthread_t p; 
    pthread_create(&p,NULL,ThreadedFunction,&test[conid]); //create new thread 
} 

pthread_t p; объявляет непрозрачную ручку на стеке, который pthread_create будет заполнить срок службы этого дескриптора должен продолжаться, пока вы не звоните pthread_join или pthread_detach..

В этом случае хранилище для этого pthread_t, вероятно, повторно используется, испортив передачу аргумента функции потока. По крайней мере, это моя догадка.

Попробуйте позвонить pthread_detach после pthread_create.

+0

Сонды хорошие, если только перегруженный pthread_create() не копирует ссылку перед созданием потока. –

+0

Вот и все! Большое спасибо. Так просто, но так легко забыть .... – cegfault

1

accept возвращает дескриптор файла, который будет использоваться повторно. Поскольку ваш ThreadedFunction никогда не заканчивается, когда выполняется с файловым дескриптором, вы получите условие гонки. Итак, после заявления closereturn;

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