C试图理解select()和FD_ISSET()

时间:2014-10-19 22:54:01

标签: c sockets

我试图建立一个基本的非阻塞聊天客户端,但我真的不明白select()FD_ISSET()。我试图用下面的代码听插座,但它不会工作,它不会打印任何东西,为什么不呢?

#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>

int main(int argc, char const* argv[])
{
   fd_set readfs;
   char sendline[100];
   char str[100];
   char *some_addr;
   int listen_fd, comm_fd;

   struct sockaddr_in servaddr;

   listen_fd = socket(AF_INET, SOCK_STREAM, 0);

   //Socket error
   if (listen_fd == -1) {
      printf("Error on getting socket, Exiting!\n");
      return 1;
   }
   bzero(&servaddr, sizeof(servaddr));

   servaddr.sin_family = AF_INET;
   servaddr.sin_addr.s_addr = htons(INADDR_ANY);
   servaddr.sin_port=htons(22000);

   bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));

   listen(listen_fd, 10);

   comm_fd = accept(listen_fd, (struct sockaddr *) NULL, NULL);

   FD_ZERO(&readfs);
   FD_SET(comm_fd, &readfs);

   while (1)
   {
      select(listen_fd,&readfs, NULL, NULL, NULL);
      if(FD_ISSET(listen_fd,&readfs))
      {
         bzero(str,100);
         read(listen_fd,str,100);
         printf("%s", str);
         /* write(listen_fd, "read!", strlen(str)+1); */
      }
   }
   return 0;
}

编辑: 我的代码试图连接到服务器:

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>

int main(int argc,char **argv)
{
    int sockfd,n;
    char sendline[100];
    char recvline[100];
    struct sockaddr_in servaddr;

    sockfd=socket(AF_INET,SOCK_STREAM,0);
    bzero(&servaddr,sizeof servaddr);

    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(22000);

    inet_pton(AF_INET,"127.0.0.1",&(servaddr.sin_addr));

    connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));

    while(1)
    {
        bzero( sendline, 100);
        bzero( recvline, 100);
        fgets(sendline,100,stdin); /*stdin = 0 , for standard input */

        write(sockfd,sendline,strlen(sendline)+1);
        read(sockfd,recvline,100);
        printf("%s\n",recvline);
    }

    return 0;
}

2 个答案:

答案 0 :(得分:1)

这里有四个主要问题:

  1. 您的select()来电和读/写循环应该使用comm_fd,而不是listen_fd。如果您在select()上致电listen_fd,当有accept()连接可用时,它会返回,但您想等待已连接的套接字输入,请使用{{ 1}}。

  2. comm_fd的第一个参数应该是集合中的最高文件描述符加一个。由于您只有一个文件描述符,因此它应该是select()

  3. 您应该在comm_fd + 1循环中移动FD_ZEROFD_SET个宏,并在每次while调用之前执行这些宏,因为select()将修改你传递给它的那些fd集。

  4. 您不会检查系统调用的返回错误。你应该。

  5. 其他要点:

    1. select()已经从POSIX移除了很长一段时间,现在,您应该使用标准bzero()

    2. 您不应该memset()通过INADDR_ANY,只需按原样使用它。

    3. 这只是您计划中的评论,但htons()可能是STDIN_FILENO0stdin指针,而不是FILE }。

答案 1 :(得分:0)

  
    

但我真的不明白select()和FD_ISSET()

  

fd_set就像一个位数组。数组中的每个位代表一个套接字或文件描述符。

FD_ISSET()是一个宏或函数,它告诉您是否在位数组(fd_set)中设置了给定的套接字描述符(例如4)。 FD_SET()允许你自己设置一个位,而FD_CLR()可以让你清空一点。

这些位不是神奇地设置,你使用select()要求操作系统内核相应地设置或清除fd_set中的每个位,然后用FD_ISSET()检查每个位并相应地采取行动。在调用select()之前,您必须设置集合以通过使用FD_SET()设置fd_set中的位或者如果要设置大量套接字/位来告诉内核您有兴趣轮询哪些描述符,使用主fd_set并将整个内容复制到您的读,写或错误集。为了提高效率,我通常会选择后者。这些是通常从0到N的整数(前3个通常不是套接字,所以你通常轮询3 .. N)。选择返回后,必须检查位。如果在readfds中设置了一个位,则可以进行读取。

select(int nfds, fd_set *readfds, fd_set *writefds,
              fd_set *exceptfds, struct timeval *timeout);

支持的状态是&#34;准备好阅读&#34;,&#34;准备好写&#34;和&#34;错误条件&#34;

如果您未在集合中设置特定位,则内核不会向您报告其状态。

同样,如果您未将nfds参数(最大描述符值)设置得足够高,则将忽略高于最大值的任何描述符。描述符不必是连续的,只是在nfds范围内。

所有这些逻辑都假设系统调用成功返回值。如果系统调用返回错误状态,您甚至不会考虑该调用的数据结构,必须进行适当的恢复或处理。

在您的代码中跳出来的主要问题是您选择的电话的第一个参数。它不会检查comm_fd是否comm_fd低于listen_fd。

我建议您保留一个max_desc的int值,并且每次接受一个新套接字时,设置max_desc = MAX(max_desc, new_fd+1),当关闭套接字时,您需要向下调整它。我总是喜欢保留一个单独的fd_set来跟踪我的进程打开的描述符(从不将它传递给select()只是用它来记账)。