Проблема с выбором при закрытии сокета - PullRequest
2 голосов
/ 21 июля 2011

Я делаю многоклиентный сервер, который принимает соединение, разветвляется и передает соединение дочернему элементу, чтобы он мог его обработать.Это многоклиентный сервер, поэтому он имеет несколько дочерних элементов.

Основной процесс находится в бесконечном while, что составляет select, чтобы выяснить, существует ли новое входящее соединение или если ребенок пытаетсяобщаться.

Проблема возникает, когда я закрываю клиента (который подключен к сыну основного сервера): случайным образом случается, что клиентское соединение закрывается, и селектор разблокируется, потому что предположительно внутренний сокет (который обрабатывает входящее соединение между дочерним и основным сервером), но, насколько мне известно, это не так.На самом деле произошло то, что клиент закрыл соединение, а ребенок только что умер.

Может кто-нибудь подсказать мне, что здесь происходит?Я совсем растерялся.

Это код бесконечного цикла на главном сервере:

while (1) {
    /*inicializo variables para el select*/
    sflag = 0;
    FD_ZERO(&readfds);
    FD_SET(sockfd, &readfds);
    FD_SET(sockifd,&readfds);
    max = (sockfd > sockifd) ? sockfd : sockifd;
    for(aux = isockets; aux != NULL; aux = aux -> next){
        FD_SET(aux -> sd, &readfds);
        max = (max > aux -> sd) ? max : aux -> sd;
    }

    printf("pre-select\n");
    select(max + 1, &readfds, NULL, NULL, NULL);
    /*checkeo si salio por actividad en un socket interno*/
    for (aux = isockets; aux != NULL; aux = aux -> next){
        if (FD_ISSET(aux -> sd, &readfds)){
            printf("comunicacion con el socket: %d\n", aux -> sd);
            sflag = 1;
            actsocket = aux -> sd;
            break;
        }
    }
    if (sflag == 1){//mensaje de un hijo
        n = recv(actsocket, buffer, sizeof(buffer), 0);
        if (n == 0) {
            printf("conexion cerrada con el socket interno: %d\n", actsocket);
            close(actsocket);
            isockets = free_sock(isockets, actsocket);
            printf("isockets: %p\n", isockets);
        }
        else if(n < 0) error ("ERROR en comunicacion interna");
        else printf("mensaje del boludon: %s\n", buffer);
    }   
    else if (FD_ISSET(sockifd, &readfds)){// un hijo inicia conexion interna
        printf("antes de accpet interno\n");
        newisockfd = accept(sockifd, (struct sockaddr *) &ucli_addr, &uclilen);
        printf("nueva conexion interna, socketfd: %d\n", newisockfd);
        isockets = add_socket(isockets,newisockfd, 0);
        recorre(isockets);
        if (newisockfd < 0) error ("ERROR en accept unix, padre");
    }
    else if (FD_ISSET (sockfd, &readfds)){/*conexion entrante*/
        printf("conexion entrante\n");
        newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr, &clilen);
            if (newsockfd < 0) error("ERROR on accept");
            pid = fork();

        if (pid < 0) error("ERROR on fork");
            if (pid == 0){//hijo
                    close(sockfd);
            dostuff(newsockfd, path, tm,fd[0]);
                    exit(0);

            }
            else {  //padre
            printf("conexion aceptada, pid hijo %d\n", pid);
            close(newsockfd);
        }
    }
    }

Так, случайно, когда я закрываю соединение, выберите разблокировать как будто "sockifd "был изменен, но это не так.Не знаю, зачем это делает.

Ответы [ 3 ]

4 голосов
/ 21 июля 2011

В вашем коде неправильно то, что вы не проверяете возвращаемое значение select.

Если select прерывается сигналом (возвращает -1 с errno = EINTRНапример, SIGCHLD, если один из детей умер), тогда содержимое &readfds не определено и, следовательно, не должно читаться.(См., Например, справочную страницу Linux для select.)

Так что проверьте возвращаемое значение, если select, и вернитесь к нему, не проходя обработку &readfdsесли есть временная ошибка типа EINTR.

1 голос
/ 22 июля 2011

Большое спасибо за это замечание, Мэт, это было на самом деле, было прерывание, которое разблокировало выбранное, я решаю это с помощью этого (есть и другие способы сделать это, конечно):

repeat_select:
if((err = select (max + 1, &readfds, NULL, NULL, NULL)) < 0)
    if (errno == EINTR) //a signal has interrupted the select, so I restarted it
        goto repeat_select;
    else
        //another error, handle it as you want

Надеюсь, это будет полезно для кого-то с такой же проблемой =)

0 голосов
/ 21 июля 2011

Когда дочерний объект закрывает соединение с сокетом, селектор разблокируется, и «recv» возвращает 0. Поэтому вы всегда должны проверять возвращаемое значение из функции «recv», чтобы определить, закрыто или нет соединение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...