【问题标题】:non-blocking socket vs. select() driven approach非阻塞套接字与 select() 驱动方法
【发布时间】:2020-12-04 19:27:55
【问题描述】:

我正在编写用户空间应用程序,其中包括使用netlink 套接字与内核通信的其他功能。我使用开源库libmnl提供的简单API。

我的应用程序在 netlink 上设置了某些选项,并订阅了 netlink 事件(通知),解析它等。所以第二个功能(事件通知)是异步的,目前我实现了一个简单的基于 select() 的循环:

...
fd_set rfd;
struct timeval tv;
int ret;

while (1) {
   tv.tv_sec = 1;
   tv.tv_usec = 0;
   FD_ZERO(&rfd);
   /* fd - is a netlink socket */
   FD_SET(fd, &rfd);

   ret = select(fd + 1, &rfd, NULL, NULL, &tv);
   if (ret < 0) {
     perror("select()");
     continue;
   } else if (ret == 0) {
      printf("Timeout on fd %d", fd);
   } else if (FD_ISSET(fd, &rfd)) {
       /*
            count = recv(fd, buf ...)
            while (count > 0) {
               parse 'buf' for netlink message, validate etc.
               count = recv(fd, buf)
            }

       */
   }
}

所以我现在观察到 else if (FD_ISSET(fd, &amp;rfd)) { 分支内的代码在第二次 recv() 调用时阻塞。

现在我想了解是否需要将 netlink 套接字设置为非阻塞(例如 SOCK_NOBLOCK),但我可能根本不需要 select(),我只需 @987654330 @循环,它不会阻塞。

【问题讨论】:

  • 如果你不使用select(),你的代码将在一个紧密的循环中运行,不断调用recv()并得到EWOULDBLOCK错误,除非你在循环中插入一个sleep。
  • 阻塞是有原因的——没有数据可以接收。您当然可以使套接字不阻塞,但是您希望达到什么结果?您将不得不返回并再次调用recv,直到有数据或有处理无数据情况的逻辑。所以这是否是正确的做法取决于期望的行为是什么。
  • 这能回答你的问题吗? socket select ()versus non-block recvseveral others.
  • 在阻塞模式下使用select() 没有多大意义。你也可以直接屏蔽recv()

标签: linux sockets select nonblocking netlink


【解决方案1】:

...如果我需要将 netlink 套接字设置为非阻塞...,但我可能根本不需要select()...

这正是非阻塞套接字的目的:您调用recv() 并评估返回值,而不是执行if(FD_ISSET(...))

如果使用阻塞套接字,则在调用select() 后不能多次调用recv();那么程序是“有效地”非阻塞的。

但是,

...正如用户“kaylum”在他的评论中已经建议的那样,无论如何你都会遇到另一个问题:

不能保证同时有一个完整的“消息”可用。套接字的另一端可能会发送消息的第一部分,等待几秒钟,然后发送消息的第二部分。

但是,select() 会告诉您至少有一个字节可用;它不会告诉你完整的消息是否可用。

如果你想在内部循环(while(count &gt; 0))中等待完整的消息,你总是需要等待(这意味着即使套接字是非阻塞的,你的程序也“有效地”具有阻塞行为) .

如果您只是想处理内部循环中已经可用的所有字节,那么条件count &gt; 0 是错误的。相反,如果你正在使用阻塞套接字,你应该这样做:

else if(FD_ISSET(...))
{
    while(FD_ISSET(...))
    {
        count = recv(...);
        if(count > 0)
        {
            ...
            select(...);
        }
        else FD_ZERO(...);
    }
}

但是,在大多数情况下,这不是必需的,您可以简单地在下一个“外部”循环中处理“剩余”数据字节。

【讨论】:

    最近更新 更多