Jump to content
  • Advertisement
Sign in to follow this  
azherdev

What is the minimum size of data I should count on with TcpClient under C# + .Net 2.0

This topic is 3540 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm using the TcpClient and TcpListener in C# .Net 2.0 to create client/server architecture. On the server side, I use BeginRead/EndRead for non-blocking sockets. What I have is a header that is sent before the main payload, the header is 48 bytes long. Can I count on those 48 bytes coming through on a single BeginRead call or will I need to concatinate 31 bytes + 17 bytes, or 1 byte + 1 byte + 1 byte + etc, or whatever comes through?

Share this post


Link to post
Share on other sites
Advertisement
Most of the time, you'll get the full 48 bytes, it's not gauranteed so it's better to be safe and assume that anything can happen. For example, some of the header for the next packet might be concatenated with a previous packet.

TCP is a stream protocol, there's no concept of a "packet" so if you ask it to read x bytes, it actually means "read at most x bytes" - it could hand you anywhere between 1 and x bytes and you should account for that.

Share this post


Link to post
Share on other sites
Quote:
Original post by azherdev

Can I count on those 48 bytes coming through on a single BeginRead


No.

TCP itself cannot make such guarantees, since while 47 bytes might transfer, client could die at that point, and remaining byte could never arrive.

Blocking clients give the option to block until specified number of bytes is received, but also run into the problem of waiting indefinitely due to connection problems.

Share this post


Link to post
Share on other sites
Got it. So, if my first 4 bytes is the size it should read up to, I can't even count on that? Huh?

So, I would need to read into a buffer until I get at least 4 bytes. Then convert that to the size it needs to wait for, then wait for at least that many bytes. Etc etc.

Is 0 bytes on ReadBegin possible? Or will it not be called if 0 bytes are received in the payload of the tcp packet?

Share this post


Link to post
Share on other sites
Quote:
Original post by azherdev
Is 0 bytes on ReadBegin possible? Or will it not be called if 0 bytes are received in the payload of the tcp packet?


If Read returns 0 it means the other side has disconnected, so if it's still connected, you're always going to get at least 1 byte.

The way I normally write a TCP client is I create a "ReadExactly" function which wraps up the "keep reading until I get a complete packet" functionality. Then I can just go "ReadExactly(48)" to get the full header, then inspect the header for the length of body and ReadExactly(that_value).

Share this post


Link to post
Share on other sites
Quote:
The way I normally write a TCP client is I create a "ReadExactly" function which wraps up the "keep reading until I get a complete packet" functionality


That naive implementation will cause you grief, because it's easy to DOS your server by sending a single byte, and then keeping the connection open without sending anything. Your thread will just sit there, waiting forever.

In general, you want to treat input as a state machine. When data comes in, put it into a queue, and notify the listener for that client. The listener will check whether there's enough for whatever it needs now; if so, dequeue that much, and enter whatever the next state is; else just do nothing and wait for the next notification.

Share this post


Link to post
Share on other sites
Quote:
Original post by wingbird101
In my experience an exception is thrown during a blocking read operation if the connection is lost.


Eclipse platform 3.4, software updater I ran yesterday stopped at 54% while downloading from a HTTP source. I only noticed it only after some 30 minutes of what should be 30 second download. UI was responsive, but wouldn't shutdown, since thread didn't complete.

Reason was quite simple - server stopped sending data, but disconnect didn't come through or was never sent.

It's one of signature problems of networked applications that do not consider that under TCP connection may simply go silent without any notification whatsoever.

I'd have to say I encounter this type of problem roughly once a day in browser, it's especially problematic for HTTP. But at least browsers have gotten somewhat better at handling it. Before 2.0, Firefox would simply hang itself in such case (IIRC).

Share this post


Link to post
Share on other sites
Quote:
Got it. So, if my first 4 bytes is the size it should read up to, I can't even count on that? Huh?

So, I would need to read into a buffer until I get at least 4 bytes. Then convert that to the size it needs to wait for, then wait for at least that many bytes. Etc etc.

Yes. I use a similar length-tagged message system in my code:

int length;
copied = FillHeader(ref buf, 4, read);
if(headerread < 4) return;
length = GetInt(msgheader, 0, 4);
if(read == copied) return;

bytes.Add(buf, 0, read - copied);

if(bytes.Length >= length){
// A message was received!
headerread = 0; // reset this for the next message
byte[] msg = bytes.Read(0, length);
OnReadMessage(this, code, msg, length);

// Don't forget to put the rest through the mill
int togo = bytes.Length - length;
if(togo > 0){
byte[] whatsleft = bytes.Read(length, togo);
bytes.Clear();
ReadInternal(whatsleft, whatsleft.Length, true);
} else bytes.Clear();
}

(That's simplified because there's a load of stuff in there about encryption and different message types which obscures the point.) FillHeader copies bytes into a header variable and removes them from the front of buf.

The important point is that as well as underreading, you can also overread – so you have to call the method recursively if there is any data left over at the end.

Share this post


Link to post
Share on other sites
Quote:
Original post by hplus0603
That naive implementation will cause you grief, because it's easy to DOS your server by sending a single byte, and then keeping the connection open without sending anything. Your thread will just sit there, waiting forever.


To be clear, my implementation is typically done with asyncronous reads and a queue with callbacks when a complete packet is (finally) received. I can see how such a qualification is needed, though [smile]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!