2011-12-22 75 views
1

我正在c中创建一个服务器守护程序,它接受大量的同时连接,并且客户端将发送数据到服务器。我目前有每个客户端连接被催生成一个新的线程。我看到accept()有时(并不总是)会返回现有连接的ID(显然)会导致各种各样的问题,包括分段错误。接受返回现有连接,导致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未被释放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

有一件事要检查是否正确bind()? – shinkou 2011-12-22 04:05:13

+0

是的,我仔细检查。 'bind()'正确发生。如果没有,我在接受连接之前退出程序 – cegfault 2011-12-22 05:21:01

+0

请发布真实代码,你有什么不正确的,特别是'test'是一个指针,但你使用'test.conid'。或者至少将其归结为展示问题的最低_compilable和runnable_程序。 – paxdiablo 2011-12-22 05:32:06

回答

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_joinpthread_detach上的不透明句柄。

在这种情况下,pthread_t的存储可能会被重用,从而导致将参数传递给线程函数。至少,这是我的猜测。

尝试在pthread_create之后致电pthread_detach

+0

Sonds不错,除非重载的pthread_create()在创建线程之前复制引用。 – 2011-12-22 11:47:23

+0

这样做!非常感谢。这么简单,但很容易被忽略.... – cegfault 2011-12-24 05:14:35

1

accept返回一个我可以重用的文件描述符。由于你的ThreadedFunction在完成文件描述符时不会终止,所以你会得到一个竞争条件。所以close声明后return;