If you want two network devices to talk to each other, then developing a TCP server and client is as low as you can get without blowing your brains off. It is really helpful in many scenarios.
- Come in handy when all other application layer protocols like HTTP, FTP etc doesn’t suit your needs
- If you wish to develop your own application protocol for server/client communication
- Lets you really optimize the communication at every bit level
- It could be faster and consumes less memory as you will tailor it to your own needs and constraints
In this article we will see how we can develop our own TCP client and server.
Server
We will create a TCP endpoint and then a TCP listener that keeps listening on the endpoint. Since the program must never terminate and should keep listening, we will use an infinite while loop. As soon as we receive a new message on the listener, we process that and send the appropriate response.
// server.cs static void Main(string[] args) { Console.WriteLine("Server starting !"); // IP Address to listen on. Loopback in this case IPAddress ipAddr = IPAddress.Loopback; // Port to listen on int port = 8081; // Create a network endpoint IPEndPoint ep = new IPEndPoint(ipAddr, port); // Create and start a TCP listener TcpListener listener = new TcpListener(ep); listener.Start(); Console.WriteLine("Server listening on: {0}:{1}", ep.Address, ep.Port); // keep running while(true) { var sender = listener.AcceptTcpClient(); // streamToMessage - discussed later string request = streamToMessage(sender.GetStream()); if (request != null) { string responseMessage = MessageHandler(request); sendMessage(responseMessage, sender); } } } private static void sendMessage(string message, TcpClient client) { // messageToByteArray- discussed later byte[] bytes = messageToByteArray(message); client.GetStream().Write(bytes, 0, bytes.Length); } public static string MessageHandler(string message) { Console.WriteLine("Received message: " + message); return "Thank a lot for the message!"; }
Client
Let us now turn our attention to the client. The aim on the client side is to be able to send message to and get the response from the TcpListener listening on the server side. So, we need to create a TcpClient the binds to the server’s IP address and port. We get the client stream and then write to it and then wait for the response data to arrive on the stream. Check it out below in action.
// client.cs static void Main(string[] args) { string requestMessage = "Please process this message!"; string responseMessage = sendMessage(requestMessage); Console.WriteLine(responseMessage); } public static string sendMessage(string message) { string response = ""; try { TcpClient client = new TcpClient("127.0.0.1", 8081); // Create a new connection client.NoDelay = true; // please check TcpClient for more optimization // messageToByteArray- discussed later byte[] messageBytes = messageToByteArray(message); using (NetworkStream stream = client.GetStream()) { stream.Write(messageBytes, 0, messageBytes.Length); // Message sent! Wait for the response stream of bytes... // streamToMessage - discussed later response = streamToMessage(stream); } client.Close(); } catch (Exception e) { Console.WriteLine(e.Message); } return response; }
In both server and in the client side, we are yet to implement 2 methods namely:
- messageToByteArray: to convert our message string into a byte array that can be written on the network stream.
- streamToMessage: to read message from the stream and convert it to a string message.
But before we do that, we need to turn our attention to the content length of the message.
Content length
To read from the stream, we either need to read byte by byte or we can read the entire message in one go. We would prefer the later and for that we need to have a buffer. Now buffer can be fixed to a size bigger than the longest message we expect to receive. But that would be sub optimal way as it increases the reading time and thus the communication time. Therefore, we need to device a way of letting the other party know what is the message length so the other party can accordingly set the buffer size.
Since both server and client are our own, we can device our own protocol to send content length data. Let us say that the first 4 bytes (32 bits) would contain the length of the message. Thus we are setting that all messages would at least be 4 bytes in length so that the other party can be assured that they can safely read 4 bytes to get the message length. Once the message length is received, the buffer can be sized accordingly and the rest of the message stream be read. While on the sender side, the rest of the message will be appended after the first 4 bytes. Let us see this in practice by defining the 2 methods we listed above:
// using UTF8 encoding for the messages static Encoding encoding = Encoding.UTF8; private static byte[] messageToByteArray(string message) { // get the size of original message byte[] messageBytes = encoding.GetBytes(message); int messageSize = messageBytes.Length; // add content length bytes to the original size int completeSize = messageSize + 4; // create a buffer of the size of the complete message size byte[] completemsg = new byte[completeSize]; // convert message size to bytes byte[] sizeBytes = BitConverter.GetBytes(messageSize); // copy the size bytes and the message bytes to our overall message to be sent sizeBytes.CopyTo(completemsg, 0); messageBytes.CopyTo(completemsg, 4); return completemsg; } private static string streamToMessage(Stream stream) { // size bytes have been fixed to 4 byte[] sizeBytes = new byte[4]; // read the content length stream.Read(sizeBytes, 0, 4); int messageSize = BitConverter.ToInt32(sizeBytes, 0); // create a buffer of the content length size and read from the stream byte[] messageBytes = new byte[messageSize]; stream.Read(messageBytes, 0, messageSize); // convert message byte array to the message string using the encoding string message = encoding.GetString(messageBytes); string result = null; foreach (var c in message) if (c != '\0') result += c; return result; }
This should be it. Implement the above 2 functions on both client and server side and both should be ready to run.
0 Comments