17

我想知道我的 TCP 套接字上有多少字节是可读的。我用标志“FIONREAD”调用ioctl,它实际上应该给我这个值。当我调用该函数时,我得到了 return val 0(所以没有 Error),但我的整数参数也得到了值 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 函数时,我确实读取了一些有效的数据(这是我所期望的)

4

4 回答 4

15

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

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

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

/* Try FIONREAD until we get *something* or ioctl fails. */
while (!bytesAv && ioctl (m_Socket,FIONREAD,&bytesAv) >= 0)
    sleep(1);
于 2011-08-08T09:18:11.347 回答
13

这里真正的答案是像 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 将返回套接字上可用的尽可能多的字节,但是不超过 [您传入的缓冲区大小]”。

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

于 2013-08-14T02:02:09.680 回答
5

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

于 2013-02-09T15:39:28.673 回答
4

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

于 2011-08-08T09:09:05.533 回答