Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I handle incomplete packet buffers?

Tags:

c#

sockets

buffer

I am writing a client for a server that typically sends data as strings in 500 or less bytes. However, the data will occasionally exceed that, and a single set of data could contain 200,000 bytes, for all the client knows (on initialization or significant events). However, I would like to not have to have each client running with a 50 MB socket buffer (if it's even possible).

Each set of data is delimited by a null \0 character. What kind of structure should I look at for storing partially sent data sets?

For example, the server may send ABCDEFGHIJKLMNOPQRSTUV\0WXYZ\0123!\0. I would want to process ABCDEFGHIJKLMNOPQRSTUV, WXYZ, and 123! independently. Also, the server could send ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890LOL123HAHATHISISREALLYLONG without the terminating character. I would want that data set stored somewhere for later appending and processing.

Also, I'm using asynchronous socket methods (BeginSend, EndSend, BeginReceive, EndReceive) if that matters.

Currently I'm debating between List<Byte> and StringBuilder. Any comparison of the two for this situation would be very helpful.

like image 503
Benjamin Manns Avatar asked Mar 15 '10 01:03

Benjamin Manns


3 Answers

Read the data from the socket into a buffer. When you get the terminating character, turn it into a message and send it on its way to the rest of your code.

Also, remember that TCP is a stream, not a packet. So you should never assume that you will get everything sent at one time in a single read.

As far as buffers go, you should probably only need one per connection at most. I'd probably start with the max size that you reasonably expect to receive, and if that fills, create a new buffer of a larger size - a typical strategy is to double the size when you run out to avoid churning through too many allocations.

If you have multiple incoming connections, you may want to do something like create a pool of buffers, and just return "big" ones to the pool when done with them.

like image 125
kyoryu Avatar answered Sep 29 '22 07:09

kyoryu


You could just use a List<byte> as your buffer, so the .NET framework takes care of automatically expanding it as needed. When you find a null terminator you can use List.RemoveRange() to remove that message from the buffer and pass it to the next layer up.

You'd probably want to add a check and throw an exception if it exceeds a certain length, rather than just wait until the client runs out of memory.

(This is very similar to Ben S's answer, but I think a byte array is a bit more robust than a StringBuilder in the face of encoding issues. Decoding bytes to a string is best done higher up, once you have a complete message.)

like image 41
EMP Avatar answered Oct 03 '22 07:10

EMP


I would just use a StringBuilder and read in one character at a time, copying and emptying the builder whenever I hit a null terminator.

like image 44
Ben S Avatar answered Sep 30 '22 07:09

Ben S