Dominando Java Networking: Construye Aplicaciones de Red Robustas con java.net
La programación de red se refiere a escribir programas que se ejecutan en múltiples dispositivos (computadoras) conectados mediante una red.
El paquete java.net de J2SE contiene clases e interfaces que manejan los detalles de comunicación de bajo nivel, permitiéndote centrarte en resolver el problema.
Protocolos Soportados
- TCP (Transmission Control Protocol): Comunicación fiable, orientada a conexión. Usado sobre IP → TCP/IP.
- UDP (User Datagram Protocol): Sin conexión, envía datagramas. Menor overhead, pero sin garantía de entrega/orden.
Este capítulo cubre dos temas principales:
- Programación con Sockets (Socket Programming): Concepto más usado en redes, explicado en detalle.
- Procesamiento de URLs: Cubierto por separado.
Programación con Sockets (TCP)
Los sockets proveen comunicación entre dos computadoras usando TCP.
- El cliente crea un socket e intenta conectar al servidor.
- El servidor crea un
ServerSocketescuchando en un puerto. - Cuando el cliente conecta, el servidor acepta y crea un socket para esa conexión.
- Ambos se comunican leyendo/escribiendo en los streams del socket.
Clases clave:
java.net.Socket: Representa un socket (cliente y servidor).java.net.ServerSocket: Mecanismo para que el servidor escuche y acepte conexiones.
Pasos para Establecer Conexión TCP
- Servidor instancia
ServerSocket(puerto). - Servidor invoca
accept()(bloquea hasta que llega cliente). - Cliente instancia
Socket(host, puerto). - Constructor de
Socketintenta conectar; si éxito, cliente tiene socket conectado. accept()retorna nuevoSocketen servidor conectado al cliente.
Tras la conexión, cada socket tiene InputStream y OutputStream. El OutputStream del cliente conecta al InputStream del servidor y viceversa. TCP es full-duplex.
Métodos de ServerSocket
| Constructor / Método | Descripción |
|---|---|
ServerSocket(int port) | Crea socket servidor en puerto; excepción si puerto ocupado. |
ServerSocket(int port, int backlog) | backlog: longitud cola de conexiones pendientes. |
ServerSocket(int port, int backlog, InetAddress addr) | Vincula a IP local específica (servidores multi-homed). |
ServerSocket() | Socket no vinculado; usar bind() después. |
accept() | Bloquea hasta conexión entrante; retorna Socket. |
setSoTimeout(int timeout) | Timeout para accept() (ms). |
getLocalPort() | Puerto en el que escucha (útil si pasaste 0). |
bind(SocketAddress, int backlog) | Vincula socket no vinculado. |
Métodos de Socket
| Constructor / Método | Descripción |
|---|---|
Socket(String host, int port) | Conecta a host:puerto; lanza excepción si falla. |
Socket(InetAddress host, int port) | Igual pero host como InetAddress. |
Socket(String host, int port, InetAddress localAddr, int localPort) | Conecta y vincula localmente a dirección/puerto específicos. |
Socket(InetAddress host, int port, InetAddress localAddr, int localPort) | Versión con InetAddress para host. |
Socket() | Socket no conectado; usar connect() después. |
connect(SocketAddress, int timeout) | Conecta con timeout (ms). |
getInetAddress() | Dirección del host remoto. |
getPort() | Puerto remoto. |
getLocalPort() | Puerto local. |
getRemoteSocketAddress() | Dirección remota completa. |
getInputStream() | InputStream del socket (lee del remoto). |
getOutputStream() | OutputStream del socket (escribe al remoto). |
close() | Cierra socket; no reutilizable. |
Clase InetAddress
Representa una dirección IP. Métodos útiles:
| Método | Descripción |
|---|---|
getByName(String host) | Resuelve nombre a IP. |
getByAddress(byte[] addr) | Crea InetAddress desde IP raw. |
getLocalHost() | Dirección del host local. |
getHostAddress() | IP en formato textual. |
getHostName() | Nombre de host para esta IP. |
toString() | Representación string (host/IP). |
Ejemplo Completo: Cliente-Servidor TCP
Cliente: GreetingClient
import java.net.*;
import java.io.*;
public class GreetingClient {
public static void main(String[] args) {
String serverName = args[0];
int port = Integer.parseInt(args[1]);
try {
System.out.println("Conectando a " + serverName + " en puerto " + port);
Socket client = new Socket(serverName, port);
System.out.println("Conectado a " + client.getRemoteSocketAddress());
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("Hola desde " + client.getLocalSocketAddress());
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
System.out.println("Servidor dice: " + in.readUTF());
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Servidor: GreetingServer
import java.net.*;
import java.io.*;
public class GreetingServer extends Thread {
private ServerSocket serverSocket;
public GreetingServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(10000);
}
public void run() {
while (true) {
try {
System.out.println("Esperando cliente en puerto " + serverSocket.getLocalPort() + "...");
Socket server = serverSocket.accept();
System.out.println("Conectado a " + server.getRemoteSocketAddress());
DataInputStream in = new DataInputStream(server.getInputStream());
System.out.println("Recibido: " + in.readUTF());
DataOutputStream out = new DataOutputStream(server.getOutputStream());
out.writeUTF("Gracias por conectar a " + server.getLocalSocketAddress() + "\n¡Adiós!");
server.close();
} catch (SocketTimeoutException s) {
System.out.println("Timeout del socket!");
break;
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
public static void main(String[] args) {
int port = Integer.parseInt(args[0]);
try {
Thread t = new GreetingServer(port);
t.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Ejecución
# Terminal 1 - Servidor
$ java GreetingServer 6066
Esperando cliente en puerto 6066...
# Terminal 2 - Cliente
$ java GreetingClient localhost 6066
Conectando a localhost en puerto 6066
Conectado a localhost/127.0.0.1:6066
Servidor dice: Gracias por conectar a /127.0.0.1:6066
¡Adiós!
Consideraciones Adicionales
- Manejo de múltiples clientes: El servidor ejemplo atiende uno a la vez. Para concurrencia, crea un hilo nuevo por cliente aceptado (pool de hilos).
- UDP (DatagramSocket/DatagramPacket): Para casos donde no se requiere fiabilidad (streaming, DNS, juegos).
- SSL/TLS: Usa
SSLSocket/SSLServerSocket(paquetejavax.net.ssl) para comunicaciones seguras. - NIO / NIO.2 (
java.nio.channels): I/O no bloqueante, selectores, escalabilidad alta (miles de conexiones con pocos hilos).
Java
- Dominar Operadores en Java: Tipos, Sintaxis y Ejemplos Prácticos
- Comentarios en Java: Tipos, Uso y Mejores Prácticas
- Dominar el Bucle for Mejorado en Java: Sintaxis, Ejemplos y Mejores Prácticas
- Dominar Strings en Java: Creación, Métodos y Mejores Prácticas
- Dominando interfaces en Java: Conceptos, implementación y mejores prácticas
- Dominando las clases internas anónimas en Java: Definición, sintaxis y ejemplos prácticos
- Dominando Try‑with‑Resources en Java: Gestión automática de recursos explicada
- Anotaciones en Java explicadas: Tipos, ubicación y ejemplos prácticos
- Aserciones en Java: Guía práctica para detectar bugs y asegurar fiabilidad del código
- Entendiendo Java Vector: Uso, Características y Comparación con ArrayList
- Dominando Java Networking: Construye Aplicaciones de Red Robustas con java.net