Once you have established a connection, the client and server must constantly monitor their NetworkStream objects for new data. When data is received, it should be added to some data buffer until you get enough to compose a complete message. After receiving the appropriate amount of data, you can try to parse the message and respond accordingly.
Essentially, you need to tweak the logic, which looks something like this:
var data = new byte[1024]; var dataLength = 0; var dataBuffer = new MyCustomDataBuffer(); while (true) { while (stream.DataAvailable) { dataLength = stream.Read(data, 0, data.Length); dataBuffer.Append(data, dataLength); while (dataBuffer.ContainsCompleteMessage()) { dataBuffer.ProcessMessage(); } } }
The control flow of this example is greatly simplified, but it should get this idea. I also do not provide an implementation for MyCustomDataBuffer , but writing such a class is not too difficult; all that really is is a stream of bytes.
How do we know if we have received the full message? Well, thatβs the point of the protocol: establish rules that let us know these things. Consider a simple data protocol example where each message consists of two parts:
- 2 bytes of the header, which mean the total size of the message in bytes
- The arbitrary number of bytes that make up the message string
In this protocol, we know that all messages have a length of at least two bytes (the theoretical smallest allowed message is 00 02 , which is an empty string). We also know that the first two bytes of the message tell us the total size of the message, so we will know when to stop reading data.
So, to implement the ContainsCompleteMessage() method from the above code, we need:
- Make sure that the data buffer has at least 2 bytes;
- Convert these two bytes to an integer;
- And determine if there are at least as many bytes in our data buffer.
As soon as we find out that we have the correct message, and we know how big it is, all we need to do is drop the first N bytes of our data buffer (where N is the size of the message), then pull the message line from the corresponding segment message data:
// here, 'msg' is a byte array that contains just the message data var msgString = Encoding.UTF8.GetString(msg, 2, msg.Length - 2); switch (msgString) { case "kill": KillServer(); break; // etc. }
The protocol that I describe here will be consistent with the stated need to send short lines over the network. Summarizing it, you can send more complex objects; you just need to add additional metadata that defines the layout of the message. I leave this as an exercise for the reader.