I've been looking everywhere for examples on how to deal with TCP message framing. I see many examples where NetworkStreams are passed into a StreamReader or StreamWriter object and then use ReadLine or WriteLine methods for '\n' delimited messages. My application protocol contains messages ending in '\n' so the NetworkStream seems to be the way to go. However, I can't find any specific examples on the proper way to handle all of this in combination with asynchronous sockets. When ReceiveCallback() is called below, how do I implement the NetworkStream and StreamReader classes to deal with message framing? According to what I've read, I may get part of one message in one receive and the rest of the message (including the '\n') in the next receive. Does this mean I could get the end of one message and part of the next message? Surely, there must be an easier way to handle this.
I have the following code:
private void StartRead(Socket socket)
{
try
{
StateObject state = new StateObject();
state.AsyncSocket = socket;
socket.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
catch (SocketException)
{
m_Socket.Shutdown(SocketShutdown.Both);
Disconnect();
}
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
StateObject state = (StateObject)ar.AsyncState;
int bytes_read = state.AsyncSocket.EndReceive(ar);
char[] chars = new char[bytes_read + 1];
System.Text.Decoder decoder = System.Text.Encoding.UTF8.GetDecoder();
int charLength = decoder.GetChars(state.Buffer, 0, bytes_read, chars, 0);
String data = new String(chars);
ParseMessage(data);
StartRead(state.AsyncSocket);
}
catch (SocketException)
{
m_Socket.Shutdown(SocketShutdown.Both);
Disconnect();
}
}
OK here's what I ended up doing. I created a reader thread that creates a NetworkStream and a StreamReader based on the network stream. Then I use StreamReader.ReadLine to read in the lines that way. It's a synchronous call but it is in its own thread. It seems to work a lot better. I had to implement this since that's our protocol for the application (newline-delimited messages). I know other people will be looking around like hell for the answer like I did here's the relevant read code in my Client class:
Basically you create a buffer, and each time you receive data, you add that data to the buffer and determine if you have already received one or more full messages.
Between
ReceiveCallback
andStartRead
you won't receive any asynchronous messages (incoming data will automatically be buffered on the socket level) so it's the ideal place to check for full messages and remove them from the buffer.All variations are possible, including receiving the end of message 1, plus message 2, plus the beginning of message 3, all in one chunk.
I don't recommend UTF8-decoding the chunk, as one UTF8-character may consist of two bytes, and if they get split between chunks your data could be corrupted. You could keep a byte[]-buffer (
MemoryStream
?) and split messages on the 0x0A byte in that case.Prefixing the chunks with a length is better than using a separator character. You don't have to deal with any sort of escaping in order to send data with a newline that way.
This answer might not be relevant to you now, because it uses features from the AsyncCTP, which will only be in the next version of .net. However, it does make things much more concise. Essentially you write exactly the code you'd do for the synchronous case, but insert 'await' statements where there are asynchronous calls.