17

I'm trying to get to know how many bytes there are readable at my TCP socket. I am calling ioctl with the Flag "FIONREAD" which should actually give me this value. When I call the function I get as return val 0 ( so no Error ) but also my integer argument gets the value 0. That would be no problem but when I call the recv() method I actually read some Bytes out of the socket. What am I doing wrong?

// here some Code:

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);

When I call the recv function i acutally read some valid Data ( which I expected )

273K
  • 29,503
  • 10
  • 41
  • 64
Toby
  • 3,815
  • 14
  • 51
  • 67

4 Answers4

15

It's happening very quickly, that's why you don't see anything. What you're doing:

  • ioctl: Is there data for me ? No, nothing yet
  • recv: Block until there is data for me. Some (short) time later: Here is your data

So if you really want to see FIONREAD, just wait for it.

/* Try FIONREAD until we get *something* or ioctl fails. */
while (!bytesAv && ioctl (m_Socket,FIONREAD,&bytesAv) >= 0)
    sleep(1);
cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • 8
    note: that's a toy example code, don't do that in real code :) – Karoly Horvath Aug 08 '11 at 09:21
  • 7
    @y_H Hey, I'm only handing him the gun :-)) – cnicutar Aug 08 '11 at 09:23
  • Works! Thank you! Worked even without the loop ( just give him to the time to get the data ^^ ) But I have another questio now... – Toby Aug 08 '11 at 09:28
  • ... without the ammo, that's not good enough;) set non-blocking i/o, so his recv call fails too. – Karoly Horvath Aug 08 '11 at 09:28
  • If my socket is blocking , couldn't I just call recv with the flag MSG_PEEK - so my program waits until the there is data available ( or socket timeout... ) but with MSG_PEEK it doesnt discard it. After that I can check the size and then call recv again without the MSG_PEEK flag...? – Toby Aug 08 '11 at 09:30
  • @yi_H True that. `FIONREAD` rarely makes sense without `O_NONBLOCK`. – cnicutar Aug 08 '11 at 09:30
  • 3
    @Toby You really really need to lok into `select(2)`. – cnicutar Aug 08 '11 at 09:31
  • Right. You should select() for read on the socket first; once select returns true, you can safely use FIONREAD. – apenwarr Oct 20 '12 at 07:15
  • 2
    I'm wondering, why nobody suggested to use poll() or epoll(). And after that to try to get the size of the data available for reading. – user1415536 Nov 03 '13 at 10:05
13

The real answer here is to use select(2) like cnicutar said. Toby, what you aren't understanding is that you have a race condition. First you look at the socket and ask how many bytes are there. Then, while your code is processing the "no data here" block, bytes are being received by the hardware & OS asynchronous to your application. So, by the time that the recv() function is called, the answer of "no bytes are available" is no longer true...

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!

Sure, a small sleep probably fixed your program two years ago, but it also taught you terrible coding practice and you lost out on an opportunity to learn how to use sockets correctly by using select().

Further, as Karoly Horvath said, you can tell recv to not read more bytes than you can store in the buffer that the user passed in. Then your function interface becomes "This fn will return as many bytes as are available on the socket, but not more than [buffer size you passed in]".

This means that this function doesn't need to worry about clearing the buffer any more. The caller can call your function as many times as necessary to clear all of the bytes out of it (or you can provide a separate fn that discards the data wholesale and not tie up that functionality in any specific data gather function). Your function is more flexible by not doing too many things. You can then create a wrapper function that is smart to your data transfer needs of a particular application, and that fn calls the get_data fn and the clear_socket fn as needed for that specific app. Now you are building a library you can carry around from project to project, and maybe job to job if you're so lucky as to have an employer that lets you take code with you.

Tim
  • 269
  • 2
  • 5
  • The race condition is not necessarily true: If the socket is blocking, all handling can already be done far before any data arrives. The recv call then just waits until the data finally arrives... – Aconcagua Oct 05 '15 at 09:00
6

Use select() then ioctl(FIONREAD) then recv()

brian beuning
  • 2,836
  • 18
  • 22
4

You're doing nothing wrong, if you are using blocking I/O recv() will block untill the data is available.

Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
  • But the problem is that the Data should already be there when I call the ioctl function. It's not like I call recv() and then the Data just arrives. I know this because I the Data I want to receive is an answer to a command I send some lines earlier in the code. – Toby Aug 08 '11 at 09:12
  • some lines earlier? Do you expect an immediate answer? It won't happen. Put a sleep there or do select/poll/epoll/kqueue on the socket, and the ioctl call will be fine. Why do you want to do that call anyway? – Karoly Horvath Aug 08 '11 at 09:15
  • I actually just thought of that too - just gonna try it with some sleep between the send command and the ioctl – Toby Aug 08 '11 at 09:16
  • 1
    I still don't know why do you want to make that ioctl call. what's the point? – Karoly Horvath Aug 08 '11 at 09:21
  • Okay it works!! thank you - well Im writing an Interface with a buffer provided dynamically by the user. I want to know if the provided buffer is big enough without callin recv multiple times! – Toby Aug 08 '11 at 09:27
  • and what if the buffer is not big enough? what do you want to do? you can limit recv to the buffer size.. – Karoly Horvath Aug 08 '11 at 09:31
  • Just want to give the user the information that the provided buffer was to small... actually...is there any way to discard the data stored in recv? – Toby Aug 08 '11 at 09:39
  • 1
    Use select or poll... and I have no idea what do you mean by flush. – Karoly Horvath Aug 08 '11 at 09:48