Building a TCP Server In .NET Core On Ubuntu

AIntroduction and Background

TCP programming is one of the most interesting sections, as far as work is concerned, in network programming. On the Ubuntu environment, I enjoy doing TCP programming using .NET Core and the native Ubuntu scripts to communicate with the TCP Servers. Previously, I wrote an article about TCP Servers and clients in .NET framework itself. Now, .NET framework itself is going to be open source. I wanted to write something about the communication channels between both of them. Basically, I am just testing what works in the new .NET environment, as compared to how it was working in the old, yet the real, .NET framework environment.

In this post, however, I have a bunch of extras available for you. I will be showing you the methods that you are going to use to build your own TCP Server, using .NET Core assemblies and how you are going to communicate with them over the network. For the client applications, I won’t be building anything at all. Instead, I will be using the native scripts that allow communication over TCP and UDP protocols.

The agenda is something like this:

  1. Building and hosting a TCP Server with built in .NET Core
  2. Initiating requests and sending the text-data and file-data to the Server, using a TCP client-native script.

These are a few things that I will walk you through in this post and I will clarify the purpose of everything as well.

We are not going to build any special application here, but TCP is the most widely-used protocol in Transport layer (in contrast to UDP) and most of the Applications and Services that we use regularly rely on this protocol. HTTP, FTP, SMTP and all other similar protocols rely directly on TCP and use the sockets (ports) for the communication, based on TCP protocols. Hence, you can kickstart your Server from a minimal program and build a vast Enterprise Application on top of it. It can scale as much as needed by the Application programming framework and the clients (or users), who are connected to it.

I am, however, not going to talk about building those complex applications, but I will give you an easy overview of the TCP Server and the client based communication. Why write this post? I will be covering TCP consumption at a cross-platform environment. I will just create the Server using .NET Core. The rest will be done natively and I will use the “nc” command in Ubuntu to communicate with the server that I have just created. Hence, the idea is to allow you to understand how you can build a Server in .NET Core for efficiency and control and then how you can communicate with that Server, using multiple platforms and their Services, that allow the users to communicate with TCP servers. So, let’s get started.

Building the server-side

In .NET environments, System.Net namespaces contains all of the reference material that may be used for learning and programming purposes. On the Server-side programming tasks, I am going to use the TcpListener object to handle the incoming TCP traffic. Basically, this is a native TCP part and you will be getting used to it quickly. I have been using this object and the programs that consume this object in their lifetime for many purposes in my own personal applications and on a few network-based applications, where client applications, devices and machines are able to communicate with my TCP Server through the router (modem, or the WLAN hotspot etc.). This way, I can create a central Server that communicates using TCP protocols and the clients who can communicate using TCP can also send or receive data from this Server. I will however not go into the depth of submitting and transmitting the data over the network in many formats. For the sake of simplicity, I will keep things very simple and straightforward, due to which I will have to work in a plain-text format.

The basic demonstration of how a Server-client communication works in a TCP environment is demonstrated in the following bitmap graphic art, that I created previously:

Server-client setup
                                 Figure 1: Server-client setup

As shown, the Server controls the way the resources are being used and consumed. The clients can use any device which can communicate using TCP protocol over the network. The Server handles the requests and grants an access to the resources. Just like interacting with any other program, TCP clients can interact with the TCP Servers, send commands, send requests, send option selections (such as selecting which service to trigger) and so on. TCP Servers then handle these requests and generate the response based on those requests and the values for the data that are being passed. TCP servers can:

  1. Handle incoming requests over TCP protocol.
  2. Run in the background as Services; which other Servers can also do.
  3. Send the data resources on the wire as a stream of the bytes.

    • Although TCP servers have been thought of a text-only framework which shares the messages, it is actually not the messages, but the bytes, that are sent. 
    • The sequence in TCP communication is actually of the bytes that are sent, not the messages. So, the clients should be able to handle incoming bytes.
     
  4. Provide the reliable, ordered and error-checked methods and mechanisms to communicate on the network and not the server we are creating, but the protocol.

In .NET Core, TCP listeners allow you to handle the available number of bytes, by using a static buffer of a fixed size. Sometimes, the data may not be sent to fill the buffer or sometimes the buffer may not be enough to be able to cover all of the buffer.

The source code for running the Server is just as simple as:

  1. Instantiating the object.
  2. Setting up the IP address to listen at and the port to listen at.
  3. Waiting for the clients to connect.

This step hosts the Server. At this stage, the program that hosts the Server should be kept active and running otherwise the program would terminate, closing the Server itself. Have a look at the code, given below:

  1. IPAddress address = IPAddress.Parse("127.0.0.1");  
  2. listener = new TcpListener(address, port);  
  3.   
  4. listener.Start();  
  5.   
  6. // Code to stop the server and use the  
  7. // listener.AcceptTcpClientAsync();  
  8. // Function to get the connecting client  
This code does the job that we need to do on the Server side; i.e., host it. I used the following code to actually denote that the Server is running and is active at the moment. Server itself doesn’t show as running.
  1. Console.WriteLine($"Server started. Listening to TCP clients at 127.0.0.1:{port}");  
After this stage, we need to stop the Server from terminating itself. Instead of keeping it on the same function and after this line of code, I created a separated function and added the code there to wait for the clients. Our class has two functions that it performs, 
  1. Starts the server
  2. Listens to clients

It is the second function that does most of the jobs in the program. This is the place, where we can add asynchronous patterns and multi threading to support multiple clients to connect at the same time. I am, however not going to dive any deeper in that section. So, the proper code to actually start up the Server and listen to the clients is shown below:

  1. using System;  
  2. using System.Net;  
  3. using System.Text;  
  4. using System.Net.Sockets;  
  5.   
  6. namespace ConsoleApplication
  7.  {  
  8.     class TcpHelper
  9.        {  
  10.         private static TcpListener listener { getset; }  
  11.         private static bool accept { getset; } = false;  
  12.    
  13.         public static void StartServer(int port) {  
  14.             IPAddress address = IPAddress.Parse("127.0.0.1");  
  15.             listener = new TcpListener(address, port);  
  16.    
  17.             listener.Start();  
  18.             accept = true;  
  19.    
  20.             Console.WriteLine($"Server started. Listening to TCP clients at 127.0.0.1:{port}");  
  21.         }  
  22.    
  23.         public static void Listen()
  24.        {  
  25.             if(listener != null && accept) 
  26.             {  
  27.    
  28.                 // Continue listening.  
  29.                 while (true)
  30.                  {  
  31.                     Console.WriteLine("Waiting for client...");  
  32.                     var clientTask = listener.AcceptTcpClientAsync(); // Get the client  
  33.    
  34.                     if(clientTask.Result != null)
  35.                       {  
  36.                         Console.WriteLine("Client connected. Waiting for data.");  
  37.                         var client = clientTask.Result;  
  38.                         string message = "";  
  39.    
  40.                         while (message != null && !message.StartsWith("quit"))
  41.                          {  
  42.                             byte[] data = Encoding.ASCII.GetBytes("Send next data: [enter 'quit' to terminate] ");  
  43.                             client.GetStream().Write(data, 0, data.Length);  
  44.    
  45.                             byte[] buffer = new byte[1024];  
  46.                             client.GetStream().Read(buffer, 0, buffer.Length);  
  47.    
  48.                             message = Encoding.ASCII.GetString(buffer);  
  49.                             Console.WriteLine(message);  
  50.                         }  
  51.                         Console.WriteLine("Closing connection.");  
  52.                         client.GetStream().Dispose();  
  53.                     }  
  54.                 }  
  55.             }  
  56.         }  
  57.     }  
  58. }  
This code starts the Server and continues to wait for a new client. Once the client is connected, it asks for the data from the client and posts that data until the client sends “quit” to the Server. Basically, it is a very simple yet powerful Server example to be used for educational purposes. Of course, we now need to call these functions from the main function too, in order to run the project.
  1. public static void Main(string[] args)  
  2. {  
  3.     // Start the server  
  4.     TcpHelper.StartServer(5678);  
  5.     TcpHelper.Listen(); // Start listening.  
  6. }  
We are finished doing the Server side scripting at the moment. The Server is set up and will listen to the client requests and the data, they are going to pass onto our Server. We now need to run the program and start the Server for listening to the requests. I am not going into the depth of the .NET Core execution processes and building processes; as for that, read my other post:  

To run the program, just do the following,

dotnet run

The output was,

Server
                                   Figure 2: Server started

As you can see, the TCP Server started at the IP, and we specified the port that we passed in the function call.

Leaving the rest to client-side

As I had talked about before, I will be using a native TCP client instead of the TCPClient object of .NET core, so that you can use the server even when you don’t have .NET installed on the system (such as in cases of using TCPClient object of .NET framework). In this post, I am going to use “netcat” command script in Unix (Linux and derivative) systems. The communication can be done simply. You can learn more about this script from the online resources as I don’t want to dig any deeper. I just want to demonstrate the usage. To learn more, you can go to any of the following URLs and learn more about this:

  1. nc - Unix, Linux Command
  2. How To Use Netcat to Establish and Test TCP and UDP Connections on a VPS
  3. nc(1) - Linux man page

You will be able to understand how to use this command to act as a client. This also allows you to create a program script that acts as a Server and listens to the requests on the network, but we are not interested in those factors. Instead, we are just looking forward to sending a request to the Server and passing on some data that will be shown on the screen.


Connecting and sending data to the Server:

The first step is to connect to the TCP server. We need two things to make the connection,
  1. IP address
  2. Port to connect at

We will pass them as the arguments to the command,

$ nc 127.0.0.1 5678

The following screen appears (our Server takes the request and returns a message)

Client connected
                                      Figure 3: Client connected

(Look at the code for our Server for more on this.)

Next, we can send the data from this terminal, as it is asking for more data. We will send few bytes of the data and then send ‘quit’ to terminate the connection. Let’s see how that works in the context.

Client sending the data
                      Figure 4: Client sending the data to the server

We sent three messages to the Server. All of them were checked and finally on the last message, the stream was closed and the terminal started asking for more commands (instead of asking for more data). Why is that so? If you pay attention, you will see that the “quit” command has been set to the terminator of the connection. When we send this command, the connection is terminated and we need to start the connection once again. Now, let’s see on the other side.

Server-side message logging
                              Figure 5: Server-side message logging

See how the behavior changes here with each event. Once our client connects, there is a message being logged here: “Client connected. Waiting for data.” This message is shown, when the client is connected. You can also log the time and other details for that client. We are passed with the data and after the “quit”, you can see there is a log of “Closing connection.” It should have also been shown on the other end, but sadly, I missed this point. You must have gotten the idea of using the clients to connect to the Server and send the data.

Points of Interest

Finally, this post was a short introduction to TCP programming in .NET Core and how you can abstract the entire concept of this, by using other platform services to communicate with the Servers.

Next Recommended Readings