非阻塞套接字与select驱动的方法

问题描述

我正在编写用户空间应用程序,该应用程序除其他功能外还使用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,&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()循环不会阻塞。

解决方法

...如果我需要将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(...);
    }
}

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

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...