Linux - 带有 FIONREAD 的 ioctl 始终为 0

Posted

技术标签:

【中文标题】Linux - 带有 FIONREAD 的 ioctl 始终为 0【英文标题】:Linux - ioctl with FIONREAD always 0 【发布时间】:2011-08-08 08:59:32 【问题描述】:

我想知道我的 TCP 套接字上有多少字节是可读的。我用标志“FIONREAD”调用ioctl,它实际上应该给我这个值。 当我调用该函数时,我得到了 return val 0 (所以没有错误),但我的整数参数也得到了值 0。这没问题,但是当我调用 recv() 方法时,我实际上从套接字中读取了一些字节。我做错了什么?

//这里有一些代码:

char recBuffer[BUFFERLENGTH] = 0;
int bytesAv = 0;
int bytesRead = 0;
int flags = 0;
if ( ioctl (m_Socket,FIONREAD,&bytesAv) < 0 )

    // Error

if ( bytesAv < 1 )

    // No Data Available

bytesRead = recv(m_Socket,recBuffer,BUFFERLENGTH,flags);

当我调用 recv 函数时,我实际上读取了一些有效数据(这是我所期望的)

【问题讨论】:

【参考方案1】:

它发生得非常快,这就是为什么你什么都看不到的原因。你在做什么:

ioctl: 有数据给我吗? 没有,还没有 recv:阻塞,直到有我的数据。稍后(短)时间:这是您的数据

所以如果你真的想看FIONREAD,就等着看吧。

/* Try FIONREAD until we get *something* or ioctl fails. */
while (!bytesAv && ioctl (m_Socket,FIONREAD,&bytesAv) >= 0)
    sleep(1);

【讨论】:

注意:这是一个玩具示例代码,不要在实际代码中这样做:) @y_H 嘿,我只是把枪递给他 :-)) 有效!谢谢!即使没有循环也能工作(只要给他时间来获取数据^^)但我现在还有另一个问题...... @Toby 你真的需要关注select(2) 我在想,为什么没有人建议使用 poll() 或 epoll()。然后尝试获取可供读取的数据大小。【参考方案2】:

这里真正的答案是像 cnicutar 所说的那样使用 select(2) 。托比,你不明白的是你有一个竞争条件。首先,您查看套接字并询问其中有多少字节。然后,当您的代码正在处理“这里没有数据”块时,硬件和操作系统正在接收与您的应用程序异步的字节。因此,到调用 recv() 函数时,“没有可用字节”的答案不再正确......

if ( ioctl (m_Socket,FIONREAD,&bytesAv) < 0 )
 // Error 


// BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!

if ( bytesAv < 1 ) // AND HERE!

    // No Data Available
    // BUT BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!


// AND MORE BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!

bytesRead = recv(m_Socket,recBuffer,BUFFERLENGTH,flags);
// AND NOW bytesRead IS NOT EQUAL TO 0!

当然,两年前的小睡眠可能修复了你的程序,但它也教会了你糟糕的编码练习,你失去了学习如何使用 select() 正确使用套接字的机会。

此外,正如 Karoly Horvath 所说,您可以告诉 recv 读取的字节数不要超过用户传入的缓冲区中可以存储的字节数。然后您的函数接口变为“此 fn 将返回尽可能多的字节数socket,但不超过 [你传入的缓冲区大小]"。

这意味着这个函数不再需要担心清除缓冲区了。调用者可以根据需要多次调用您的函数以清除其中的所有字节(或者您可以提供一个单独的 fn 来丢弃数据批发并且不将该功能绑定在任何特定的数据收集函数中)。通过不做太多事情,您的功能更加灵活。然后,您可以创建一个对特定应用程序的数据传输需求智能的包装器函数,并且 fn 根据该特定应用程序的需要调用 get_data fn 和 clear_socket fn。现在,您正在构建一个库,您可以从一个项目到另一个项目,如果您有幸拥有一个可以让您随身携带代码的雇主,那么您可能还可以从一个项目到另一个项目。

【讨论】:

竞争条件不一定为真:如果套接字阻塞,则所有处理都可以在任何数据到达之前完成。然后,recv 调用就等到数据最终到达...【参考方案3】:

使用 select() 然后 ioctl(FIONREAD) 然后 recv()

【讨论】:

【参考方案4】:

你没有做错,如果你使用阻塞 I/O,recv() 将阻塞直到数据可用。

【讨论】:

但问题是当我调用 ioctl 函数时数据应该已经存在了。这不像我调用 recv() 然后数据才到达。我知道这一点是因为我想要接收的数据是对我在代码前面发送一些行的命令的回答。 前面几行?您期待立即得到答复吗?它不会发生。在那里休眠或在套接字上执行 select/poll/epoll/kqueue,ioctl 调用就可以了。你为什么要打那个电话? 我其实也只是想到了这一点 - 只是想在发送命令和 ioctl 之间稍作睡眠尝试一下 我还是不知道你为什么要打那个ioctl调用。有什么意义? 使用 select 或 poll... 我不知道你所说的 flush 是什么意思。

以上是关于Linux - 带有 FIONREAD 的 ioctl 始终为 0的主要内容,如果未能解决你的问题,请参考以下文章

为啥来自 /dev/null 的 ioctl FIONREAD 在 Mac OS X 上返回 0 而在 Linux 上返回随机数?

使用 FIONREAD 调用 ioctl() 会在明显的竞争条件下导致奇怪的副作用,

在哪里可以找到系统调用参数的定义?

.Net Core Linux centos7行—IOC模块

Spring--IOC注解方式注入

IOC - 具有静态辅助方法的 util 类是不是应该与 IOC 连接?