【问题标题】:Problem with select when closing socket关闭套接字时选择问题
【发布时间】:2011-07-21 04:52:40
【问题描述】:

我正在做一个多客户端服务器,它接受连接、分叉并将连接提供给子节点,以便它可以处理它。它是一个多客户端服务器,所以它有多个子节点。

主进程位于无限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”被修改了一样,但事实并非如此。不知道为什么会这样。

【问题讨论】:

    标签: c sockets select


    【解决方案1】:

    您的代码中有一个问题是您没有检查select 的返回值。

    如果select 被信号中断(返回-1errno = EINTR,例如SIGCHLD 如果其中一个孩子死了),那么&amp;readfds 的内容是未定义的,因此不能是读。 (例如,参见select 的 Linux 手册页。)

    所以检查返回值是否为select,如果出现像EINTR 这样的临时错误,则直接循环返回而不经过&amp;readfds 处理。

    【讨论】:

      【解决方案2】:

      非常感谢 Mat 的评论,实际上是,有一个中断正在取消阻止选择,我用这个解决了它(当然还有更多方法):

      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
      

      我希望这对有同样问题的人有用 =)

      【讨论】:

        【解决方案3】:

        当孩子关闭套接字连接时,select 将解除阻塞,“recv”将返回 0。因此您应始终检查“recv”函数的返回值以确定连接是否关闭。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-02-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-07
          • 1970-01-01
          • 2013-09-22
          • 1970-01-01
          相关资源
          最近更新 更多