TCPListener BeginRead() doesn't read everything in VB.NET

Okay, this is a weird.

I have a simple server/client application going using TCPListener and TCPClient, written in VB.NET 2005. Like any server/client TCP program, the server starts listening, then the client connects, and then I define NetworkStream streams for both. The client and server are their own separate projects (though both projects are in the same solution).

In my case I'm running both on the same machine. The client connets to 127.0.0.1.

The client calls stream.beginwrite() to asynchronously write a large string of data (e.g. 200kb) out the networkstream to the server. Here's the call:

           ' "chunk" is a byte array that is +200kb long
            stream.BeginWrite(chunk, 0, chunk.Length, AddressOf Callback_WriteComplete, 0)

The server should see this on it's end, but the tcplistener object's "Available" property never goes higher than 8192 bytes. I figure that's as much as it will buffer in one go maybe?

But in any case, that's alright. I use stream.Read() to read in a small 16 byte "header" that I defined when I send the data from the client, part of the header describes how big the rest of the chunk's data is, so I know how much to read, and when to stop.

I already parsed the header and filled the "readSize" integer variable in with that information. Then I have the server call stream.BeginRead() to asynchronously read all the data in. Here's my beginread() call:

               ' "allDone" is an IAsyncResult object
              ' "readSize" is the already known size of the data I want to read, in the 200kb range
              ' "bytesRead" is an integer that gets filled with the amount of data that got read by the async read call.

                allDone = stream.BeginRead(data, 0, readSize, AddressOf Callback_ReadComplete, 0)
                allDone.AsyncWaitHandle.WaitOne(10000, True)
                bytesRead = stream.EndRead(allDone)

The thing is, the data() byte array only gets filled with ~15-60k of data. The exact amount varies for each call. This is reflected in the bytesRead return value. Even though I requested that it read +200kb of data, it only reads in a few kb. I have to call beginread over and over again to get everything.

Now before we blame the asynchronous reading on this behavior, I DID try it with a normal synchronous read() call. Same problem!

Seems to me like reading the data in chunk by chunk should be something that the TCPListener/Client have to worry about, not me. If I ask for 200kb of data I'd expect to receive that, especially in the synchronous read() call which sits and waits for that amount of data to become available before returning.

Is this an expected behavior? Or a bug of some kind? Or am I just doing it wrong?
LVL 31
Frosty555Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

PlatoConsultantCommented:
Set the TCPClient.ReceiveBufferSize Property



The ReceiveBufferSize property gets or sets the number of bytes that you are expecting to store in the receive buffer for each read operation. This property actually manipulates the network buffer space allocated for receiving incoming data.

Your network buffer should be at least as large as your application buffer to ensure that the desired data will be available when you call the NetworkStream.Read method. Use the ReceiveBufferSize property to set this size. If your application will be receiving bulk data, you should pass the Read method a very large application buffer.

If the network buffer is smaller than the amount of data you request in the Read method, you will not be able to retrieve the desired amount of data in one read operation. This incurs the overhead of additional calls to the Read method.

' Sets the receive buffer size using the ReceiveBufferSize public property.
tcpClient.ReceiveBufferSize = 1024 * 20
 
' Gets the receive buffer size using the ReceiveBufferSize public property.
If tcpClient.ReceiveBufferSize = 1024 * 20 Then
   Console.WriteLine(("The receive buffer was successfully set to " + tcpClient.ReceiveBufferSize.ToString()))
End If

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Frosty555Author Commented:
Awesome, you guys are good. That fixed the issue perfectly.

I still made it so that it will call BeginRead() multiple times if necessary to read all the data... just in case. But, at least 99% of the time I can expect it to only require a single call now!
0
Frosty555Author Commented:
Nevermind I implemented this using a new implementation of the Stream class called "QueueStream", which supports simultaneous reading and writing of data in the stream in a first-in-first-out matter, similar to a queue.

http://www.experts-exchange.com/Programming/Languages/.NET/Visual_Basic.NET/Q_24020005.html

0
Frosty555Author Commented:
Whoops, that comment was posted to the wrong question.

Ignore me!!
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.