3

Assume I have simple protocol implemented over TCP, where each message is made up of:

  1. An int indicating data length.
  2. Binary data, of the length specified in 1.

Reading such a message I would like something like:

int length = input.ReadInt();
byte[] data = input.ReadBytes(length);

Using Socket.Receive or NetworkStream.Read the available number of bytes is read. I want the call to ReadBytes to block until length bytes are available.

Is there a simple way to do this, without having to loop over the read, restarting at an offset waiting for the remaining data?

In a real application the read should probably be done Async or on a background thread, but I've ignored that for now. The important thing is to be able to have the read not complete until all data is available.

Edit

I know that I can buffer the data myself, and I know how to do it. It's just a loop around Receive that continues at the next offset. What I am asking for is if there is a reusable implementation of such a loop, without the need for an own loop of any kind (or alternatively a reusable Async implemenation that finishes when all data is available).

Community
  • 1
  • 1
Anders Abel
  • 67,989
  • 17
  • 150
  • 217

6 Answers6

5

Something, somewhere is going to have to loop. After all, multiple socket reads could be required.

I believe that BinaryReader.Read will keep looping until either it's read as much as you've asked for or hit the end of the stream, but assuming you would want to throw an exception if you reached the end of the stream, I'd write personally write a separate method. It's easy enough to implement in one place and reuse, after all.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    The `BinaryReader.Read` doc says that it returns the number of bytes read and that *This might be less than the number of bytes requested if that many bytes are not available*. I guess I have to test whether this means "bytes available right now" or "bytes received before the socket was closed". – Anders Abel Jul 22 '11 at 16:56
  • 1
    @Anders: Yes, the documentation is unclear. I believe it *will* loop - but that ambiguity is just another reason to write your own method. It's about 7 lines of code - just do it :) – Jon Skeet Jul 22 '11 at 16:59
  • too bad your'e not in Java, read(new byte[9]) will block until 9 bytes are read in....I am in the same boat looking for something simple. note that I do NOT loop in my simulation server. we simply have read(new byte[1]) to get a framing size header and then read(new byte[length]) to get the packet itself...no loop, just two reads after we sent a request. – Dean Hiller Dec 15 '11 at 17:01
  • @DeanHiller: It depends on the input stream - but in general, InputStream.read will *not* block until it's filled the buffer. `DataInputStream.readFully` will, but that's a different matter. If you're using read on a general `InputStream` without checking the return value, you're leaving yourself open to failure. – Jon Skeet Dec 15 '11 at 17:07
  • @JonSkeet, sorry, I was thinking BufferedInputStream which is commonly used so no developer has to write the loop....yes the base InputStreams don't do it....C# needs that, it would be nice. obviously you just wrap any InputStream with the Buffered one and read from the BufferInputStream – Dean Hiller Dec 22 '11 at 18:17
0

Socket.Available will do the trick, if you don't mind a tight loop with a wait in?

http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.available%28VS.71%29.aspx

Kieren Johnstone
  • 41,277
  • 16
  • 94
  • 144
  • 1
    Be careful - if you don't read data it will stall the socket, so that you may wait forever if you are waiting for more than just a tiny data fragment. Therefore I would NOT use this to wait until a certain amount of data has been received. – Lucero Jul 22 '11 at 16:47
0

You need to buffer the data yourself, the specification of read is that it can read any amount between 1 bytes and the buffer size when it returns.

Lucero
  • 59,176
  • 9
  • 122
  • 152
0

Take a look on the following link: http://blog.stephencleary.com/2009/04/tcpip-net-sockets-faq.html

In this link you will find excelent info on sockets and also a library.

The one thing I would make sure to implement is a timeout mechanism in order to avoid getting stuck when the network goes bad.

Ben Gribaudo
  • 5,057
  • 1
  • 40
  • 75
Klinger
  • 4,900
  • 1
  • 30
  • 35
0

it sounds like the yield statement could suit this scenario just fine.

i.e. say there's a loop watching the incomming stream, and once you hit each length number, you give back control to the caller via 'yield' to IEnumerable / foreach.

Perhaps the yield could in turn signal via an event for an alternative decoupling to IEnumerable. I find IEnumerable to be convenient though.

sgtz
  • 8,849
  • 9
  • 51
  • 91
-1

well, kind stinks we have to write a loop but I would have liked nonetheless if someone put a loop in this forum post so I could have cut and paste it real quick...here was mine(and yeah, the i thing I would rather base in time but it is only for unit tests and they control a simulated server so I don't care that much+ I don't want my unit test to get stuck on a failure when it calls my Read method)

        int numBytes = 0;
        int i = 0;
        while(numBytes != length)
        {
            numBytes += latestClient.Receive(body, numBytes, length-numBytes, SocketFlags.None);                
            if(i == 10000)
                throw new Exception("Could not read enough data.  read="+numBytes+" but expected="+length);
        }
Dean Hiller
  • 19,235
  • 25
  • 129
  • 212