2011-12-22 3 views
1

많은 동시 연결을 허용하는 서버 데몬을 만들고 클라이언트가 서버로 데이터를 전송합니다. 현재 각 클라이언트 연결을 새 스레드로 생성하고 있습니다. 나는 (정확히는) 세분화 오류를 포함하여 다양한 문제를 야기하는 기존 연결의 ID를 때로는 (항상은 아님) 돌려 줄 것이라고보고있다.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()으로이 문제를 경험 한 적이 없으므로 아마도 threading과 관련된 문제입니까? 이 시점에서, 나는 생각할 수있는 모든 것에 대해서만 시도해 보았습니다. 그러나 나는 방금 무언가를 놓친 것 같습니다.

편집 : 편집 된 코드는 mystruct가 while 루프, 그리고 (잘하면) 더 많은 통찰력을 제공합니다.

편집 # 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_join 또는 pthread_detach를 호출 할 때까지 그 핸들의 수명이 지속되어야 기입합니다 pthread_create 스택에 불투명 한 핸들을 선언합니다.

이 경우 pthread_t의 저장소가 다시 사용되어 스레드 함수에 인수가 전달되는 것을 망칠 수 있습니다. 적어도 그건 내 추측이다.

pthread_create 이후 pthread_detach으로 전화 해보세요.

+0

오버로드 된 pthread_create()가 쓰레드를 생성하기 전에 레퍼런스를 복사하지 않는 한 좋은 일이다. –

+0

그랬어! 감사합니다. 그렇게 간단하지만 간과하기 쉬운 .... – cegfault

1

accept은 재사용 할 파일 설명자를 반환합니다. ThreadedFunction이 파일 설명자로 끝나면 절대로 경쟁 조건을 갖지 않을 것입니다. 따라서 close 진술 후 return;

관련 문제