Building Asynchronous TCP Client-Server Applications in C#

Impleemnting a Multi-Client TCP Server

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class NetworkServer
{
    private readonly TcpListener _tcpListener;
    private readonly int _listeningPort;

    public NetworkServer(int port)
    {
        _listeningPort = port;
        _tcpListener = new TcpListener(IPAddress.Any, port);
    }

    public async Task BeginListeningAsync()
    {
        _tcpListener.Start();
        Console.WriteLine($"Server started on port {_listeningPort}");

        while (true)
        {
            var incomingConnection = await _tcpListener.AcceptTcpClientAsync();
            _ = ProcessConnectionAsync(incomingConnection);
        }
    }

    private async Task ProcessConnectionAsync(TcpClient connection)
    {
        try
        {
            var endpoint = connection.Client.RemoteEndPoint;
            Console.WriteLine($"New client connected from {endpoint}");
            
            using (connection)
            using (var dataStream = connection.GetStream())
            {
                var receiveBuffer = new byte[4096];
                int dataReceived;

                while ((dataReceived = await dataStream.ReadAsync(receiveBuffer, 0, receiveBuffer.Length)) > 0)
                {
                    var receivedText = Encoding.UTF8.GetString(receiveBuffer, 0, dataReceived);
                    Console.WriteLine($"Received: {receivedText}");

                    var response = Encoding.UTF8.GetBytes($"REPLY: {receivedText}");
                    await dataStream.WriteAsync(response, 0, response.Length);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Connection error: {ex.Message}");
        }
    }
}

TCP Client Implementation

using System;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class NetworkClient
{
    private readonly TcpClient _connection;
    private readonly NetworkStream _dataStream;

    public NetworkClient(string hostAddress, int portNumber)
    {
        _connection = new TcpClient();
        _connection.Connect(hostAddress, portNumber);
        _dataStream = _connection.GetStream();
    }

    public async Task TransmitAsync(string payload)
    {
        try
        {
            var messageBytes = Encoding.UTF8.GetBytes(payload);
            await _dataStream.WriteAsync(messageBytes, 0, messageBytes.Length);
            Console.WriteLine($"Sent: {payload}");

            var replyBuffer = new byte[4096];
            int bytesRead = await _dataStream.ReadAsync(replyBuffer, 0, replyBuffer.Length);
            var serverReply = Encoding.UTF8.GetString(replyBuffer, 0, bytesRead);
            Console.WriteLine($"Server reply: {serverReply}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Transmission failed: {ex.Message}");
        }
    }

    public void Terminate()
    {
        _dataStream?.Close();
        _connection?.Close();
    }
}

Usage Examples

Server Initialization

var server = new NetworkServer(8080);
await server.BeginListeningAsync();

Client Connection

using var client = new NetworkClient("localhost", 8080);
await client.TransmitAsync("Test message from client");
client.Terminate();

Core Implementation Features

Asynchronous Communicatoin Enhancement

public async Task ManageClientConnectionAsync(TcpClient clientSocket)
{
    using (clientSocket)
    using (var stream = clientSocket.GetStream())
    {
        var buffer = ArrayPool<byte>.Shared.Rent(4096);
        try
        {
            while (true)
            {
                int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                if (bytesRead == 0) break;

                var message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                await HandleIncomingDataAsync(clientSocket, message);
            }
        }
        finally
        {
            ArrayPool<byte>.Shared.Return(buffer);
        }
    }
}

Robust Error Handling

try
{
    // Network operations
}
catch (SocketException socketEx) when (socketEx.SocketErrorCode == SocketError.ConnectionAborted)
{
    Console.WriteLine("Client abruptly disconnected");
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation cancelled by user");
}

Length-Prefixed Protocol Implemantation

// Sending with length prefix
var dataToSend = Encoding.UTF8.GetBytes("Message content");
var lengthPrefix = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(dataToSend.Length));
await networkStream.WriteAsync(lengthPrefix, 0, lengthPrefix.Length);
await networkStream.WriteAsync(dataToSend, 0, dataToSend.Length);

// Receiving with length prefix
var lengthBuffer = new byte[4];
await ReadExactAsync(networkStream, lengthBuffer, 0, 4);
var messageLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(lengthBuffer, 0));
var messageBuffer = new byte[messageLength];
await ReadExactAsync(networkStream, messageBuffer, 0, messageLength);

Performance Optimization Strategies

Memory Pool Management

var rentedBuffer = MemoryPool<byte>.Shared.Rent(4096);
try
{
    // Perform I/O operations with rentedBuffer
}
finally
{
    MemoryPool<byte>.Shared.Return(rentedBuffer);
}

Connection Pooling

public class ConnectionPoolManager
{
    private readonly ConcurrentBag<TcpClient> _availableConnections = new();
    
    public async Task<TcpClient> AcquireConnectionAsync(string host, int port)
    {
        if (!_availableConnections.TryTake(out var connection))
        {
            connection = new TcpClient();
            await connection.ConnectAsync(host, port);
        }
        return connection;
    }
    
    public void ReleaseConnection(TcpClient connection)
    {
        if (connection.Connected)
        {
            _availableConnections.Add(connection);
        }
    }
}

Concurrency Control

private readonly SemaphoreSlim _connectionLimiter = new(50);

public async Task ProcessClientAsync(TcpClient client)
{
    await _connectionLimiter.WaitAsync();
    try
    {
        // Handle client processing
    }
    finally
    {
        _connectionLimiter.Release();
    }
}

Project Architecture

TcpNetworkApplication/
├── ServerSide/
│   ├── ServerProgram.cs       // Server entry point
│   └── TcpNetworkServer.cs    // Core server logic
├── ClientSide/
│   ├── ClientProgram.cs       // Client entry point
│   └── TcpNetworkClient.cs    // Client implementation
├── Protocol/
│   └── CommunicationProtocol.cs // Message protocol definitions
└── UnitTests/
    └── NetworkTests.cs        // Test suite

Monitoring and Diagnostics

Logging Framework

public interface INetworkLogger
{
    void LogInfo(string message);
    void LogError(Exception ex, string context);
}

public class FileNetworkLogger : INetworkLogger
{
    private readonly string _logPath;
    
    public void LogInfo(string message)
    {
        File.AppendAllText(_logPath, $"[{DateTime.UtcNow}] INFO: {message}{Environment.NewLine}");
    }
}

Advanced Features

Heartbeat Mechanism

private async Task MaintainConnectionAsync()
{
    var heartbeatInterval = TimeSpan.FromSeconds(30);
    using var timer = new PeriodicTimer(heartbeatInterval);
    
    while (await timer.WaitForNextTickAsync())
    {
        try
        {
            await _dataStream.WriteAsync(Encoding.UTF8.GetBytes("PING"));
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Heartbeat failed: {ex.Message}");
            break;
        }
    }
}

Secure Communication Layer

private async Task EstablishSecureConnectionAsync()
{
    var secureStream = new SslStream(_dataStream, false);
    var sslOptions = new SslClientAuthenticationOptions
    {
        TargetHost = Dns.GetHostName(),
        EnabledSslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12,
        RemoteCertificateValidationCallback = (sender, cert, chain, errors) => true
    };
    
    await secureStream.AuthenticateAsClientAsync(sslOptions);
    return secureStream;
}

Tags: tcp C# Network Programming Async Await

Posted on Thu, 30 Jul 2026 16:59:15 +0000 by axon