超过1024个客户端使用fd_set?

时间:2018-02-28 04:19:55

标签: c++ sockets

我使用select()来衡量服务器未接收任何新消息的时间。代码很简单,看起来像这样:

int res = -1;
do {
    FD_ZERO(&readfds);
    FD_SET(sockfd, &readfds);
    res = select(maxfd+1, &readfds, NULL, NULL, &tv);
    gettimeofday(&current, NULL);
    if (get_time_diff(current, last) >= diff) {
        // do something
    }
} while (res <= 0);

当然,由于fd_set只有1024位,因此不能使用超过1024个连接。幸运的是,我不需要存储所有FD,我只需要知道何时发生新连接。所以我将maxfd+1替换为服务器FD之后的下一个数字(总是相同的,在我的情况下等于18)。

现在一切似乎都很好,并且所有客户端都从服务器获得正确的消息。但是,我不确定它是否是一个有效的解决方案,而且它肯定不是一个非常干净的解决方案。这会导致我不知道的任何问题吗?

1 个答案:

答案 0 :(得分:1)

哎呀,select受设计限制,fd值超过1023使用时不安全。

老实说,您可能无法使用select找到重载生产服务器(如果您这样做,可能不会使用它们。)

  

所以我用服务器的FD之后的下一个数字替换了maxfd + 1(它总是相同的,在我的情况下等于18)。

我假设您的示例是一个最小的示例,并且您不仅仅是在查看单个监听fd(否则,您可能只会阻止accept,更有意义)。

这会提醒你打开另一个文件描述符* ...但是,我觉得这不是最好的方法,因为:

  • 仅对第一个文件描述符...

    执行此操作

    新客户端将根据第一个客户端的状态行事,他们自己的状态将被忽略,因为他们的fd状态从未经过测试。

  • 它使您的代码既脆弱又严格。

  • 其他文件描述符(非套接字)可能占用特定的文件描述符,使实现完全破坏。

  • 如果您将文件描述符添加到集合中,您仍需要测试它们的值。

更好的解决方案可能包括:

  • 测试客户的fd值并强制限制(if (fd >= 1024) close(fd))。

  • 使用poll(老实说,这就是它的原因 - 它是为了解决select强加的限制而引入的。)

  • 使用特定于操作系统的API(适用于Linux的epoll,适用于BSD / macOS的kqueue等)

  • 使用抽象出特定于操作系统的API的库(例如,libev)。

祝你好运!

* Unix样式系统保证为任何新文件描述符分配可用的最低fd值。

这意味着您的第一个客户端将始终收到服务器+ 1(假设您没有打开或关闭任何其他文件描述符)。

但是较新的文件描述符将被赋予更高的值(除非更低的文件描述符可用)。因此,当您为第一个客户的状态进行测试时,其他客户端永远不会被轮询。