¿Por qué la programación de redes en Java es un superpoder?
Imagina: escribes código y, de repente, tu programa comienza a comunicarse con otro programa al otro lado del mundo. Envía solicitudes, recibe respuestas, sincroniza datos, todo en milisegundos. Esto no es magia, es programación de redes en Java. Si quieres crear no solo utilidades locales, sino verdaderos sistemas distribuidos, chats, juegos en línea o servicios REST, no puedes prescindir de la programación de redes.
Java ofrece un potente conjunto de herramientas para ello: desde los clásicos Socket y ServerSocket hasta el Java NIO (Non-blocking I/O) de alto rendimiento y los modernos WebSockets. En este artículo, analizaremos los tres enfoques, escribiremos código real y entenderemos cómo elegir la herramienta adecuada para tu tarea. ¡Vamos allá!
1. Sockets clásicos: un chat TCP «improvisado»
Empecemos por lo fundamental. Los sockets en Java son el punto de entrada al mundo TCP/IP. El servidor crea un ServerSocket, escucha en un puerto y acepta conexiones. El cliente crea un Socket y se conecta al servidor. Es tan simple como dos más dos.
Escribimos un servidor eco simple
El servidor recibe un mensaje del cliente y lo devuelve (eco). El código es mínimo, pero ilustra toda la esencia:
import java.io.*;import java.net.*;
public class EchoServer { public static void main(String[] args) { try (ServerSocket serverSocket = new ServerSocket(12345)) { System.out.println("Servidor iniciado en el puerto 12345..."); Socket clientSocket = serverSocket.accept(); // se bloquea hasta la conexión System.out.println("Cliente conectado: " + clientSocket.getInetAddress());
BufferedReader in = new BufferedReader( new InputStreamReader(clientSocket.getInputStream())); PrintWriter out = new PrintWriter( clientSocket.getOutputStream(), true);
String inputLine; while ((inputLine = in.readLine()) != null) { System.out.println("Recibido: " + inputLine); out.println("Eco: " + inputLine); if (inputLine.equalsIgnoreCase("exit")) break; } } catch (IOException e) { e.printStackTrace(); } }}Y aquí el cliente que envía mensajes:
import java.io.*;import java.net.*;
public class EchoClient { public static void main(String[] args) throws IOException { Socket socket = new Socket("localhost", 12345); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); BufferedReader stdIn = new BufferedReader( new InputStreamReader(System.in));
String userInput; System.out.println("Ingrese un mensaje (exit para salir):"); while ((userInput = stdIn.readLine()) != null) { out.println(userInput); System.out.println("Respuesta del servidor: " + in.readLine()); if (userInput.equalsIgnoreCase("exit")) break; } socket.close(); }}¿Qué es importante aquí? Los métodos accept() y readLine() bloquean el hilo. Esto significa que mientras un cliente se comunica, los demás esperan. La solución es la concurrencia.
Servidor multihilo: manejamos muchos clientes
Para cada nueva conexión, creamos un hilo separado. Usamos un pool de hilos ExecutorService para no crear miles de hilos innecesariamente:
import java.io.*;import java.net.*;import java.util.concurrent.*;
public class MultiThreadedServer { private static final int PORT = 12345; private static final ExecutorService threadPool = Executors.newFixe