принять возвращает существующее соединение, вызывая ошибку сегмента - PullRequest
1 голос
/ 22 декабря 2011

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

Я даже отключил опцию сокета 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
    }
}

Ответы [ 2 ]

1 голос
/ 22 декабря 2011

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

1 голос
/ 22 декабря 2011

Это не работает:

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.

...