Implementar un servidor de alto rendimiento en C# sin depender de IIS ni de bibliotecas de terceros es perfectamente viable. Este enfoque permite construir servicios como API MVC, HTTP, FTP, Socket, WebSocket y transferencia de archivos grandes, los cuales pueden ejecutarse como servicios de Windows o iniciarse junto con la aplicación principle.
Servicio de API con arquitectura MVC
La arquitectura MVC (Modelo-Vista-Controlador) es ideal para separar la lógica de negocio del manejo de solicitudes. En C#, se puede implementar rápidamente utilizando ASP.NET Core.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace ApiMcvPersonalizado
{
public class Inicio
{
public Inicio(IConfiguration configuracion)
{
Configuracion = configuracion;
}
public IConfiguration Configuracion { get; }
public void RegistrarServicios(IServiceCollection servicios)
{
servicios.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configurar(IApplicationBuilder app, IHostingEnvironment entorno)
{
if (entorno.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
La clase Inicio es el punto central. El método RegistrarServicios agrega el framwork MVC al contenedor de dependencias. El método Configurar define el pipeline de solicitudes HTTP, aplicando middleware para excepciones, redirección HTTPS y el enrutamiento MVC.
Servicio HTTP básico con HttpListener
Para crear un servidor HTTP simple y autónomo, la clase HttpListener de .NET ofrece una solución directa.
using System;
using System.Net;
using System.Text;
class ServidorHttpSimple
{
private readonly HttpListener _oyente;
public ServidorHttpSimple()
{
_oyente = new HttpListener();
_oyente.Prefixes.Add("http://localhost:8080/");
}
public void Ejecutar()
{
_oyente.Start();
Console.WriteLine("Servidor HTTP iniciado. Escuchando en http://localhost:8080/");
while (true)
{
var contexto = _oyente.GetContext();
var respuesta = contexto.Response;
var contenido = Encoding.UTF8.GetBytes("¡Hola desde un servidor HTTP personalizado!");
respuesta.ContentLength64 = contenido.Length;
using (var flujoSalida = respuesta.OutputStream)
{
flujoSalida.Write(contenido, 0, contenido.Length);
}
}
}
}
Este código inicia un bucle que escucha indefinidamente. Por cada solicitud recibida, se genera una respuesta de texto simple y se cierra el flujo de salida. Es una base excelente para construir funcionalidades más complejas.
Servicio FTP usando FluentFTP
Implementar un servidor FTP desde cero es complejo. La biblioteca FluentFTP simplifica la interacción con servidores FTP existentes.
using FluentFTP;
using System;
class ClienteFtp
{
public void ConectarYOperar()
{
using (var cliente = new FtpClient("127.0.0.1", "usuario", "contraseña"))
{
cliente.Connect();
Console.WriteLine("Conexión FTP establecida.");
// Operaciones de subida/descarga de archivos aquí
cliente.Disconnect();
}
}
}
El ejemplo muestra la conexión y desconexión básica. FluentFTP encapsula el protocolo FTP, permitiendo enfocarse en la lógica de la aplicación.
Servicio Socket con TCP
Los sockets TCP son fundamentales para la comunicación de red a bajo nivel. C# provee un control robusto mediante TcpListener.
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class ServidorSocketTcp
{
private const int TAMANO_BUFFER = 4096;
private readonly TcpListener _listenerTcp;
public ServidorSocketTcp()
{
_listenerTcp = new TcpListener(IPAddress.Any, 13000);
}
public void Iniciar()
{
_listenerTcp.Start();
Console.WriteLine("Servidor Socket TCP escuchando en el puerto 13000.");
while (true)
{
using (var cliente = _listenerTcp.AcceptTcpClient())
using (var flujo = cliente.GetStream())
{
var datosBuffer = new byte[TAMANO_BUFFER];
var bytesLeidos = flujo.Read(datosBuffer, 0, datosBuffer.Length);
var mensajeEntrante = Encoding.UTF8.GetString(datosBuffer, 0, bytesLeidos);
Console.WriteLine($"Mensaje recibido: {mensajeEntrante}");
var respuesta = Encoding.UTF8.GetBytes("Confirmación de recepción.");
flujo.Write(respuesta, 0, respuesta.Length);
}
}
}
}
El servidor acepta conexiones entrantes, lee los datos del flujo de red, procesa el mensaje y envía una respuesta antes de cerrar la conexión. Es ideal para protocolos de comunicación personalizados.
Servicio WebSocket en tiempo real
Para comunicación bidireccional en tiempo real, los WebSockets son la solución. Se pueden implementar sobre un oyente HTTP.
using System;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class ServidorWebSocket
{
private readonly HttpListener _oyenteHttp;
private readonly CancellationTokenSource _cancelacion;
public ServidorWebSocket()
{
_oyenteHttp = new HttpListener();
_oyenteHttp.Prefixes.Add("http://localhost:8081/");
_cancelacion = new CancellationTokenSource();
}
public async Task EjecutarAsync()
{
_oyenteHttp.Start();
Console.WriteLine("Servidor WebSocket activo en http://localhost:8081/");
while (!_cancelacion.Token.IsCancellationRequested)
{
var contexto = await _oyenteHttp.GetContextAsync();
if (contexto.Request.IsWebSocketRequest)
{
var wsContext = await contexto.AcceptWebSocketAsync(null);
await ProcesarWebSocketAsync(wsContext.WebSocket);
}
else
{
contexto.Response.StatusCode = 400;
contexto.Response.Close();
}
}
_oyenteHttp.Close();
}
private async Task ProcesarWebSocketAsync(WebSocket socket)
{
var buffer = new byte[1024];
var resultado = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
var mensaje = Encoding.UTF8.GetString(buffer, 0, resultado.Count);
Console.WriteLine($"WebSocket recibió: {mensaje}");
var respuesta = Encoding.UTF8.GetBytes("Eco: " + mensaje);
await socket.SendAsync(new ArraySegment<byte>(respuesta), WebSocketMessageType.Text, true, CancellationToken.None);
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Cierre normal", CancellationToken.None);
}
}</byte></byte>
Este servidor detecta peticiones HTTP que solicitan una actualización a WebSocket. Una vez establecida la conexión, entra en un ciclo de lectura/escritura, permitiendo la comunicación en tiempo real.
Transferencia de arhcivos grandes
Enviar archivos grandes requiere un manejo de memoria eficiente, leyendo y transmitiendo datos en chunks.
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
class ServidorTransferenciaArchivos
{
private const int TAMANO_CHUNK = 8192; // 8 KB
private readonly TcpListener _listenerTcp;
public ServidorTransferenciaArchivos()
{
_listenerTcp = new TcpListener(IPAddress.Any, 13001);
}
public void Iniciar()
{
_listenerTcp.Start();
Console.WriteLine("Servidor de transferencia listo en puerto 13001.");
while (true)
{
using (var cliente = _listenerTcp.AcceptTcpClient())
using (var flujoRed = cliente.GetStream())
using (var flujoArchivo = new FileStream("datos_pesados.bin", FileMode.Open, FileAccess.Read))
{
var buffer = new byte[TAMANO_CHUNK];
int leidos;
while ((leidos = flujoArchivo.Read(buffer, 0, buffer.Length)) > 0)
{
flujoRed.Write(buffer, 0, leidos);
}
}
}
}
}
El proceso lee el archivo desde el disco en bloques pequeños y los escribe directamente al flujo de red. Esto evita cargar el archivo completo en la memoria, haciendo viable la transferencia de archivos de cualquier tamaño.
Combinando estos componentes, se puede ensamblar un servidor polivalente y de alto rendimiento en C#, capaz de atender múltiples tipos de solicitudes de red de manera eficiente y sin dependencias externas.