Running a TCP Server in docker container
When we talk about having a TCP connection between 2 applications or between an application and a device, we have a dedicated socket created for each connection between a client and the server. The server listens for client connections at a specified port. Clients should be aware of the server ip address and listening port to connect to the server.
In a normal scenario, the TCP server runs on a host machine and uses the host port. But if we plan to run the TCP server application in a docker container we need to consider the following points.
- The port inside the container that the TCP server going to listen for client connections should be exposed.
- We need to map the container port to a host port.
Implementation
The example implementation is done using c#.Net and dotnet core.
- Create a dotnet core console application and name it as TCPServer.
- The program.cs file is as follows
class Program
{
static void Main(string[] args)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 3077);
TcpListener server = new TcpListener(ep);
server.Start(); // start the server
Console.WriteLine($”TCP server in docker started with ip: {ep.Address}, port: {ep.Port}. Waiting for connection…..”);
while (true)
{
const int size = 1024 * 1024;
string message = null;
byte[] buffer = new byte[size];
var sender = server.AcceptTcpClient();
sender.GetStream().Read(buffer, 0, size);
// Read the message
message = Encoding.Unicode.GetString(buffer);
Console.WriteLine($”Message received: {message}”);
byte[] bytes = System.Text.Encoding.Unicode.GetBytes(“Reply message : ” + message);
sender.GetStream().Write(bytes, 0, bytes.Length); // Send the reply
}
}
}
- As the TCP server will run inside a docker container, lets add docker support to the project and the Dockerfile is defined as below. The highlighted line allows the 3077 port to be exposed from the container.
FROM mcr.microsoft.com/dotnet/core/runtime:2.2-stretch-slim AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
WORKDIR /src
COPY TcpServer.csproj .
RUN dotnet restore TcpServer.csproj
COPY . .
RUN dotnet build “TcpServer.csproj” -c Release -o /app
FROM build AS publish
RUN dotnet publish “TcpServer.csproj” -c Release -o /app
FROM base AS final
WORKDIR /app
COPY –from=publish /app .
EXPOSE 3077
ENTRYPOINT [“dotnet”, “TcpServer.dll”]
- Next step is to build the project with the command
“docker build -t tcpserver:latest .”
- Let’s tag the image as
“docker tag tcpserver:latest localhost:5000/tcpserver:latest”
- Then run the container with the below command. Here we are asking the docker engine to run the container by mapping the host ip 3077 to container ip 3077.
“docker run -i -t -p 3077:3077 localhost:5000/tcpserver:latest”
The output will be as below. The tcp server will start inside the container and wait for client connections.
TCP server in docker started with ip: 0.0.0.0, port: 3077. Waiting for connection…..
- To test the tcp server in container, lets create a client which is a dotnet core console application. The program .cs is as below.
class Program
{
static void Main(string[] args)
{
Console.WriteLine(“TCP Client Starting…..”);
while (true)
{
var message = Console.ReadLine();
var bytes = System.Text.Encoding.Unicode.GetBytes(message);
SendMessage(bytes);
}
}
private static byte[] SendMessage(byte[] messageBytes)
{
const int bytesize = 1024 * 1024;
try
{
System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient(“127.0.0.1”, 3077);
NetworkStream stream = client.GetStream();
stream.Write(messageBytes, 0, messageBytes.Length);
Console.WriteLine(“Connected to server.”);
messageBytes = new byte[bytesize];
// Receive the stream of bytes
stream.Read(messageBytes, 0, messageBytes.Length);
var messageToPrint = System.Text.Encoding.Unicode.GetString(messageBytes);
Console.WriteLine(messageToPrint);
stream.Dispose();
client.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return messageBytes; // Return response
}
}
- Now let’s run the client in console with command dotnet run TcpClient.dll. The output in the command prompt should be as below
TCP Client Starting…..
Type a message and hit Enter to start sending messages to the server in the client screen. In this case the server receives the message from client and replies it back.
9. In the above case we had kept the host and container ports same while running the container. It can be different.
“docker run -i -t -p 3076:3077 localhost:5000/tcpserver:latest”
In this case host port 3076 is mapped to container port 3077. So now the tcp client must connect to port 3076 to send and receive messages.