Skip to content

Networking in C# — Complete Guide

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Networking in C#. We cover key concepts, practical examples, and best practices to help you master this topic.

Hook

Modern applications communicate over networks. C# provides comprehensive networking APIs ranging from high-level HttpClient for REST calls to low-level sockets for custom protocols. Whether you are building Microservices, real-time applications, or IoT solutions, mastering networking in .NET is essential.

Learning Path

graph LR
  A[Networking] --> B[HttpClient]
  A --> C[gRPC]
  A --> D[SignalR]
  B --> E[Best Practices]
  C --> F[Protocol Buffers]
  D --> G[Real-time]
  style A fill:#4a90d9,color:#fff
  style B fill:#4a90d9,color:#fff
  style C fill:#4a90d9,color:#fff
  style D fill:#4a90d9,color:#fff
  style E fill:#4a90d9,color:#fff
  style F fill:#4a90d9,color:#fff
  style G fill:#4a90d9,color:#fff

HttpClient

HttpClient is the primary class for making HTTP requests.

using System;
using System.Net.Http;
using System.Net.Http.Json;

public class ApiClient
{
    private readonly HttpClient _httpClient;

    public ApiClient()
    {
        // Always reuse HttpClient instances
        _httpClient = new HttpClient
        {
            BaseAddress = new Uri("https://api.github.com"),
            Timeout = TimeSpan.FromSeconds(30),
            DefaultRequestHeaders =
            {
                { "User-Agent", "MyApp/1.0" },
                { "Accept", "application/json" }
            }
        };
    }

    public async Task<T?> GetAsync<T>(string endpoint)
    {
        var response = await _httpClient.GetAsync(endpoint);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<T>();
    }

    public async Task<T?> PostAsync<T>(string endpoint, object data)
    {
        var response = await _httpClient.PostAsJsonAsync(endpoint, data);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<T>();
    }

    // Streaming large responses
    public async Task ProcessStreamAsync(string endpoint)
    {
        var response = await _httpClient.GetAsync(endpoint,
            HttpCompletionOption.ResponseHeadersRead);

        using var stream = await response.Content.ReadAsStreamAsync();
        using var reader = new StreamReader(stream);

        string? line;
        while ((line = await reader.ReadLineAsync()) != null)
        {
            Console.WriteLine($"Received: {line}");
        }
    }
}

public record GitHubUser(string Login, int Id, string? Name);

HttpClientFactory

Use IHttpClientFactory for resilient HTTP clients in production.

// In Program.cs
builder.Services.AddHttpClient("GitHub", client =>
{
    client.BaseAddress = new Uri("https://api.github.com");
    client.DefaultRequestHeaders.UserAgent.ParseAdd("MyApp/1.0");
})
.AddTransientHttpErrorPolicy(policy =>
    policy.WaitAndRetryAsync(3, retryAttempt =>
        TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))))
.AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(
    TimeSpan.FromSeconds(10)));

// In service
public class GitHubService
{
    private readonly HttpClient _httpClient;

    public GitHubService(IHttpClientFactory httpClientFactory)
    {
        _httpClient = httpClientFactory.CreateClient("GitHub");
    }

    public async Task<GitHubUser?> GetUserAsync(string username)
    {
        return await _httpClient.GetFromJsonAsync<GitHubUser>($"users/{username}");
    }
}

gRPC

gRPC provides high-performance remote procedure calls using Protocol Buffers.

// greet.proto
syntax = "proto3";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}
// Server
public class GreeterService : Greeter.GreeterBase
{
    public override Task<HelloReply> SayHello(HelloRequest request,
        ServerCallContext context)
    {
        return Task.FromResult(new HelloReply
        {
            Message = $"Hello {request.Name}"
        });
    }
}

// Client
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(new HelloRequest { Name = "World" });
Console.WriteLine(reply.Message);

SignalR

SignalR enables real-time web functionality.

// Hub
public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }

    public override async Task OnConnectedAsync()
    {
        await base.OnConnectedAsync();
        Console.WriteLine($"Client connected: {Context.ConnectionId}");
    }
}

// Server setup
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSignalR();
var app = builder.Build();
app.MapHub<ChatHub>("/chat");
app.Run();

// Client (C#)
var connection = new HubConnectionBuilder()
    .WithUrl("https://localhost:5001/chat")
    .Build();

connection.On<string, string>("ReceiveMessage", (user, message) =>
{
    Console.WriteLine($"{user}: {message}");
});

await connection.StartAsync();
await connection.InvokeAsync("SendMessage", "C# Client", "Hello SignalR!");

Raw Sockets

For custom protocols, use TcpClient and UdpClient.

// TCP Server
var listener = new TcpListener(IPAddress.Any, 5000);
listener.Start();
Console.WriteLine("Server started on port 5000");

while (true)
{
    using var client = await listener.AcceptTcpClientAsync();
    using var stream = client.GetStream();
    using var reader = new StreamReader(stream);
    using var writer = new StreamWriter(stream);

    string? data = await reader.ReadLineAsync();
    Console.WriteLine($"Received: {data}");
    await writer.WriteLineAsync($"Echo: {data}");
}

// TCP Client
using var tcpClient = new TcpClient();
await tcpClient.ConnectAsync("localhost", 5000);
using var stream = tcpClient.GetStream();
using var writer = new StreamWriter(stream) { AutoFlush = true };
using var reader = new StreamReader(stream);

await writer.WriteLineAsync("Hello TCP Server");
string? response = await reader.ReadLineAsync();
Console.WriteLine(response);

DNS and IP

Resolve hostnames to IP addresses.

var host = await Dns.GetHostEntryAsync("google.com");
Console.WriteLine($"Host: {host.HostName}");
foreach (var ip in host.AddressList)
    Console.WriteLine($"  IP: {ip}");

var localIp = Dns.GetHostEntry(Dns.GetHostName())
    .AddressList
    .FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork);
Console.WriteLine($"Local IP: {localIp}");

Common Mistakes

  1. Not reusing HttpClient: Creating a new HttpClient for each request exhausts socket ports. Use IHttpClientFactory or a single static instance.

  2. Ignoring cancellation: Always pass CancellationToken to network calls to support graceful shutdown.

  3. Not handling transient failures: Network calls fail. Use Polly for retry policies with exponential backoff.

  4. Blocking on async calls: Using .Result or .Wait() on Task causes thread pool starvation and potential deadlocks.

  5. Forgetting to dispose network resources: TcpClient, NetworkStream, and HttpResponseMessage should be disposed properly.

Practice Questions

  1. Write a download manager that downloads multiple files concurrently using HttpClient and reports progress.

  2. Implement a chat application using SignalR with group support for private messaging.

  3. Create a gRPC service for a product catalog with CRUD operations.

  4. Challenge: Build a port scanner using TcpClient that scans a range of IP addresses and ports asynchronously.

FAQ

What is the difference between HttpClient and WebClient?

WebClient is obsolete. HttpClient is the modern, feature-rich replacement with async support, headers, and streaming.

When should I use gRPC instead of REST?

gRPC is ideal for high-performance internal microservices, streaming, and polyglot environments. REST is better for browser clients and public APIs.

How do I handle WebSocket connections in C#?

Use System.Net.WebSockets.ClientWebSocket for client connections or Microsoft.AspNetCore.WebSockets for server-side.

What is the best way to handle network timeouts?

Set HttpClient.Timeout and CancellationToken. Combine with Polly timeout policies for fine-grained control.

Can I use SignalR from non-.NET clients?

Yes, SignalR has JavaScript, Java, and Python clients. The protocol is also spec-compliant for custom implementations.

Mini Project: Real-Time Stock Ticker

Build a SignalR hub that broadcasts simulated stock prices to connected clients.

using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Concurrent;

public class Stock
{
    public string Symbol { get; set; } = "";
    public decimal Price { get; set; }
    public decimal Change { get; set; }
}

public class StockTickerHub : Hub
{
    private static readonly Random Rng = new();
    private static readonly ConcurrentDictionary<string, decimal> Stocks = new()
    {
        ["AAPL"] = 150.00m,
        ["GOOGL"] = 2800.00m,
        ["MSFT"] = 310.00m,
        ["AMZN"] = 3400.00m
    };

    public override async Task OnConnectedAsync()
    {
        await Clients.Caller.SendAsync("StocksInitial", GetStocks());
        await base.OnConnectedAsync();
    }

    private List<Stock> GetStocks()
    {
        return Stocks.Select(kvp => new Stock
        {
            Symbol = kvp.Key,
            Price = kvp.Value,
            Change = 0
        }).ToList();
    }

    public async Task StartTicker()
    {
        while (true)
        {
            await Task.Delay(1000);

            foreach (var symbol in Stocks.Keys)
            {
                var change = (decimal)(Rng.NextDouble() - 0.5) * 5;
                Stocks[symbol] = Math.Max(1, Stocks[symbol] + change);
            }

            var updates = Stocks.Select(kvp => new Stock
            {
                Symbol = kvp.Key,
                Price = Math.Round(kvp.Value, 2),
                Change = Math.Round(kvp.Value - 150, 2)
            }).ToList();

            await Clients.All.SendAsync("StocksUpdated", updates);
        }
    }
}

// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSignalR();
var app = builder.Build();
app.MapHub<StockTickerHub>("/stockticker");
app.Run();

Networking in C# spans from simple HTTP calls to complex real-time systems. The .NET ecosystem provides battle-tested libraries for every networking scenario, enabling you to build Distributed Systems with confidence.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro