【问题标题】:How does select() monitor sockets?select() 如何监控套接字?
【发布时间】:2012-01-15 19:20:51
【问题描述】:

Beej's Guide to network Programming 中所述,select() 监视一组用于读取的文件描述符(使用recv()),一组用于写入的文件描述符(使用send())和最后一个,我不知道。当服务器套接字接收到来自客户端套接字的消息时,read_fds set 将被修改,select() 从阻塞状态返回。向客户端套接字发送消息也是如此。例如:

for(;;) {
        read_fds = master; // copy it
        if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
            perror("select");
            exit(4);
        }
//the rest is code for processing ready socket

我猜read_fds 集合此时将包含唯一的就绪套接字描述符(其他已删除),就绪套接字描述符是新连接的套接字或从连接的套接字发送的消息。我的理解正确吗?

看来ready socket必须一个一个处理。当我试图在 gdb 上运行它以了解行为时,当程序正在处理就绪套接字(select() 返回之后的代码)时,我尝试发送一些消息并通过一些新客户端连接到服务器。即使没有调用select(),它如何识别新客户端或新发送的消息?

【问题讨论】:

  • "即使 select() 没有被调用" - 你为什么不调用它呢?你的意思是什么?
  • @yi_H 我的意思是,在 GDB 中单步执行代码时,未调用 select() 行,但我发送消息或尝试将新客户端连接到调试服务器,检查代码不在 select() 行上。
  • 您是否将监听套接字添加到 read_fds 中?
  • @yi_H 我确实做到了。我只是按照 Beej 的例子。

标签: c sockets


【解决方案1】:

如 Beej 的网络编程指南中所述,select() 监视一组用于读取的文件描述符(使用 recv()),一组用于写入的文件描述符(使用 send())

是的

最后一个,我不知道。

最后一个已经没有任何用处了。

我猜 read_fds 集将包含此时唯一的就绪套接字描述符(其他已删除),就绪套接字描述符是新连接的套接字或从连接的套接字发送的消息。我的理解正确吗?

没错。

看来ready socket必须一个一个处理。当我尝试在 gdb 上运行它以了解行为时,当程序正在处理就绪套接字(select() 返回后的代码)时,我尝试发送一些消息并通过一些新客户端连接到服务器。即使不调用 select(),它如何识别新客户端或新发送的消息?

通常,当您创建这样的轮询循环时,您会在循环中添加新的套接字。也就是说,您可以在下次调用 select 之前将它们添加到适当的 fd_sets 中。

当新套接字变为可写时,您将在其上send

【讨论】:

  • 谢谢。那么 GDB 的情况如何呢?在通过 accept() 行时,我使用 telnet 客户端连接到服务器
  • @Amumu:你的评论毫无意义。
【解决方案2】:

当您处理可能会阻塞的多个套接字时(在您的情况下是读取套接字),您需要确定哪些套接字中有数据等待读取。您可以通过调用 select() 并将套接字添加到您的 read_set 来做到这一点。

对于您的侦听套接字,如果您调用 accept() 并且没有挂起的连接,那么您的接受将阻塞,直到新连接到达。所以你还想选择()这个套接字。接受该客户端后,您需要将其添加到您的 read_set 中。

例如伪代码

for (;;) {
    struct timeval tv = { timeout, 0 };
    fd_set read_set;
    FD_ZERO(&read_set);
    FD_SET(listen_sock, &read_set);
    max_fd = max(max_fd, listen_sock);

    /* add all your other other client sockets to thread read_set */
    n = select(max_fd, &read_set, NULL, NULL, tv);
    if (n > 0) {
        if (FD_ISSET(listen_sock, &read_set)) {
            cli = accept(listen_sock);
            /* add to list of clients */
        }
        else  {
            for (int i = 0; i < max_clients; i++) {
                if (FD_ISSET(clients[i], &read_set)) {
                    /* data is waiting. recv */
                    bytes = recv(clients[i], ..)
                    if (bytes <= 0) {
                        /* error or EOF, remove client list, so we don't select on this anymore */
                    }
                }
            }
        }
    }

注意,如果另一端没有主动读取,并且发送缓冲区已满,发送也可以阻塞。因此,如果您要发送,您可能需要检查它是否“可发送”。

【讨论】:

  • 谢谢。那么 GDB 的情况如何呢?在通过 accept() 行时,我使用 telnet 客户端连接到服务器。
  • 对不起,我真的不明白你对 GDB 的问题。你能详细说明一下吗?
  • 这意味着,当我使用 GDB 检查代码时,行 select(...) 被单步执行。那时,应该没有监控发生,因为 select() 返回了。但是,当我尝试与 telnet 客户端连接时,它确实连接并没有实际停留在 select(...) 行或 accept(...) 行。
  • 好吧,您的选择没有超时,所以它会立即返回。基本上为零。即选择超时。什么都没有准备好。对于 GDB,TCP 将执行 SYN/ACK 序列并完成连接。它正在等待您的用户应用调用接受。
  • 我认为当超时设置为NULL时它会阻塞?当我走到 select() 行时,它实际上被阻塞了。你的意思是连接是由GDB执行的?我很困惑。
猜你喜欢
  • 2015-06-04
  • 1970-01-01
  • 2015-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-28
  • 1970-01-01
  • 2011-02-15
相关资源
最近更新 更多