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
- Verwenden Sie HttpClient wieder (Singleton oder
IHttpClientFactory). - Verwalten Sie Timeouts und Wiederholungsrichtlinien mit der Polly-Bibliothek.
- Verwenden Sie in asynchronen Methoden
ConfigureAwait(false), um Deadlocks zu vermeiden. - Wenn Sie TcpClient verwenden, schließen Sie Ressourcen mit
usingundawaitkorrekt. - Fügen Sie immer Fehlerbehandlung und Logging für Netzwerk-IO-Vorgänge hinzu.
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 SieTimeout- und Fehlerbehandlung.
Ähnliche Artikel
Asynchrone Programmierung in C# – Grundlagen (async/await)
Lernen Sie async und await in C#, um reaktionsfähige Anwendungen mit asynchronen Tasks und Beispielen zu entwickeln.
Methoden und Parameterverwendung in C#
Lernen Sie Methoden und die Verwendung von Parametern in C#, einschließlich Wert- und Referenzparametern sowie optionalen Parametern.
Visual Studio / VS Code Tipps für C#
Lernen Sie Visual Studio und VS Code Tipps für C#, um produktiver mit Shortcuts und Tools zu arbeiten.