Wird geladen...

Netzwerkprogrammierung in C# (TcpClient, HttpClient, REST-API-Aufrufe)

Lernen Sie Netzwerkprogrammierung in C# mit TcpClient, HttpClient und REST-API-Aufrufen für Datenaustausch.

In modernen Anwendungen spielt Netzwerkkommunikation (Networking) eine große Rolle. In C# kann sowohl mit niedrigstufigen TCP-Sockets als auch mit höherstufigen HTTP/REST-API-Aufrufen kommuniziert werden. .NET bietet dafür leistungsstarke Klassen: TcpClient, TcpListener und HttpClient. In diesem Artikel sehen wir uns Schritt für Schritt Beispiele von der grundlegenden Netzwerkprogrammierung bis zu REST-API-Aufrufen an.


1. Was ist TCP?

TCP (Transmission Control Protocol) ist ein verbindungsorientiertes und zuverlässiges Kommunikationsprotokoll. Datenpakete werden in der richtigen Reihenfolge übertragen, und fehlerhafte Pakete werden erneut gesendet. TCP arbeitet nach dem Client–Server-Modell: Eine Seite lauscht (TcpListener), die andere verbindet sich (TcpClient).


2. Einfache Server-Implementierung mit TcpListener

Das folgende Beispiel zeigt einen TCP-Server, der eingehende Verbindungen auf Port 5000 überwacht.


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

class TcpServer
{
    public static async Task StartAsync()
    {
        var listener = new TcpListener(IPAddress.Loopback, 5000);
        listener.Start();
        Console.WriteLine("Server gestartet. Lauscht auf Port 5000...");

        while (true)
        {
            using TcpClient client = await listener.AcceptTcpClientAsync();
            Console.WriteLine("Neuer Client verbunden!");

            using var stream = client.GetStream();
            using var reader = new StreamReader(stream, Encoding.UTF8);
            using var writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true };

            string? message = await reader.ReadLineAsync();
            Console.WriteLine($"Nachricht vom Client: {message}");

            await writer.WriteLineAsync($"Antwort vom Server: {message?.ToUpper()}");
        }
    }

    static async Task Main() => await StartAsync();
}

Der Server liest bei jeder neuen Verbindung die Nachricht, wandelt sie in Großbuchstaben um und sendet sie zurück. IPAddress.Loopback steht nur für den lokalen Rechner.


3. Verbindung mit TcpClient herstellen

Auf der Client-Seite kann man mit TcpClient eine Verbindung zum Server aufbauen:


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

class TcpClientApp
{
    static async Task Main()
    {
        using var client = new TcpClient();
        await client.ConnectAsync("127.0.0.1", 5000);

        using var stream = client.GetStream();
        using var writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true };
        using var reader = new StreamReader(stream, Encoding.UTF8);

        Console.Write("Nachricht: ");
        string message = Console.ReadLine()!;
        await writer.WriteLineAsync(message);

        string response = await reader.ReadLineAsync() ?? "";
        Console.WriteLine($"Antwort vom Server: {response}");
    }
}

Nachdem der Server gestartet wurde, kann dieser Client ausgeführt werden. Die gesendete Nachricht wird in Großbuchstaben zurückgegeben.


4. Web- und REST-API-Aufrufe mit HttpClient

Die heute am häufigsten verwendete Form der Netzwerkkommunikation sind HTTP-basierte REST-Dienste. In .NET wird die Klasse HttpClient für diese Aufgaben verwendet. Sie arbeitet asynchron, ist wiederverwendbar und ideal für den JSON-Datenaustausch geeignet.


using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

class HttpClientExample
{
    static readonly HttpClient http = new HttpClient();

    static async Task Main()
    {
        var url = "https://jsonplaceholder.typicode.com/posts/1";
        Console.WriteLine($"GET {url}");

        var response = await http.GetAsync(url);
        string json = await response.Content.ReadAsStringAsync();

        var post = JsonSerializer.Deserialize<Post>(json);
        Console.WriteLine($"Titel: {post?.Title}");
    }

    public class Post
    {
        public int Id { get; set; }
        public string? Title { get; set; }
        public string? Body { get; set; }
    }
}

Dieses Beispiel ruft JSON-Daten von einer REST-API ab und deserialisiert sie in eine C#-Klasse (Post). JsonSerializer ist die integrierte JSON-Bibliothek von .NET.


5. Eine POST-Anfrage senden

Mit HttpClient kann eine POST-Anfrage gesendet werden, indem man den JSON-Inhalt manuell erstellt oder Serialisierung verwendet.


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

var http = new HttpClient();

var newPost = new
{
    title = "Neuer Beitrag",
    body = "Hallo Welt!",
    userId = 1
};

var json = JsonSerializer.Serialize(newPost);
var content = new StringContent(json, Encoding.UTF8, "application/json");

var response = await http.PostAsync("https://jsonplaceholder.typicode.com/posts", content);
Console.WriteLine($"Status: {response.StatusCode}");

string result = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Antwort: {result}");

Dieses Beispiel zeigt die grundlegende Methode zum Senden von Daten an eine REST-API. In echten Anwendungen ist die Verwendung des async/await-Musters entscheidend für die Reaktionsfähigkeit.


6. HttpClient-Lebenszyklus (Best Practice)

Das Erstellen eines neuen HttpClient-Objekts für jede Anfrage führt zu unnötigem Ressourcenverbrauch. Die richtige Vorgehensweise ist, eine einzelne (Singleton-)Instanz zu verwenden:


// Program.cs
builder.Services.AddHttpClient<IMyApiService, MyApiService>();

// Service
public class MyApiService : IMyApiService
{
    private readonly HttpClient _http;
    public MyApiService(HttpClient http)
    {
        _http = http;
        _http.BaseAddress = new Uri("https://api.example.com/");
    }

    public async Task<User?> GetUserAsync(int id)
    {
        return await _http.GetFromJsonAsync<User>($"users/{id}");
    }
}

In ASP.NET Core verwaltet die HttpClientFactory automatisch das Connection Pooling.


7. Fehlerbehandlung und Timeout-Einstellungen

Bei HTTP-Anfragen sollten Timeouts, Fehler und unerwartete Ausnahmen immer behandelt werden.


var http = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };

try
{
    var response = await http.GetAsync("https://api.github.com/repos/dotnet/runtime");
    response.EnsureSuccessStatusCode(); // Wirft Ausnahme bei Status != 200–299

    string content = await response.Content.ReadAsStringAsync();
    Console.WriteLine("Erfolgreiche Anfrage: " + content.Length + " Bytes");
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"HTTP-Fehler: {ex.Message}");
}
catch (TaskCanceledException)
{
    Console.WriteLine("Zeitüberschreitung!");
}

8. SSL, Header und Token-Verwendung

Die meisten modernen APIs verwenden den Authorization-Header für Authentifizierung. Sie können Header und benutzerdefinierte Einstellungen einfach hinzufügen:


var http = new HttpClient();
http.DefaultRequestHeaders.Add("Authorization", "Bearer <token>");
http.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");

var res = await http.GetAsync("https://api.github.com/user");
Console.WriteLine(res.StatusCode);

HTTPS-Verbindungen sind standardmäßig sicher. In Entwicklungsumgebungen (z. B. selbstsignierte Zertifikate) können spezielle Handler konfiguriert werden.


9. Asynchrone Datenströme bei großen Dateien

Bei großen Dateien oder Datenströmen können Sie direkt aus dem Stream lesen, ohne alles in den Speicher zu laden:


using var http = new HttpClient();
using var response = await http.GetAsync("https://example.com/bigfile.zip", HttpCompletionOption.ResponseHeadersRead);

await using var stream = await response.Content.ReadAsStreamAsync();
using var file = File.Create("heruntergeladen.zip");

await stream.CopyToAsync(file);
Console.WriteLine("Datei heruntergeladen.");

Diese Methode nutzt den Speicher effizient, da die Datei nicht vollständig in den RAM geladen wird.


10. Beispiel: REST-API-Verbrauch (WeatherService)

Das folgende Beispiel ruft Daten von einer Wetter-API ab und wandelt sie in ein Modell um.


public class WeatherInfo
{
    public string? City { get; set; }
    public float Temperature { get; set; }
    public string? Condition { get; set; }
}

public class WeatherService
{
    private readonly HttpClient _http;
    public WeatherService(HttpClient http)
    {
        _http = http;
        _http.BaseAddress = new Uri("https://api.weatherapi.com/v1/");
    }

    public async Task<WeatherInfo?> GetAsync(string city)
    {
        var key = "<your-api-key>";
        string url = $"current.json?key={key}&q={city}&aqi=no";

        var res = await _http.GetAsync(url);
        res.EnsureSuccessStatusCode();

        using var s = await res.Content.ReadAsStreamAsync();
        using var doc = await JsonDocument.ParseAsync(s);

        return new WeatherInfo
        {
            City = city,
            Temperature = doc.RootElement.GetProperty("current").GetProperty("temp_c").GetSingle(),
            Condition = doc.RootElement.GetProperty("current").GetProperty("condition").GetProperty("text").GetString()
        };
    }
}

Diese Struktur entspricht den Prinzipien der Clean Architecture und verwendet Dependency Injection über die HttpClientFactory.


11. Leistung und Best Practices


TL;DR

  • TcpClient/TcpListener: Bieten Low-Level, verbindungsbasierte TCP-Kommunikation.
  • HttpClient: High-Level-Client für die Kommunikation mit REST-APIs.
  • GetAsync, PostAsync: Senden HTTP-Anfragen; JSON-Verarbeitung erfolgt über JsonSerializer.
  • HttpClientFactory: Empfohlen zur Leistungssteigerung und Instanzverwaltung.
  • Asynchrone Vorgänge: Verwenden Sie await, und beachten Sie Timeout- und Fehlerbehandlung.

Ähnliche Artikel