C: select()下部分代码不执行

问题描述 投票:0回答:3

我有这样的东西:

#define QUIT_TIME 5
int main(int argc, char **argv) {
        //... SOCKETS STUFF ....
    fdmax = parentfd;


    while (notdone) {

        //Set the timers
        waitd.tv_sec = 1;
        waitd.tv_usec = 0;

        FD_ZERO(&tempreadfds);
        FD_ZERO(&tempwritefds);

        FD_ZERO(&readfds);          /* initialize the read fd set */
        FD_ZERO(&writefds);         /* initialize the write fd set */

        FD_SET(parentfd, &readfds); /* add listener socket fd */
        FD_SET(0, &readfds);        /* add stdin fd (0) */

        tempreadfds = readfds; //make a copy
        tempwritefds = writefds; //make a copy

        if (select(fdmax+1, &tempreadfds, &tempwritefds, (fd_set*) 0, &waitd) < 0) {
            error("ERROR in select");
        }

        for(i = 1; i <= fdmax; i++) {

            if(FD_ISSET(i, &readfds)) {
                if(i == parentfd) {
                //This is a new connection
                childfd = accept(parentfd, (struct sockaddr *) &clientaddr, &clientlen);
                if (childfd < 0)
                    error("ERROR on accept");

                InitializeDataStructures(childfd);

                FD_SET(childfd, &readfds); //add to the master set
                if(childfd > fdmax)
                    fdmax = childfd;
            } else {
                //Existing connection
                if((nBytes = read(i, connections[i].buffer, MAXBUFFER)) <= 0) {
                    if(nBytes == 0) {
                        //Connection closed
                        printf("Socket %d hung up\n", read_write_loop);
                    } else {
                        error("\nReceive error\n");
                    }

                    FD_CLR(i, &readfds);
                } else {
                    //We have some data from the connection
                                            //... Manipulate the buffer
                    //Handle the message
                }
            }
            }

            if(FD_ISSET(i, &writefds)) {
                                .....
                FD_CLR(i, &writefds);
            }

            //Timer checking
            if(connections[i].active) {
                gettimeofday(&TimeNow, NULL);
                timeval_diff(&Interval, &TimeNow, &connections[i].TimeConnected);
                printf("*_*_*__*_*_*__*_*_*_*_* difference is %ld seconds, %ld microseconds\n",
                         Interval.tv_sec,
                         Interval.tv_usec
                        );
                if(Interval.tv_sec >= QUIT_TIME) {
                    printf("Timer elapsed!!\n");
                }
            }

        }
    }

    /* clean up */
    printf("Terminating server.\n");
    close(parentfd);
    return 0;
}

void InitializeDataStructures(int i) {

    clients[i].active = YES;
    clients[i].fd = i;
    //Initialize other members of the structure
}

long long timeval_diff(struct timeval *difference, timeval *end_time, struct timeval *start_time) {
      struct timeval temp_diff;

      if(difference==NULL)
        difference=&temp_diff;

      difference->tv_sec =end_time->tv_sec -start_time->tv_sec ;
      difference->tv_usec=end_time->tv_usec-start_time->tv_usec;

      while(difference->tv_usec<0)
      {
        difference->tv_usec+=1000000;
        difference->tv_sec -=1;
      }

      return 1000000LL*difference->tv_sec + difference->tv_usec;

    }

我期望在执行过程中“Timer elapsed”行至少会打印一次(TimeConnected 被初始化为 if 条件之一),但由于某种原因,它永远不会打印出来。我认为我的 while 循环应该继续打印它...有人知道我是否在某个地方搞砸了吗?

编辑: 实际上,我正在使用计时器在超时后断开时间。我刚刚观察到,如果另一个客户端连接到服务器,它会打印“计时器已过”。我确实传递了最终参数来选择,但不确定为什么它没有任何效果。

感谢bdk!!如果您有兴趣了解我在这段代码中遇到的“愚蠢”错误,请详细阅读下面的讨论...这是一个明显的错误,我忽略了...这一切都是因为教程中的一句话:“选择修改你的原始描述符”。

变更列表:

  • 请注意,一组 FD_ZERO 语句被错误地放置在 while 循环中
  • FD_ISSET 被传递给 readfds 和 writefds,而不是 tempreadfds 和 tempwritefds...

工作代码:

#define QUIT_TIME 5
int main(int argc, char **argv) {
        //... SOCKETS STUFF ....
    fdmax = parentfd;


    FD_ZERO(&readfds);          /* initialize the read fd set */
    FD_ZERO(&writefds);         /* initialize the write fd set */

    while (notdone) {

        //Set the timers
        waitd.tv_sec = 1;
        waitd.tv_usec = 0;

        FD_ZERO(&tempreadfds);
        FD_ZERO(&tempwritefds);


        FD_SET(parentfd, &readfds); /* add listener socket fd */
        FD_SET(0, &readfds);        /* add stdin fd (0) */

        tempreadfds = readfds; //make a copy
        tempwritefds = writefds; //make a copy

        if (select(fdmax+1, &tempreadfds, &tempwritefds, (fd_set*) 0, &waitd) < 0) {
            error("ERROR in select");
        }

        for(i = 1; i <= fdmax; i++) {

            if(FD_ISSET(i, &tempreadfds)) {
                if(i == parentfd) {
                //This is a new connection
                childfd = accept(parentfd, (struct sockaddr *) &clientaddr, &clientlen);
                if (childfd < 0)
                    error("ERROR on accept");

                InitializeDataStructures(childfd);

                FD_SET(childfd, &readfds); //add to the master set
                if(childfd > fdmax)
                    fdmax = childfd;
            } else {
                //Existing connection
                if((nBytes = read(i, connections[i].buffer, MAXBUFFER)) <= 0) {
                    if(nBytes == 0) {
                        //Connection closed
                        printf("Socket %d hung up\n", read_write_loop);
                    } else {
                        error("\nReceive error\n");
                    }

                    FD_CLR(i, &readfds);
                } else {
                    //We have some data from the connection
                                            //... Manipulate the buffer
                    //Handle the message
                }
            }
            }

            if(FD_ISSET(i, &tempwritefds)) {
                                .....
                FD_CLR(i, &writefds);
            }

            //Timer checking
            if(connections[i].active) {
                gettimeofday(&TimeNow, NULL);
                timeval_diff(&Interval, &TimeNow, &connections[i].TimeConnected);
                printf("*_*_*__*_*_*__*_*_*_*_* difference is %ld seconds, %ld microseconds\n",
                         Interval.tv_sec,
                         Interval.tv_usec
                        );
                if(Interval.tv_sec >= QUIT_TIME) {
                    printf("Timer elapsed!!\n");
                }
            }

        }
    }

    /* clean up */
    printf("Terminating server.\n");
    close(parentfd);
    return 0;
}

void InitializeDataStructures(int i) {

    clients[i].active = YES;
    clients[i].fd = i;
    //Initialize other members of the structure
}

long long timeval_diff(struct timeval *difference, timeval *end_time, struct timeval *start_time) {
      struct timeval temp_diff;

      if(difference==NULL)
        difference=&temp_diff;

      difference->tv_sec =end_time->tv_sec -start_time->tv_sec ;
      difference->tv_usec=end_time->tv_usec-start_time->tv_usec;

      while(difference->tv_usec<0)
      {
        difference->tv_usec+=1000000;
        difference->tv_sec -=1;
      }

      return 1000000LL*difference->tv_sec + difference->tv_usec;

    }
c timer posix-select
3个回答
1
投票

看看你选择的循环参数,它们对我来说看起来很可疑。主要是您在 tempreadfd 和 tempwritefd 上调用 select,但是当您调用 FD_ISSET 时,您将其传递给 readfd 和 writefd。在调用 select 之前,您使用 FD_SET 来设置您感兴趣的所有 fd。由于这些变量没有被发送到 select,因此未触发的 fd 不会被屏蔽。因此,您将在所有描述符上检测到“活动”。该接受描述符上确实没有任何活动,因此它会阻塞,直到有新客户端连接为止。

至少这是我的猜测。


1
投票

如果我正确地阅读了您的代码,您将进入 while 循环,然后检查描述符是否在读取集中。如果是,则转到 if 语句的accept() 部分。如果不是,则输入 else 部分,立即阻止读取。如果套接字处于活动状态,则机器人没有可用数据,您将阻塞在那里,直到数据可用。它不会进入甚至检查计时器的部分,直到成功读取或输入错误。

如果 select 返回大于零的值,则应仅在检查套接字的位置输入代码,然后应在尝试读取套接字之前检查该套接字是否在读取集中。

通常,您会构建一个 fdset 来检查您正在接受连接的套接字,并构建另一个 fdset 来检查您已接受并实际读取数据的套接字。我想你可以按照你提出的方式来做,但我建议你从 read() 切换到 recv() 并使用 MSG_PEEK 标志。


0
投票

这是一个非常特定于代码的问题,但谁知道......也许这对其他人会有一些帮助......我最终使用一个线程来解决问题(我知道......不是最好的,但我只是去疯狂地试图调试这个......)。感谢所有耐心帮助我的人...

在上面的代码中,我没有在 main() 的 while 循环内测试计时器,而是将其修改如下:

if(ThreadSpawned == 0) {
                pthread_create(&thread, NULL, cleanup, (void *) &fdmax);
                ThreadSpawned = 1;
            }

然后清理函数如下:

void *cleanup(void *arg) {
    //Timer checking
    int i, *fdmax;
    fdmax = (int *) arg;

    while(1) {
        for(i = 1; i <= *fdmax; i++) {
            if(connections[i].active == 1) {
                gettimeofday(&TimeNow, NULL);
                timeval_diff(&Interval, &TimeNow, &clients[i].TimeConnected);
                printf("*_*_*__*_*_*__*_*_*_*_* difference is %ld seconds, %ld microseconds\n",
                        Interval.tv_sec,
                        Interval.tv_usec
                );
                fflush(stdout);
                if((int) Interval.tv_sec >= QUIT_TIME) {
                    printf("Timer elapsed!!\n");
                    fflush(stdout);
                }
            }
        }
        sleep(20);
    }
}

任何更清洁的解决方案都是最受欢迎的。也许,bdk 在他的一个答案中提出的原因是程序在接受时阻塞的原因,但是如果它在那里阻塞,我将无法计算超时......所以我最终使用一个线程来做这个任务...

© www.soinside.com 2019 - 2024. All rights reserved.