Networking en C# (TcpClient, HttpClient, llamadas REST API)
Aprende networking en C# con TcpClient, HttpClient y llamadas REST API para gestionar comunicación y datos externos.
En las aplicaciones modernas, la comunicación en red juega un papel fundamental.
En C#, se puede comunicar tanto con sockets TCP de bajo nivel como con llamadas HTTP/REST API de alto nivel.
.NET ofrece clases potentes para estas tareas: TcpClient, TcpListener y HttpClient.
En este artículo veremos paso a paso ejemplos que van desde la programación de red básica hasta las llamadas a APIs REST.
1. ¿Qué es TCP?
TCP (Transmission Control Protocol) es un protocolo de comunicación orientado a la conexión y confiable.
Los paquetes de datos se envían en orden y los paquetes con errores se retransmiten.
TCP funciona con el modelo cliente–servidor: una parte escucha (TcpListener) y la otra se conecta (TcpClient).
2. Servidor simple con TcpListener
El siguiente ejemplo muestra un servidor TCP que escucha conexiones entrantes en el puerto 5000.
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("Servidor iniciado. Escuchando en el puerto 5000...");
while (true)
{
using TcpClient client = await listener.AcceptTcpClientAsync();
Console.WriteLine("¡Nuevo cliente conectado!");
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($"Mensaje recibido: {message}");
await writer.WriteLineAsync($"Respuesta del servidor: {message?.ToUpper()}");
}
}
static async Task Main() => await StartAsync();
}
El servidor lee el mensaje, lo convierte a mayúsculas y lo devuelve.
IPAddress.Loopback representa solo la máquina local.
3. Cliente con TcpClient
En el lado del cliente, podemos usar TcpClient para conectarnos al servidor:
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("Mensaje: ");
string message = Console.ReadLine()!;
await writer.WriteLineAsync(message);
string response = await reader.ReadLineAsync() ?? "";
Console.WriteLine($"Respuesta del servidor: {response}");
}
}
Una vez que el servidor esté en ejecución, inicie este cliente. El mensaje enviado se devolverá en mayúsculas.
4. Llamadas Web y REST API con HttpClient
Hoy en día, la forma más común de comunicación en red se basa en servicios REST sobre HTTP.
En .NET, la clase HttpClient se utiliza para este propósito.
Funciona de forma asíncrona, es reutilizable y es ideal para el intercambio de datos en formato JSON.
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($"Título: {post?.Title}");
}
public class Post
{
public int Id { get; set; }
public string? Title { get; set; }
public string? Body { get; set; }
}
}
Este ejemplo obtiene datos JSON de una API REST y los deserializa en una clase C# (Post).
JsonSerializer es la biblioteca JSON integrada en .NET.
5. Enviar una solicitud POST
Con HttpClient, puede enviar una solicitud POST creando manualmente el contenido JSON o usando serialización.
using System.Net.Http;
using System.Text;
using System.Text.Json;
var http = new HttpClient();
var newPost = new
{
title = "Nueva publicación",
body = "¡Hola mundo!",
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($"Estado: {response.StatusCode}");
string result = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Respuesta: {result}");
Este ejemplo demuestra la forma básica de enviar datos a una API REST. En aplicaciones reales, es importante usar el patrón async/await para mantener la capacidad de respuesta.
6. Ciclo de vida de HttpClient (Buenas prácticas)
Crear una nueva instancia de HttpClient para cada solicitud provoca un uso excesivo de recursos. El enfoque correcto es usar una sola instancia (singleton):
// Program.cs
builder.Services.AddHttpClient<IMyApiService, MyApiService>();
// Servicio
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}");
}
}
En ASP.NET Core, HttpClientFactory gestiona automáticamente el agrupamiento de conexiones (connection pooling).
7. Manejo de errores y configuración de tiempo de espera
En las llamadas HTTP, se deben manejar los errores, los tiempos de espera y las excepciones inesperadas.
var http = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
try
{
var response = await http.GetAsync("https://api.github.com/repos/dotnet/runtime");
response.EnsureSuccessStatusCode(); // Lanza excepción si el estado no está entre 200–299
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine("Solicitud exitosa: " + content.Length + " bytes");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Error HTTP: {ex.Message}");
}
catch (TaskCanceledException)
{
Console.WriteLine("¡Tiempo de espera excedido!");
}
8. SSL, encabezados y uso de tokens
La mayoría de las API modernas utilizan el encabezado Authorization para autenticación.
Puede agregar fácilmente encabezados personalizados y configuraciones especiales:
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);
Las conexiones HTTPS son seguras por defecto. En entornos de desarrollo (por ejemplo, certificados autofirmados), puede configurar un controlador personalizado.
9. Flujo asíncrono para grandes volúmenes de datos
Para archivos grandes o transmisiones de datos, puede leer directamente desde el Stream sin cargarlo todo en memoria:
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("descargado.zip");
await stream.CopyToAsync(file);
Console.WriteLine("Archivo descargado.");
Este método utiliza la memoria de manera eficiente, ya que el archivo no se carga completamente en la RAM.
10. Ejemplo: Consumo de una API REST (WeatherService)
El siguiente ejemplo obtiene datos de una API de clima y los convierte en un modelo.
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()
};
}
}
Esta estructura sigue los principios de la Clean Architecture y utiliza Inyección de Dependencias a través de HttpClientFactory.
11. Rendimiento y mejores prácticas
- Reutilice HttpClient (instancia única o
IHttpClientFactory). - Gestione tiempos de espera y políticas de reintento con la biblioteca Polly.
- Utilice
ConfigureAwait(false)en operaciones asíncronas para evitar bloqueos (deadlocks). - Si usa TcpClient, cierre los recursos correctamente con
usingyawait. - Agregue siempre manejo de excepciones y registro de logs en operaciones de E/S de red.
Resumen (TL;DR)
- TcpClient/TcpListener: Proporcionan comunicación TCP de bajo nivel basada en conexión.
- HttpClient: Cliente de alto nivel para comunicarse con APIs REST.
- GetAsync, PostAsync: Envían solicitudes HTTP; la conversión JSON se realiza con
JsonSerializer. - HttpClientFactory: Recomendado para la gestión de instancias y mejora del rendimiento.
- Operaciones asíncronas: Use
await, manejeTimeouty excepciones correctamente.
Artículos relacionados
Consejos de Visual Studio / VS Code para C#
Aprende consejos de Visual Studio y VS Code para C# y mejora tu productividad con atajos y herramientas.
Fundamentos de programación asíncrona en C# (async/await)
Aprende async y await en C# para crear aplicaciones fluidas con tareas asíncronas y ejemplos prácticos.
Métodos y uso de parámetros en C#
Aprende a definir métodos y usar parámetros en C#, incluyendo parámetros por valor y referencia, parámetros opcionales y ejemplos.