linux - 非阻塞套接字与 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()
循环并且它不会阻塞。
解决方案
...如果我需要将 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(...);
}
}
但是,在大多数情况下,这不是必需的,您可以在下一个“外部”循环中简单地处理“剩余”数据字节。
推荐阅读
- sql - 在存在的列上出现 ORA-00904 无效标识符错误
- amazon-web-services - 尝试通过 SSH 连接到 AWS EC2 会返回“端口 22:没有到主机的路由”
- c++ - Visual Studio,从命令行运行 cmakesettings.json
- mesos - Mesos offer.getExecutorIdsList 给出了一个空列表,尽管 UI 显示了执行者
- java - android java等待文件下载器功能
- sql - 相同的帐户不同的状态然后其他其他相同的状态。如何在 T-SQl 中执行此操作
- javascript - VS Code 没有在断点处显示正确的源代码
- python - 正则表达式保留一些内容并删除方括号中的所有内容
- ruby-on-rails - 在 Rails 中使用 Capybara 和 Selenium 测试 Google 地图标记
- php - 邮件()在特定页面上不起作用