Может ли свойство fd сокета UNIX влиять на поведение select?

#sockets #select

#сокеты #выберите

Вопрос:

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

server.c

 int main(int argc, char *argv[])
{
    int listenfd = 0, connfd = 0, connfd1 =0;
    struct sockaddr_in serv_addr; 
    char sendBuff[1025];
    time_t ticks; 
    int one = 1;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)amp;one, sizeof(one));

    memset(amp;serv_addr, '0', sizeof(serv_addr));
    memset(sendBuff, '0', sizeof(sendBuff)); 

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(12345); 

    bind(listenfd, (struct sockaddr*)amp;serv_addr, sizeof(serv_addr));
    listen(listenfd, 10);
    connfd = accept(listenfd, (struct sockaddr*)NULL, NULL); 
    ticks = time(NULL);
    snprintf(sendBuff, sizeof(sendBuff), "%.24sn", ctime(amp;ticks));
    write(connfd, sendBuff, strlen(sendBuff)); 
    close(connfd);
    close(listenfd);
    printf("existing...n");
}
  

client.c:

 int main(int argc, char *argv[])
{
    int sockfd = 0, n = 0;
    char recvBuff[1024];
    struct sockaddr_in serv_addr; 
    fd_set rset;
    fd_set wset;
    fd_set eset;
    struct timeval tv;
    int nready;
    int count;

    if(argc != 2)
    {
        printf("n Usage: %s <ip of server> n",argv[0]);
        return 1;
    } 

    memset(recvBuff, '0',sizeof(recvBuff));
    sockfd = socket(AF_INET, SOCK_STREAM, 0));
    memset(amp;serv_addr, '0', sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(12345); 

    inet_pton(AF_INET, argv[1], amp;serv_addr.sin_addr);
    connect(sockfd, (struct sockaddr *)amp;serv_addr, sizeof(serv_addr));
    count = -1;
    while(1){
        FD_ZERO(amp;rset);
        FD_ZERO(amp;eset);
        FD_SET(sockfd, amp;rset);
        FD_SET(sockfd, amp;eset);
        memset(amp;tv, 0, sizeof(struct timeval)); 
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        sleep(1);
        count   ;
        printf("loopcount is %dn", count);

        nready = select(sockfd   1, amp;rset, 0, amp;eset, amp;tv);      
        if(nready < 0){
            printf("error in select, exitn");
            exit(-1);
        }
        if(nready == 0){
            printf("nothing ready, continue...n");
            continue;
        }
        if(FD_ISSET(sockfd, amp;rset)){
            printf("socket readn");
            n = read(sockfd, recvBuff, sizeof(recvBuff) - 1);
            if(n < 0){
              printf("read errorn");
            }else if(n == 0){
                 printf("zero bytes, we assume it is EOFn");
            }else{
                 printf("read n = %dn", n);
                 recvBuff[n] = 0;
                 printf("%sn",recvBuff);
            }
        }
        if(FD_ISSET(sockfd, amp;eset)){
            printf("socket exceptionn");
        }
     }
     close(sockfd);

    return 0;
}
  

Итак, код очень прост, сервер просто ждет первого клиента, затем записывает ему текущее время, затем сервер закроет все FD и завершит работу.
В клиенте он сначала подключается к серверу, затем он будет бесконечно выбирать этот sockfd для чтения и исключения.
Результат выполнения на моей тестовой машине:

 ./client 9.21.63.207
loopcount is 0
socket read
read n = 25
Fri Jun 13 02:50:53 2014

loopcount is 1
socket read
zero bytes, we assume it is EOF
loopcount is 2
socket read
zero bytes, we assume it is EOF
loopcount is 3
socket read
zero bytes, we assume it is EOF
  

Мы можем видеть, что после завершения работы сервера select на стороне клиента по-прежнему сообщает nReady> 0, и sockfd можно прочитать, хотя read возвращает 0, поскольку мы достигли EOF. Я думаю, это имеет смысл. Но когда я встраиваю этот простой код в более крупную систему (это продукт нашей компании, около 1,5 млн строк кода), после завершения работы сервера select на стороне клиента сообщит nReady ==0, обратите внимание, он просто возвращает 0, а не -1, что означает, что здесь не возникает ошибок….

Поскольку наш проект очень сложный, я не знаю, почему этот простой код ведет себя по-другому, когда он встроен в более крупную систему, а не в отдельное приложение. Я предполагаю, что, возможно, наш проект установил некоторые глобальные параметры сокета, которые влияют на поведение select? Я не знаю, кто-нибудь знает, почему? Спасибо.

Комментарии:

1. Это просто дикий выстрел в темноте, но создает ли ваш реальный сервер (т. Е. Тот, Который находится в проекте с 1,5 миллионами строк кода) какие-либо дочерние процессы? Если это так, то ваша проблема может заключаться в том, что один или несколько из этих дочерних процессов автоматически унаследовали копию файлового дескриптора сервера для своего сокета, и именно поэтому сокет остается открытым даже после завершения исходного серверного процесса. Если это проблема, вы можете избежать ее в Windows, вызвав SetHandleInformation((HANDLE)fd, HANDLE_FLAG_INHERIT, 0) для сокета, или в Unix / Posix-land, закрыв сокет внутри дочернего процесса.

2. @Jeremy: Ты гений! Это действительно причина, как вы указали, есть дочерний процесс, который не закрывает fd, большое спасибо!