首页 > 解决方案 > 非阻塞套接字与 select() 驱动方法

问题描述

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

我的应用程序在 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, &rfd)) {分支块内的代码。recv()

现在我试图了解是否需要将 netlink 套接字设置为非阻塞(SOCK_NOBLOCK例如),但是我可能根本不需要 select(),我只是可以有recv() -> message parse -> recv()循环并且它不会阻塞。

标签: linuxsocketsselectnonblockingnetlink

解决方案


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

这正是非阻塞套接字的目的:if(FD_ISSET(...))您无需调用recv()并评估返回值。

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

然而,

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

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

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

如果您想在内循环 ( while(count > 0)) 中等待完整的消息,您将始终需要等待(这意味着即使套接字是非阻塞的,您的程序也“有效地”具有阻塞行为)。

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

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

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


推荐阅读