使用FD_SET进行读写集以在C中发送和接收数据

Zah*_*tar 1 c sockets loops posix-select

我有一个客户端和服务器,客户端运行一个select循环以在TCP和UDP连接之间进行多路复用。我正在尝试将我的TCP连接文件描述符同时添加到readwrite集中,然后使用writeset和1使用readset 发起一个消息交换。我与设备的消息通信write正常,但与read设备无法通信。

客户代码:

    char buf[256] = {};
    char buf_to_send[256] = {};
    int nfds, sd, r;
    fd_set rd, wr;
    int connect_init = 1;

/* I do the Connect Command here */

    FD_ZERO(&rd);
    FD_ZERO(&wr);

    FD_SET(sd, &rd);
    FD_SET(sd, &wr);

    nfds = sd;


    for(; ;){

       r = select(nfds + 1, &rd, &wr, NULL, NULL);

       if(connect_init == 0){

          if(FD_ISSET(sd, &rd)){    // this is not working, if I change rd to wr, it works!

          r = recv(sd, buf, sizeof(buf),0);
          printf("received buf  = %s", buf);
          sprintf(buf, "%s", "client_reply\n");
          send(sd, buf, strlen(buf), 0);

      }
   }
/* Everything below this works correctly */
if (connect_init){

   if(FD_ISSET(sd, &wr)){

     sprintf(buf_to_send, "%s", "Client connect request");
     write(sd, buf_to_send, strlen(buf_to_send)); 

     recv(sd, buf, sizeof(buf), 0);
     printf("Server said = %s", buf);  

     sprintf(buf_to_send, "Hello!\n"); // client Hellos back
     send(sd, buf_to_send, strlen(buf_to_send), 0);

   }
   connect_init = 0;
  }

} // for loops ends
Run Code Online (Sandbox Code Playgroud)

hyd*_*yde 5

每次调用之前,您都需要在循环中初始化集合select。这是必需的,因为select会对其进行修改。Beej的《网络编程指南》中有一种使用方法的综合示例select

因此,在您的代码中,它似乎select首先返回并允许写入,但不允许读取,这会将读取位重置为0,然后没有任何东西可以将其设置为1,因为从那时起select就不会触摸它,因为它已经0。

如果selectAPI困扰您,请看一下poll,它会避免这种情况(请注意,实际/效率上可能没有差异,基本上可以归结为个人喜好)。在具有许多描述符的“真实”代码上(例如具有许多客户端的网络服务器),在性能方面,您应该使用其他机制,可能是一些更高级别的事件库,然后使用操作系统特定的系统API,例如Linux的epoll工具。但是,仅检查几个描述符select是经过尝试的,真实的且相对可移植的选择。

  • 原因是select()修改FD集以指示已准备就绪。如果您不自行重置,则只能选择上次准备的内容。 (2认同)