I use Net.Sockets.Socket class to write a TCP server. Since TCP operates on streams, one needs an approach to seperate messages from each other. (For details, see the Message Framing post of Stephen Cleary in his blog here.)
What I want to achieve is writing a TCP server class with the support for custom message framing protocols. An example initialization of this class is here:
var receiveDelimiter = Encoding.UTF8.GetBytes("[END]");
var sendDelimiter = Encoding.UTF8.GetBytes("\r\n");
var protocol = new DelimiterFramingProtocol(receiveDelimiter, sendDelimiter);
var server = new Server(protocol);
server.Start(port);
The protocol should be derived from the abstract class MessageFramingProtocol and the server should be able to use it to seperate messages. In the example above, the server should only fire its DataReceived event if the delimiter (which is “[END]”) is received and the arguments of DataReceived should only have the part of the message that is before the delimiter. If there are more bytes received after the delimiter, the server should store them and fire DataReceived only when the delimiter is received again. Server also should send the sendDelimiter after every message that it sends.
What I need is not this whole server class or any of the protocol classes. What I need is a template, a design advice. Assuming I have a property of type FramingProtocol called Protocol in the server class, how can I use it in receiving and sending operations in the Server class? What abstract methods / properties it should have to provide the flexibility that you see above? I should be able to write custom protocol classes that derive from FramingProtocol. They may use delimiters, length-prefixing, both of them or other, custom approaches to seperate messages.
I wouldn’t go with only one Protocol instance that is passed to the server – it will need lots of them. Provide the server with a factory class that either creates new Protocol instances or depools them from a pool created and filled at startup.
What I usually do is something like this:
RX:
Provide an ‘int Protocol::addBytes(char *data,int len)’ function. Fed with the address and length of raw rx data, the function returns either -1, (means that it has consumed all the raw data without fully assembling a protocol unit), or a positive integer that is the index of data consumed at the point it assembled a valid PDU. If the instance manages to assemble a PDU, it can be further processed, (eg. fired into a ‘DataReceived(Protocol *thisPDU)’ event and a new Protocol instance created, (or depooled), and loaded up with the remaining raw data.
TX:
Provide, (quite possibly overloaded), ‘bool Protocol::loadFrom(SomeDataClass * outData, OutStreamClass *outStream)’ methods that can load data from whatever source into internal member vars so that a complete set of data exists to generate a serialized PDU, (and return false, or raise an exception if there is some issue – eg. provided data fails sanity-check). If no error is detected, the instance drives the serialized data out of the passed ‘outStream’ stream/socket/buffer+len.