[C# Socket]TCP, buffering and message parsing

Started by
1 comment, last by Annoyed 14 years, 9 months ago
Hi, Been working with some message (de)serialization to be able to send send/receive message objects over a TCP socket. It works but I would appreciate a comment or two on the general design. The issue is receiving. I prefer to have a fixed size receive buffer instead of having to to create or resize buffer between receives. This is an acceptable limitation in the context in which it is being used. When receiving there are 3 scenarios (disregarding errors/corruption whatn not). 1: I receive a full message 2: I receive a truncated message 3: I receive the tail of a previously truncated message (this is never truncated also because of size limitation). I can also receive a combination of either. First case is the trivial one; I parse message and pass it along and continue to probe for messages in buffer. Third case leads to a similar state as the first; I parse the message and pass it along and continue to probe for messages in buffer. Second case is the intersting one and I handle it as follows: I copy the bytes to the head of the byte array and then setup a receive callback starting at the successive index in buffer. Following case number 3 is bound to happen. The reason for this choice is that the receive calls don't operate on circular buffer so this is my way of simulating a circular buffer. I use BinaryWriter and BinaryReader to handle (de)serialization and they don't cope with circular buffers per default. The performance impact of the copy is bounded since the messages are currently limited to 512 bytes but average messages are less than 100 bytes. The buffer is configured large enough that a copy will never overlap bytes that are pending (de)serialization. Is this a reasonable design?
Advertisement
Depending on the size of your buffer you may just want to "not do anything" when you hit case #2. Just leave the truncated stuff in there and pick it up next pass (or the whenever you hit case #1.)

If you do have to pull it/them out then working with a circular buffer may work fine if you have hard, discrete message sizes or enough memory to throw down in your circular of 2x(largest_message_size). The worst thing you could have happen is a circular overwrite. Ouch.

You may consider just moving to a buffer-pool system, either your own or something like Boost's pool class. Then you pop off a pre-allocated chunk of memory, fill in the front with your truncated data and push onto a holder list. Keep filling in until you're at case #1 and pass the pooled-memory off to your handler. You don't incur any more memory read/writes than with your circular buffer or with case #1, but you avoid circular issues and you can use your preferred methods.

And, just as a side note... when you say "continue to probe for messages in buffer" ... if you're not using length-prefixed messages you may be losing a chunk of speed. (If you are, ignore the following. :)

#1 Read head n-bytes to get message length
#2 Compare length-data to your buffer's available data.

That'll tell you very very quickly if you have a "complete" message to retrieve.

When I hear "probe" I hear "iterative scanning every pass." If this is not that case, my apologies. :)

-Szii
Yea probe was not the best figure of speech...
Messages are serialized with a uniform header (type,sequence,length). Length storing the length of the payload in bytes.
I mean of course continue reading from buffer until no more complete messages are available.
When receiving I know how many bytes I receive and when I have read and passed all whole messages I am left possibly with a residual number of bytes as byte_received - reader_position.

You may indeed have a point that I don't have to copy every time. But for the moment It is prudent planning to avoid the circular override as you mentioned. I am looking to improve it but exactly how.

I am using the MemoryStream to the byte array and parse values from it using the BinaryReader. If I am to use a buffer pool, which does sound like a good idea, it would also complicate the appliance of the BinaryReader, Say a integer first byte is in one buffer and the remaining three in the other buffer.

Do you have any suggestions to that?

Writing my own serializer is always an option but... Never done that and the current way of (de)serialization does seem plausible so far =P

[Edited by - Annoyed on June 26, 2009 1:25:19 AM]

This topic is closed to new replies.

Advertisement