在C文件描述符中选择可能无法正确设置?

时间:2014-06-08 23:34:26

标签: select io network-programming synchronous

所以我一直在浏览Beej的网络指南。在之前的一个问题上,我被告知要删除由于阻塞I / OI需要使用select来检查文件描述符之类的大量冻结,然后才接收,所以我只收到什么东西在套接字中等待 - 我试图还有一个用户可以发送和接收的程序 - 所以我想尝试为stdin添加select并尽可能使用fgets-我假设它只是在0处检查fd ...

问题是,我现有的代码没有做任何事情 - 任何想法?

char prev[100];
    char nil[100];
    memset(nil, 0, sizeof nil);


    struct timeval tv;
    fd_set read_fds;
    tv.tv_sec = 0;
    tv.tv_usec = 500000;

    FD_ZERO(&read_fds);
    FD_SET(sockfd, &read_fds);
    FD_SET(STDIN, &read_fds);

    int fdmax = new_fd+1;



    while (1) {



/*      if (send(sockfd, "Howdy", 100, 0) == -1) {
            perror("send");
            exit(1);
        }*/


         if (select(fdmax, &read_fds, NULL, NULL, &tv) == -1) {
            perror("select");
            exit(4);
            }

            int i = 0;

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

                if (FD_ISSET(i, &read_fds)) {



                    if (i == STDIN) {

                        printf("You have input");
                    }

                    if (i == sockfd) {



                        if ((numbytes = recv(sockfd, buf, 99, 0)) == -1) {
                            perror("recv");
                            exit(1);
                        }

                        if (strcmp(prev, buf) == 0 || strcmp(nil, buf) == 0 ) {
                            continue;
                        }

                        buf[numbytes] = '\0';

                        printf("\n%s\n", buf);

                        memmove(prev, buf, sizeof buf);
                    }
                }
            }

2 个答案:

答案 0 :(得分:1)

这是因为您可能在select系统上使用Linux,而您可能没有看到其手册页的这一部分:

  

在Linux上,select()修改超时以反映未经过的时间   睡觉;大多数其他实现不会这样做。 (POSIX.1-2001   允许任何行为          ior。)这会导致读取超时的Linux代码移植到其他操作系统以及代码移植时出现问题   移植到重用了Linux的Linux          struct loopval用于循环中的多个select(),而不重新初始化它。考虑在select()之后未定义超时   回报。

这意味着传递给timeval的{​​{1}}结构会在内部修改,您应该在调用select函数之间重置它。

作为旁注,您可能还需要查看epoll,它可以帮助您查看文件描述符更改select之类的事件,但更自然地使用,更具可扩展性。

答案 1 :(得分:0)

下面块中的所有代码都应该在while()循环中(即在每次调用select()之前)。 select()在返回之前修改这些数据结构的状态,因此在每次调用select()之前必须将它们重新初始化为默认状态。

struct timeval tv;
fd_set read_fds;
tv.tv_sec = 0;
tv.tv_usec = 500000;

FD_ZERO(&read_fds);
FD_SET(sockfd, &read_fds);
FD_SET(STDIN, &read_fds);

此外,fdmax应设置为您传递给FD_SET的所有fd的最大值,再加上1:

int fdmax = std::max(sockfd, STDIN)+1;

最后,你的for循环是不必要的;你可以改为

if (FD_ISSET(STDIN, &read_fds)) {
   printf("You have input");
}
else if (FD_ISSET(sockfd, &read_fds)) {
   // code to recv() from sockfd goes here
}