【问题标题】:In C how can I access/iterate through file descriptors that are added dynamically to an fd_set?在 C 中,我如何访问/迭代动态添加到 fd_set 的文件描述符?
【发布时间】:2018-05-30 16:54:11
【问题描述】:

我正在尝试使用 select() 和 fd_sets 在 Linux 中使用 C 实现服务器/多客户端程序。我正在尝试将从一个连接的客户端发送的消息广播到所有其他连接的客户端,但我不知道如何在动态添加后访问 fd_set 中其他客户端的套接字。我正在尝试复制我在 C++ 中找到的这个实现,但是 C 中的 fd_set 没有 C++ 的属性。这是我要复制的代码:

for(int i = 0; i < master.fd_count; i++)
{
    SOCKET outSock = master.fd_array[i];
    if(outSock != listening && outSock != sock)
    {
        send(outSock, buffer, 250);
    }
}

其中master 是 fd_set,listening 是侦听新客户端的原始套接字,sock 是即将广播的消息来自的套接字。

谁能帮助我学习如何访问 fd_set 套接字元素,以便能够像示例中那样对它们进行!= 比较。或者,将我指向另一种方法来实现多客户端设置,我可以将消息广播回所有连接的客户端。我最初尝试使用带有fork() 管道的多进程,但我找不到有关如何正确实现它的足够信息。

【问题讨论】:

  • 通常你应该在一个单独的数组中跟踪你添加到集合中的元素,然后对其进行迭代。您可以使用 FD_ISSET 检查 fd 是否在集合中
  • 不要使用select。使用poll。无论如何,它更容易使用,并且前者存在基本的接口缺陷,使得在没有未定义行为和黑客攻击的情况下无法使用高 fd 数。

标签: c linux


【解决方案1】:

在 C 中,您使用宏 FD_ISSET 来确定给定位是否已设置。详情请见manual page for select(2)

基本思想是,首先用FD_ZERO 将集合归零,然后用FD_SET 设置一些位,然后调用select()(或pselect(),根据口味)。当select() 返回时,您遍历集合并使用FD_ISSET 确定您是否可以对指定描述符执行非阻塞 I/O 操作。

网上有很多例子;例如,an example from IBM

【讨论】:

    【解决方案2】:

    养成阅读手册页的习惯。 select(2) 列出了为fd_set 提供的宏:

       void FD_CLR(int fd, fd_set *set);
       int  FD_ISSET(int fd, fd_set *set);
       void FD_SET(int fd, fd_set *set);
       void FD_ZERO(fd_set *set);
    

    您可以使用FD_SETFD_ISSET 宏来设置或测试fd_set 中与您的文件描述符相对应的位。

    【讨论】:

      【解决方案3】:

      保留您自己的已连接“用户”列表,例如包含每个用户所需的结构的链接列表(如用户名和其他数据)套接字描述符。

      然后,如果您需要向所有用户发送消息,只需遍历此列表即可。由于结构包含有关用户的所有信息,因此在迭代时很容易跳过一个或多个用户,例如不发送给消息来源的用户。


      简单示例

      struct user
      {
          char  *name;    // Name of the user
          SOCKET socket;  // Socket descriptor for communication
          // Other data needed for the user...
          struct user *next;  // For linking into a list
      };
      
      // The list of all users
      struct user *users = NULL;
      
      // Broadcast a message to *all* connected users
      void broadcast(const char *message)
      {
          for (struct user *u = users; u != NULL; u = u->next)
          {
              send(u->socket, message, strlen(message), 0);
          }
      }
      
      // Broadcast to all except a specific user
      void broadcast_except_name(const char *message, const char *name)
      {
          for (struct user *u = users; u != NULL; u = u->next)
          {
              if (strcmp(u->name, name) != 0)
              {
                  send(u->socket, message, strlen(message), 0);
              }
          }
      }
      
      // Broadcast to all except a specific socket
      void broadcast_except_socket(const char *message, SOCKET socket)
      {
          for (struct user *u = users; u != NULL; u = u->next)
          {
              if (u->socket != socket)
              {
                  send(u->socket, message, strlen(message), 0);
              }
          }
      }
      

      [省略创建或操作列表的函数]

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-10-31
        • 2021-07-06
        • 2020-09-08
        • 1970-01-01
        • 1970-01-01
        • 2011-03-31
        • 2018-07-22
        相关资源
        最近更新 更多