Linux - 使用FIONREAD的ioctl始终为0

Tob*_*oby 11 sockets ioctl

我想知道我的TCP套接字有多少字节可读.我用标志"FIONREAD"调用ioctl,它实际上应该给我这个值.当我调用函数时,我得到返回值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);
Run Code Online (Sandbox Code Playgroud)

当我调用recv函数时,我实际读取了一些有效的数据(我预期的)

cni*_*tar 11

它发生得非常快,这就是为什么你什么也看不见.你在做什么:

  • ioctl:有数据吗?不,还没有
  • recv:阻止,直到有数据给我.一些(短)时间之后:这是您的数据

所以,如果你真的想看FIONREAD,只需等待它.

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

  • 注意:这是一个玩具示例代码,不要在实际代码中这样做:) (7认同)
  • @y_H嘿,我只是递给他枪:-)) (3认同)
  • @Toby你真的需要进入`select(2)`. (3认同)
  • 我想知道,为什么没有人建议使用 poll() 或 epoll()。然后尝试获取可供读取的数据大小。 (2认同)

小智 9

这里真正的答案是使用像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!
Run Code Online (Sandbox Code Playgroud)

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

此外,正如Karoly Horvath所说,你可以告诉recv不要读取比你在用户传入的缓冲区中存储的更多的字节数.然后你的函数接口变成"这个fn将返回插槽上可用的字节数,但是不超过[传入的缓冲区大小]".

这意味着此功能不再需要担心清除缓冲区.调用者可以根据需要多次调用您的函数来清除其中的所有字节(或者您可以提供单独的fn来丢弃数据批发,而不是在任何特定数据收集功能中绑定该功能).不做太多事情,你的功能更灵活.然后,您可以创建一个智能的包装函数,以满足特定应用程序的数据传输需求,并且fn根据特定应用程序的需要调用get_data fn和clear_socket fn.现在你正在建立一个图书馆,你可以在项目之间随身携带,也许你可以找到工作,如果你很幸运,有一个雇主可以让你随身携带代码.


bri*_*ing 5

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