Protocolos de red: TCP, UDP, IP, DNS, HTTP/HTTPS y modelos OSI/TCP-IP
Este artículo es una introducción completa a los protocolos de red, incluyendo TCP, UDP, IP, DNS, HTTP/HTTPS, el modelo OSI y la pila TCP/IP con ejemplos prácticos.
En resumen
Los protocolos de red regulan la comunicación entre computadoras. TCP/IP es la pila de protocolos fundamental, OSI describe 7 capas, HTTP/HTTPS permiten la comunicación web y DNS resuelve nombres en direcciones IP.
Descripción técnica compacta
Los protocolos de red son reglas y convenciones estandarizadas para la comunicación de datos entre computadoras en una red.
Protocolos importantes:
TCP (Transmission Control Protocol)
- Tipo: Orientado a conexión, confiable
- Características: Handshake de tres vías, control de flujo, corrección de errores
- Aplicación: Web, correo electrónico, transferencia de archivos
- Puerto: 80 (HTTP), 443 (HTTPS), 25 (SMTP)
UDP (User Datagram Protocol)
- Tipo: Sin conexión, no confiable
- Características: Simple, rápido, sin garantías
- Aplicación: Streaming, juegos, DNS
- Puerto: 53 (DNS), 123 (NTP), 67 (DHCP)
IP (Internet Protocol)
- Tipo: Conmutación de paquetes, enrutamiento
- Versiones: IPv4 (32 bits), IPv6 (128 bits)
- Características: Direccionamiento, fragmentación
- Aplicación: Base de Internet
DNS (Domain Name System)
- Tipo: Resolución de nombres
- Función: Dominio → dirección IP
- Tipos de registro: A, AAAA, MX, NS, CNAME
- Puerto: 53 (UDP/TCP)
HTTP/HTTPS (Hypertext Transfer Protocol)
- Tipo: Protocolo de aplicación para web
- Métodos: GET, POST, PUT, DELETE
- Estados: 200 OK, 404 Not Found, 500 Server Error
- Puerto: 80 (HTTP), 443 (HTTPS)
Puntos relevantes para exámenes
Estos puntos son la columna vertebral de los exámenes para técnicos en informática (IHK AP1/AP2). En la prueba no solo debes mencionar términos, sino explicar diferencias, asignar casos de uso y establecer conexiones.
- Protocolos de red: Reglas para la comunicación basada en computadoras. Explicación: Sin protocolos estandarizados, los sistemas diferentes no podrían comunicarse entre sí. Regulan el formato, el orden, el manejo de errores y la confirmación.
- TCP: Orientado a conexión, confiable, handshake de tres vías (SYN → SYN-ACK → ACK). Explicación: TCP garantiza la transferencia completa y correcta de datos. En el examen debes explicar el handshake y saber cuándo se utiliza TCP (web, correo electrónico, FTP).
- UDP: Sin conexión, rápido, no confiable. Explicación: UDP envía sin establecer una conexión y sin confirmación de recepción. En el examen debes destacar el contraste con TCP y nombrar aplicaciones donde la velocidad es más importante (DNS, VoIP, juegos, streaming).
- IP: Conmutación de paquetes, enrutamiento, IPv4 (32 bits) frente a IPv6 (128 bits). Explicación: IP es el fundamento de Internet. Debes explicar la diferencia entre IPv4 (direcciones limitadas, NAT necesario) e IPv6 (direcciones ilimitadas) y entender cómo los routers reenvían paquetes.
- DNS: Resolución de nombres, dominio → dirección IP. Explicación: DNS traduce nombres legibles para humanos en direcciones de máquina. En el examen debes explicar la estructura jerárquica (raíz → TLD → autoritativo) y los tipos de registro (A, AAAA, MX, CNAME).
- HTTP/HTTPS: Comunicación web, solicitud/respuesta. Explicación: Debes explicar los métodos (GET, POST, PUT, DELETE) y códigos de estado (200, 404, 500) y destacar la diferencia entre HTTP (puerto 80, sin cifrar) y HTTPS (puerto 443, TLS).
- Modelo OSI: Modelo de 7 capas. Explicación: En el examen debes nombrar las capas y asignarlas: Capa 2 = Data Link (Switch), Capa 3 = Network (Router, IP), Capa 4 = Transport (TCP, UDP), Capa 7 = Application (HTTP, DNS).
- Pila TCP/IP: Modelo de 4 capas. Explicación: TCP/IP es el modelo utilizado prácticamente en Internet. Debes poder asignar las 4 capas a las 7 capas OSI (por ejemplo, Application de TCP/IP = capas 5-7 de OSI).
- Relevancia para IHK: Base para la administración y el desarrollo de redes. Explicación: En AP1 y AP2 se prueban regularmente redes, protocolos y seguridad. El aprendizaje repetido lleva a momentos de iluminación.
Componentes centrales
Los componentes centrales forman el fundamento técnico de la comunicación en red. Quien entienda estas piezas no solo puede describir protocolos, sino también analizar problemas de red y desarrollar sus propias aplicaciones de red.
- Modelo OSI: Modelo teórico de 7 capas. Por qué es importante: El modelo OSI proporciona un lenguaje común para describir la comunicación de red. Cuando un colega dice “El problema está en la capa 3”, sabes inmediatamente que el enrutamiento/IP está afectado. Te ayuda a delimitar errores de forma sistemática.
- Pila TCP/IP: Modelo práctico de 4 capas. Por qué es importante: TCP/IP es el modelo realmente utilizado en Internet. Es más compacto que OSI e inmediatamente implementable. Si entiendes TCP/IP, entiendes cómo funciona realmente Internet.
- Protocolos de transporte: TCP, UDP para transporte de datos. Por qué es importante: La elección del protocolo de transporte determina qué tan confiable es la comunicación de tu aplicación. Una app de banca necesita TCP, un stream en vivo necesita UDP. Tú como desarrollador tomas esta decisión activamente.
- Protocolos de red: IP para enrutamiento y direccionamiento. Por qué es importante: Las direcciones IP son como direcciones postales en la red. Sin direccionamiento correcto, los datos no llegan. IPv4 frente a IPv6 es un tema de migración actual en muchas empresas.
- Protocolos de aplicación: HTTP, DNS, SMTP para servicios. Por qué es importante: Estos protocolos son la interfaz entre el usuario y la red. Cada acceso a un sitio web utiliza HTTP, cada nombre de dominio necesita DNS. Como desarrollador, trabajas con ellos diariamente.
- Números de puerto: Identificación de servicios. Por qué es importante: Una dirección IP por sí sola no es suficiente. En un servidor a menudo se ejecutan varios servicios simultáneamente. Los puertos (por ejemplo, 80 para HTTP, 443 para HTTPS) dirigen el tráfico al programa correcto. En el examen debes conocer los puertos bien conocidos.
- Programación de sockets: Comunicación de red. Por qué es importante: Los sockets son la interfaz de programación para redes. Quien entienda sockets puede construir sus propios servidores y clientes. Eso distingue a un desarrollador de aplicaciones de un simple usuario.
- Seguridad de red: Firewalls, VPN, TLS. Por qué es importante: Cada red abierta es un objetivo de ataque. TLS cifra datos, los firewalls bloquean tráfico no deseado. En el examen y en la práctica, la seguridad no es un complemento opcional, sino un requisito.
Ejemplos prácticos
1. Programación de sockets TCP con Java
¿Qué hace este código? Este programa Java muestra un servidor TCP completo con gestión de pool de threads, capaz de atender múltiples clientes simultáneamente. También incluye un cliente TCP y pruebas de rendimiento para latencia y throughput.
¿Por qué es útil entenderlo? TCP es el protocolo de transporte más utilizado en Internet. Quien entienda cómo se construye un servidor TCP, también entiende cómo funcionan fundamentalmente los servidores web, servidores de correo y bases de datos. El handshake de tres vías, la gestión de conexiones y el manejo de threads son conceptos centrales en el examen y en la práctica.
¿Qué puedes aprender de esto?
- Cómo un ServerSocket acepta conexiones y las delega a threads separados
- Cómo intercambiar datos bidireccionales con BufferedReader y PrintWriter
- Por qué un pool de threads es importante para no crear miles de threads
- Cómo medir latencia y throughput de forma programática
- Cómo cerrar correctamente las conexiones de cliente y liberar recursos
Si no dominas Java o no lo has aprendido, lee este ejemplo de todas formas. Pero presta más atención a la estructura. Cuándo se asignan las variables globales, cuándo la conexión real del cliente, cuándo aparecen las instrucciones try y por qué exactamente ahí… Más abajo encontrarás otro ejemplo en Python.
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
public class TCPServerDemo {
private static final int PORT = 8080;
private static final int MAX_CLIENTS = 10;
public static void main(String[] args) {
System.out.println("=== TCP Server Demo ===");
// Thread Pool für Client-Verbindungen
ExecutorService threadPool = Executors.newFixedThreadPool(MAX_CLIENTS);
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
System.out.println("TCP Server gestartet auf Port " + PORT);
while (true) {
// Auf Client-Verbindungen warten
Socket clientSocket = serverSocket.accept();
System.out.println("Neuer Client verbunden: " + clientSocket.getInetAddress());
// Client in separatem Thread behandeln
threadPool.execute(new ClientHandler(clientSocket));
}
} catch (IOException e) {
System.err.println("Server-Fehler: " + e.getMessage());
} finally {
threadPool.shutdown();
}
}
// Client-Handler
static class ClientHandler implements Runnable {
private final Socket clientSocket;
public ClientHandler(Socket socket) {
this.clientSocket = socket;
}
@Override
public void run() {
try (
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(
clientSocket.getOutputStream(), true)
) {
String clientAddress = clientSocket.getInetAddress().toString();
System.out.println("Handler für " + clientAddress + " gestartet");
// Begrüßung senden
out.println("Willkommen beim TCP Server!");
out.println("Type 'exit' to disconnect");
// Nachrichten vom Client empfangen und beantworten
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println("Nachricht von " + clientAddress + ": " + inputLine);
if ("exit".equalsIgnoreCase(inputLine.trim())) {
out.println("Goodbye!");
break;
}
// Echo mit Timestamp
String response = "Echo: " + inputLine + " [" +
java.time.LocalDateTime.now() + "]";
out.println(response);
}
} catch (IOException e) {
System.err.println("Fehler bei Client-Handler: " + e.getMessage());
} finally {
try {
clientSocket.close();
System.out.println("Client-Verbindung geschlossen");
} catch (IOException e) {
System.err.println("Fehler beim Schließen der Verbindung: " + e.getMessage());
}
}
}
}
// TCP Client
static class TCPClient {
private final String hostname;
private final int port;
public TCPClient(String hostname, int port) {
this.hostname = hostname;
this.port = port;
}
public void start() {
try (
Socket socket = new Socket(hostname, port);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(
socket.getOutputStream(), true);
BufferedReader stdIn = new BufferedReader(
new InputStreamReader(System.in))
) {
System.out.println("Mit Server verbunden");
// Server-Antwort empfangen
String serverResponse = in.readLine();
System.out.println("Server: " + serverResponse);
// Interaktive Kommunikation
String userInput;
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput);
serverResponse = in.readLine();
System.out.println("Server: " + serverResponse);
if ("exit".equalsIgnoreCase(userInput.trim())) {
break;
}
}
} catch (UnknownHostException e) {
System.err.println("Unbekannter Host: " + hostname);
} catch (IOException e) {
System.err.println("I/O Fehler: " + e.getMessage());
}
}
}
// TCP Performance Test
static class TCPPerformanceTest {
public static void testTCPLatency(String hostname, int port, int iterations) {
try (Socket socket = new Socket(hostname, port);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()))) {
System.out.println("=== TCP Latency Test ===");
long totalTime = 0;
for (int i = 0; i < iterations; i++) {
long startTime = System.nanoTime();
out.println("ping");
String response = in.readLine();
long endTime = System.nanoTime();
long latency = (endTime - startTime) / 1_000_000; // ms
totalTime += latency;
if (i % 10 == 0) {
System.out.printf("Ping %d: %dms%n", i + 1, latency);
}
}
double avgLatency = (double) totalTime / iterations;
System.out.printf("Durchschnittliche Latenz: %.2fms%n", avgLatency);
} catch (IOException e) {
System.err.println("Fehler bei Latenz-Test: " + e.getMessage());
}
}
public static void testTCPThroughput(String hostname, int port, int dataSize) {
try (Socket socket = new Socket(hostname, port);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()))) {
System.out.println("=== TCP Throughput Test ===");
// Testdaten erstellen
StringBuilder testData = new StringBuilder();
for (int i = 0; i < dataSize; i++) {
testData.append("A");
}
long startTime = System.nanoTime();
out.println("throughput:" + testData.toString());
String response = in.readLine();
long endTime = System.nanoTime();
long duration = (endTime - startTime) / 1_000_000; // ms
double throughput = (double) dataSize / (duration / 1000.0) / 1024.0; // KB/s
System.out.printf("Durchsatz: %.2f KB/s (%d Bytes in %dms)%n",
throughput, dataSize, duration);
} catch (IOException e) {
System.err.println("Fehler bei Throughput-Test: " + e.getMessage());
}
}
}
// Main für Client-Tests
public static void main(String[] args) {
// Server starten
if (args.length == 0) {
TCPServerDemo.main(args);
} else {
// Client-Tests
TCPClient client = new TCPClient("localhost", PORT);
// Interaktiver Client
new Thread(() -> client.start()).start();
// Performance Tests
try {
Thread.sleep(2000); // Warten bis Server bereit
TCPPerformanceTest.testTCPLatency("localhost", PORT, 100);
TCPPerformanceTest.testTCPThroughput("localhost", PORT, 10240);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
2. Programación de sockets UDP con Python
¿Qué hace este código? Este programa Python muestra un servidor UDP con función de broadcast, un cliente UDP interactivo, pruebas de rendimiento para latencia, throughput y pérdida de paquetes, además de una demostración de UDP Multicast.
¿Por qué es importante entenderlo? UDP es lo opuesto a TCP: rápido pero poco confiable. Quien entiende UDP sabe cuándo la velocidad es más importante que la confiabilidad. Broadcasting y Multicast son conceptos fundamentales para descubrimiento de redes, streaming y comunicación IoT. En los exámenes deberás poder contrastar TCP y UDP.
¿Qué puedes aprender?
- Cómo crear sockets UDP con
socket.SOCK_DGRAM - Por qué UDP no requiere establecimiento de conexión y cómo
recvfromentrega las direcciones del remitente - Cómo funciona el broadcasting hacia todos los clientes y el multicast hacia un grupo
- Por qué UDP tiene pérdida de paquetes y cómo medirlo
- Cómo usar threads para enviar y recibir simultáneamente
Presta especial atención a la estructura del ejemplo. Analiza los imports desde el inicio y pregúntate por qué se importan y podrían ser necesarios.
import socket
import threading
import time
import json
from datetime import datetime
# UDP Server
class UDPServer:
def __init__(self, host='localhost', port=9999):
self.host = host
self.port = port
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.clients = set()
self.running = False
def start(self):
"""Inicio del servidor UDP"""
self.server_socket.bind((self.host, self.port))
self.running = True
print(f"UDP Server running on {self.host}:{self.port}")
while self.running:
try:
# Recibir datos
data, client_address = self.server_socket.recvfrom(2048)
# Agregar cliente a la lista
self.clients.add(client_address)
print(f"Mensaje de {client_address}: {data.decode()}")
# Enviar mensaje a todos los clientes (broadcast)
self.broadcast(data, client_address)
except Exception as e:
print(f"Error al recibir: {e}")
def broadcast(self, message, sender_address):
"""Enviar mensaje a todos los clientes"""
timestamp = datetime.now().strftime("%H:%M:%S")
broadcast_message = f"[{timestamp}] {sender_address[0]}: {message.decode()}"
for client in self.clients:
if client != sender_address:
try:
self.server_socket.sendto(broadcast_message.encode(), client)
except Exception as e:
print(f"Error al enviar a {client}: {e}")
def stop(self):
"""Detener servidor"""
self.running = False
self.server_socket.close()
print("UDP Server stopped")
# UDP Client
class UDPClient:
def __init__(self, host='localhost', port=9999):
self.host = host
self.port = port
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.running = False
def start(self, username):
"""Inicio del cliente UDP"""
self.running = True
print(f"UDP Client connected to {self.host}:{self.port}")
# Iniciar thread de recepción
receive_thread = threading.Thread(target=self.receive_messages)
receive_thread.daemon = True
receive_thread.start()
try:
while self.running:
# Ingresar mensaje
message = input(f"{username}: ")
if message.lower() == 'exit':
break
# Enviar mensaje
full_message = f"{username}: {message}"
self.client_socket.sendto(full_message.encode(), (self.host, self.port))
except KeyboardInterrupt:
print("\nCliente terminado")
finally:
self.stop()
def receive_messages(self):
"""Recibir mensajes del servidor"""
while self.running:
try:
data, server = self.client_socket.recvfrom(2048)
print(f"\r{data.decode()}")
print("Mensaje: ", end="", flush=True)
except Exception as e:
if self.running:
print(f"Error al recibir: {e}")
break
def stop(self):
"""Detener cliente"""
self.running = False
self.client_socket.close()
print("UDP Client stopped")
# Prueba de rendimiento UDP
class UDPPerformanceTest:
def __init__(self, host='localhost', port=9999):
self.host = host
self.port = port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
def test_latency(self, iterations=100):
"""Prueba de latencia UDP"""
print(f"=== UDP Latency Test ===")
latencies = []
for i in range(iterations):
start_time = time.time()
# Enviar mensaje ping
self.socket.sendto(b"ping", (self.host, self.port))
# Recibir respuesta
try:
data, _ = self.socket.recvfrom(1024)
end_time = time.time()
latency = (end_time - start_time) * 1000 # ms
latencies.append(latency)
if i % 10 == 0:
print(f"Ping {i+1}: {latency:.2f}ms")
except socket.timeout:
print(f"Timeout en ping {i+1}")
if latencies:
avg_latency = sum(latencies) / len(latencies)
min_latency = min(latencies)
max_latency = max(latencies)
print(f"\nEstadísticas de latencia:")
print(f" Promedio: {avg_latency:.2f}ms")
print(f" Mínimo: {min_latency:.2f}ms")
print(f" Máximo: {max_latency:.2f}ms")
def test_throughput(self, data_size=1024, duration=5):
"""Prueba de throughput UDP"""
print(f"\n=== UDP Throughput Test ===")
# Crear datos de prueba
test_data = b'A' * data_size
start_time = time.time()
packets_sent = 0
while time.time() - start_time < duration:
try:
self.socket.sendto(test_data, (self.host, self.port))
packets_sent += 1
# Pausa breve para no saturar el servidor
time.sleep(0.001)
except Exception as e:
print(f"Error al enviar: {e}")
break
end_time = time.time()
total_time = end_time - start_time
throughput = (packets_sent * data_size) / total_time / 1024 # KB/s
packets_per_second = packets_sent / total_time
print(f"Estadísticas de throughput:")
print(f" Paquetes enviados: {packets_sent}")
print(f" Duración total: {total_time:.2f}s")
print(f" Paquetes/segundo: {packets_per_second:.2f}")
print(f" Throughput: {throughput:.2f} KB/s")
def test_packet_loss(self, packets=1000):
"""Prueba de pérdida de paquetes UDP"""
print(f"\n=== UDP Packet Loss Test ===")
packets_lost = 0
for i in range(packets):
try:
# Enviar número secuencial
packet = f"packet_{i}".encode()
self.socket.sendto(packet, (self.host, self.port))
# Pausa breve
time.sleep(0.001)
except Exception as e:
packets_lost += 1
loss_rate = (packets_lost / packets) * 100
print(f"Estadísticas de pérdida de paquetes:")
print(f" Paquetes enviados: {packets}")
print(f" Paquetes perdidos: {packets_lost}")
print(f" Tasa de pérdida: {loss_rate:.2f}%")
def close(self):
"""Cerrar socket"""
self.socket.close()
# Demostración de UDP Multicast
class UDPMulticastDemo:
def __init__(self, multicast_group='224.0.0.1', port=5000):
self.multicast_group = multicast_group
self.port = port
def create_multicast_sender(self):
"""Crear emisor multicast"""
sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Configurar TTL (cuántos hops puede hacer el paquete)
ttl = struct.pack('b', 1)
sender.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
return sender
def create_multicast_receiver(self):
"""Crear receptor multicast"""
receiver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Vincular socket a la dirección multicast
receiver.bind(('', self.port))
# Unirse al grupo multicast
group = socket.inet_aton(self.multicast_group)
receiver.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, group + socket.inet_aton('0.0.0.0'))
return receiver
def send_multicast(self, message):
"""Enviar mensaje multicast"""
sender = self.create_multicast_sender()
try:
sender.sendto(message.encode(), (self.multicast_group, self.port))
print(f"Multicast enviado: {message}")
finally:
sender.close()
def receive_multicast(self):
"""Recibir mensajes multicast"""
receiver = self.create_multicast_receiver()
try:
print(f"Receptor multicast escuchando en {self.multicast_group}:{self.port}")
while True:
try:
data, address = receiver.recvfrom(1024)
print(f"Multicast desde {address}: {data.decode()}")
except KeyboardInterrupt:
break
finally:
# Salir del grupo
group = socket.inet_aton(self.multicast_group)
receiver.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, group + socket.inet_aton('0.0.0.0'))
receiver.close()
# Main para demostraciones UDP
def main():
import sys
if len(sys.argv) > 1:
mode = sys.argv[1]
if mode == "server":
# Iniciar servidor UDP
server = UDPServer()
try:
server.start()
except KeyboardInterrupt:
server.stop()
elif mode == "client":
# Iniciar cliente UDP
username = input("Su nombre: ")
client = UDPClient()
client.start(username)
elif mode == "performance":
# Pruebas de rendimiento
test = UDPPerformanceTest()
test.test_latency(100)
test.test_throughput(1024, 5)
test.test_packet_loss(1000)
test.close()
elif mode == "multicast_send":
# Emisor multicast
multicast = UDPMulticastDemo()
while True:
message = input("Mensaje multicast (o 'exit'): ")
if message.lower() == 'exit':
break
multicast.send_multicast(message)
elif mode == "multicast_receive":
# Receptor multicast
multicast = UDPMulticastDemo()
multicast.receive_multicast()
else:
print("Uso: python udp_demo.py [server|client|performance|multicast_send|multicast_receive]")
else:
print("Uso: python udp_demo.py [server|client|performance|multicast_send|multicast_receive]")
if __name__ == "__main__":
main()
3. Consultas DNS con diferentes lenguajes de programación
¿Qué hace este código? Este programa Java muestra varios tipos de búsquedas DNS: resolución normal de nombres (dominio → IP), DNS inverso (IP → dominio), consulta de todas las direcciones IP, pruebas de rendimiento DNS y una comparación de caché DNS (primera búsqueda versus segunda).
¿Por qué es útil entender esto? DNS es la guía telefónica de Internet. Sin DNS tendrías que memorizar direcciones IP. En exámenes y en el trabajo diario necesitas diagnosticar problemas DNS, ya sea un A-Record incorrecto, un caché expirado o un servidor de nombres inaccesible. Cuando entiendes DNS, entiendes por qué algunos sitios web funcionan y otros no.
¿Qué puedes aprender?
- Cómo
InetAddress.getByName()traduce nombres de dominio en IPs - Cómo funciona DNS inverso con la misma clase
- Por qué el caché DNS es importante y cómo afecta el rendimiento
- Cómo evaluar comparativamente búsquedas DNS de forma programática
- La diferencia entre IPv4 (
Inet4Address) e IPv6 (Inet6Address)
import java.net.*;
import java.util.*;
public class DNSLookupDemo {
// Einfache DNS-Abfrage
public static String dnsLookup(String hostname) {
try {
InetAddress address = InetAddress.getByName(hostname);
return address.getHostAddress();
} catch (UnknownHostException e) {
return "Unknown host: " + hostname;
}
}
// Reverse DNS-Abfrage (IP → Hostname)
public static String reverseDNSLookup(String ipAddress) {
try {
InetAddress address = InetAddress.getByName(ipAddress);
return address.getHostName();
} catch (UnknownHostException e) {
return "Unknown IP: " + ipAddress;
}
}
// Alle IP-Adressen für einen Hostnamen
public static List<String> getAllIPAddresses(String hostname) {
List<String> addresses = new ArrayList<>();
try {
InetAddress[] allAddresses = InetAddress.getAllByName(hostname);
for (InetAddress addr : allAddresses) {
addresses.add(addr.getHostAddress());
}
} catch (UnknownHostException e) {
addresses.add("Unknown host: " + hostname);
}
return addresses;
}
// DNS-Record-Typen abfragen (vereinfacht)
public static void dnsLookupWithTypes(String hostname) {
System.out.println("=== DNS Lookup für " + hostname + " ===");
// A-Record (IPv4)
String ipv4 = dnsLookup(hostname);
System.out.println("A-Record (IPv4): " + ipv4);
// AAAA-Record (IPv6)
try {
InetAddress ipv6Address = InetAddress.getByName(hostname);
if (ipv6Address instanceof Inet6Address) {
System.out.println("AAAA-Record (IPv6): " + ipv6Address.getHostAddress());
}
} catch (UnknownHostException e) {
System.out.println("AAAA-Record (IPv6): Nicht gefunden");
}
// Reverse Lookup
if (!ipv4.equals("Unknown host: " + hostname)) {
String reverse = reverseDNSLookup(ipv4);
System.out.println("PTR-Record (Reverse): " + reverse);
}
// MX-Records (Mail Exchange)
dnsLookupMX(hostname);
// NS-Records (Name Server)
dnsLookupNS(hostname);
}
// MX-Records abfragen
public static void dnsLookupMX(String hostname) {
try {
// In der Praxis würde man DNS-Bibliotheken wie dnsjava verwenden
// Hier zeigen wir das Konzept
System.out.println("MX-Records: Verwendet DNS-Bibliotheken für vollständige Abfrage");
// Beispiel mit einfacher Logik
if (hostname.endsWith(".com")) {
System.out.println(" MX: mail." + hostname + " (Beispiel)");
}
} catch (Exception e) {
System.out.println("MX-Records: Fehler bei Abfrage - " + e.getMessage());
}
}
// NS-Records abfragen
public static void dnsLookupNS(String hostname) {
try {
System.out.println("NS-Records: Verwendet DNS-Bibliotheken für vollständige Abfrage");
// Beispiel mit einfacher Logik
String[] commonNS = {"ns1." + hostname, "ns2." + hostname};
for (String ns : commonNS) {
System.out.println(" NS: " + ns + " (Beispiel)");
}
} catch (Exception e) {
System.out.println("NS-Records: Fehler bei Abfrage - " + e.getMessage());
}
}
// DNS-Performance-Test
public static void dnsPerformanceTest(String hostname, int iterations) {
System.out.println("=== DNS Performance Test ===");
long totalTime = 0;
int successfulLookups = 0;
for (int i = 0; i < iterations; i++) {
long startTime = System.nanoTime();
String result = dnsLookup(hostname);
long endTime = System.nanoTime();
long duration = (endTime - startTime) / 1_000_000; // ms
if (!result.startsWith("Unknown host")) {
totalTime += duration;
successfulLookups++;
}
if (i % 10 == 0) {
System.out.printf("Lookup %d: %dms%n", i + 1, duration);
}
}
if (successfulLookups > 0) {
double avgTime = (double) totalTime / successfulLookups;
System.out.printf("Durchschnittliche DNS-Lookup Zeit: %.2fms%n", avgTime);
System.out.printf("Erfolgreiche Lookups: %d/%d%n", successfulLookups, iterations);
}
}
// DNS-Caching Demo
public static void dnsCachingDemo(String hostname) {
System.out.println("=== DNS Caching Demo ===");
// Erster Lookup (Cache-Miss)
long start = System.nanoTime();
String result1 = dnsLookup(hostname);
long firstLookup = (System.nanoTime() - start) / 1_000_000;
// Zweiter Lookup (Cache-Hit)
start = System.nanoTime();
String result2 = dnsLookup(hostname);
long secondLookup = (System.nanoTime() - start) / 1_000_000;
System.out.println("Erster Lookup: " + firstLookup + "ms");
System.out.println("Zweiter Lookup: " + secondLookup + "ms");
if (secondLookup < firstLookup) {
System.out.println("DNS-Caching aktiv (zweiter Lookup schneller)");
} else {
System.out.println("Kein DNS-Caching erkannt");
}
// Ergebnisse vergleichen
System.out.println("Ergebnis 1: " + result1);
System.out.println("Ergebnis 2: " + result2);
System.out.println("Ergebnisse gleich: " + result1.equals(result2));
}
// IPv6 Support Test
public static void ipv6SupportTest(String hostname) {
System.out.println("=== IPv6 Support Test ===");
try {
// IPv6-Adresse auflösen
InetAddress[] addresses = InetAddress.getAllByName(hostname);
boolean hasIPv4 = false;
boolean hasIPv6 = false;
for (InetAddress addr : addresses) {
if (addr instanceof Inet4Address) {
hasIPv4 = true;
System.out.println("IPv4: " + addr.getHostAddress());
} else if (addr instanceof Inet6Address) {
hasIPv6 = true;
System.out.println("IPv6: " + addr.getHostAddress());
}
}
System.out.println("IPv4 Support: " + (hasIPv4 ? "Ja" : "Nein"));
System.out.println("IPv6 Support: " + (hasIPv6 ? "Ja" : "Nein"));
} catch (UnknownHostException e) {
System.out.println("IPv6 Support Test fehlgeschlagen: " + e.getMessage());
}
}
public static void main(String[] args) {
String hostname = "google.com";
if (args.length > 0) {
hostname = args[0];
}
// Verschiedene DNS-Abfragen
dnsLookupWithTypes(hostname);
// Performance-Test
dnsPerformanceTest(hostname, 50);
// Caching-Demo
dnsCachingDemo(hostname);
// IPv6 Support Test
ipv6SupportTest(hostname);
}
}
4. Cliente HTTP/HTTPS con características avanzadas
¿Qué hace este código? Este programa Python implementa un cliente HTTP/HTTPS completo con características avanzadas: estrategia de reintentos, diversos métodos HTTP (GET, POST, HEAD, OPTIONS), verificación de certificados SSL/TLS, gestión de cookies, seguimiento de redirecciones, descarga de archivos con indicador de progreso, pruebas de rendimiento y un cliente API para endpoints REST.
¿Por qué vale la pena entenderlo? HTTP/HTTPS es el protocolo de aplicación más importante en la web. Cada llamada a una API, cada visita a una página web y cada sincronización de datos utiliza HTTP. Quien entienda cómo funcionan las peticiones, respuestas, códigos de estado, encabezados y sesiones podrá depurar APIs, identificar problemas de seguridad y construir aplicaciones web de alto rendimiento. En los exámenes, los métodos HTTP y códigos de estado son preguntas estándar.
¿Qué puedes aprender de esto?
- Cómo construir una conexión reutilizable usando
requests.Session() - Por qué las estrategias de reintentos importan y cómo implementarlas con
urllib3.util.retry - Cómo extraer información de certificados SSL y versiones TLS
- Cómo se transmiten automáticamente las cookies entre peticiones
- Cómo rastrear o bloquear redirecciones
- Cómo descargar archivos grandes eficientemente con
stream=True - Cómo construir un cliente API REST genérico con operaciones CRUD
import requests
import json
import time
import ssl
import urllib3
from urllib3.util.retry import Retry
from datetime import datetime
# HTTP Client con características avanzadas
class HTTPClient:
def __init__(self):
self.session = requests.Session()
self.setup_session()
def setup_session(self):
"""Configurar la sesión"""
# Estrategia de reintentos
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["HEAD", "GET", "OPTIONS"]
)
adapter = urllib3.HTTPAdapter(max_retries=retry_strategy)
self.session.mount("https://", adapter)
self.session.mount("http://", adapter)
# Headers
self.session.headers.update({
'User-Agent': 'HTTPClient-Demo/1.0',
'Accept': 'application/json',
'Accept-Language': 'de-DE,de;q=0.9,en;q=0.8',
'Cache-Control': 'no-cache'
})
# Timeout
self.session.timeout = 30
print("HTTP Client configurado")
def get_request(self, url, params=None):
"""Petición GET con manejo de errores"""
try:
print(f"GET Request: {url}")
start_time = time.time()
response = self.session.get(url, params=params)
end_time = time.time()
self.print_response_info(response, end_time - start_time)
return response
except requests.exceptions.RequestException as e:
print(f"GET Request falló: {e}")
return None
def post_request(self, url, data=None, json_data=None):
"""Petición POST con datos"""
try:
print(f"POST Request: {url}")
if json_data:
print(f"Datos JSON: {json.dumps(json_data, indent=2)}")
start_time = time.time()
if json_data:
response = self.session.post(url, json=json_data)
else:
response = self.session.post(url, data=data)
end_time = time.time()
self.print_response_info(response, end_time - start_time)
return response
except requests.exceptions.RequestException as e:
print(f"POST Request falló: {e}")
return None
def print_response_info(self, response, duration):
"""Mostrar información de la respuesta"""
print(f"Código de estado: {response.status_code}")
print(f"Tiempo de respuesta: {duration:.2f}s")
print(f"Content-Type: {response.headers.get('Content-Type', 'N/A')}")
print(f"Content-Length: {response.headers.get('Content-Length', 'N/A')}")
# Mostrar encabezados
if response.headers:
print("Encabezados de la respuesta:")
for key, value in response.headers.items():
print(f" {key}: {value}")
def test_http_methods(self, url):
"""Probar diferentes métodos HTTP"""
print(f"=== Prueba de métodos HTTP para {url} ===")
# GET
print("\n--- GET ---")
response = self.get_request(url)
# HEAD
print("\n--- HEAD ---")
try:
response = self.session.head(url)
print(f"Estado HEAD: {response.status_code}")
print(f"Encabezados HEAD: {dict(response.headers)}")
except Exception as e:
print(f"HEAD Request falló: {e}")
# OPTIONS
print("\n--- OPTIONS ---")
try:
response = self.session.options(url)
print(f"Estado OPTIONS: {response.status_code}")
allowed_methods = response.headers.get('Allow', 'N/A')
print(f"Métodos permitidos: {allowed_methods}")
except Exception as e:
print(f"OPTIONS Request falló: {e}")
def test_https_features(self, url):
"""Probar características HTTPS"""
print(f"=== Prueba de características HTTPS para {url} ===")
try:
# Información del certificado SSL
response = self.session.get(url)
# Mostrar información SSL
cert_info = response.raw._connection.peer.cert
if cert_info:
print("Información del certificado SSL:")
for cert in cert_info:
print(f" Sujeto: {cert.subject}")
print(f" Emisor: {cert.issuer}")
print(f" Válido desde: {cert.not_valid_before}")
print(f" Válido hasta: {cert.not_valid_after}")
print(f" Serie: {cert.serial_number}")
# Versión TLS
tls_version = response.raw._connection.tls_version
print(f"Versión TLS: {tls_version}")
# Suite de cifrado
cipher_suite = response.raw._connection.cipher
print(f"Suite de cifrado: {cipher_suite}")
except Exception as e:
print(f"Prueba de características HTTPS falló: {e}")
def test_authentication(self, url, username, password):
"""Probar autenticación HTTP"""
print(f"=== Prueba de autenticación para {url} ===")
# Autenticación básica
try:
from requests.auth import HTTPBasicAuth
auth = HTTPBasicAuth(username, password)
response = self.session.get(url, auth=auth)
print(f"Estado autenticación básica: {response.status_code}")
if response.status_code == 200:
print("Autenticación básica exitosa")
else:
print("Autenticación básica falló")
except Exception as e:
print(f"Prueba de autenticación falló: {e}")
def test_cookies(self, url):
"""Probar gestión de cookies"""
print(f"=== Prueba de cookies para {url} ===")
try:
# Primera petición sin cookies
response1 = self.session.get(url)
print(f"Estado de primera petición: {response1.status_code}")
print(f"Cookies tras primera petición: {len(self.session.cookies)}")
# Segunda petición con cookies
response2 = self.session.get(url)
print(f"Estado de segunda petición: {response2.status_code}")
print(f"Cookies tras segunda petición: {len(self.session.cookies)}")
# Información de cookies
if self.session.cookies:
print("Detalles de cookies:")
for cookie in self.session.cookies:
print(f" {cookie.name}: {cookie.value}")
print(f" Dominio: {cookie.domain}")
print(f" Ruta: {cookie.path}")
print(f" Seguro: {cookie.secure}")
print(f" Solo HTTP: {cookie.has_attr('HttpOnly')}")
except Exception as e:
print(f"Prueba de cookies falló: {e}")
def test_redirects(self, url):
"""Probar gestión de redirecciones"""
print(f"=== Prueba de redirecciones para {url} ===")
try:
# No seguir redirecciones
response = self.session.get(url, allow_redirects=False)
print(f"Estado sin redirecciones: {response.status_code}")
if 300 <= response.status_code < 400:
location = response.headers.get('Location', 'N/A')
print(f"Redirigir a: {location}")
# Seguir redirecciones
response = self.session.get(url, allow_redirects=True)
print(f"Estado con redirecciones: {response.status_code}")
print(f"URL final: {response.url}")
# Historial de redirecciones
if hasattr(response.history, '__iter__'):
print("Historial de redirecciones:")
for i, resp in enumerate(response.history):
print(f" {i+1}. {resp.status_code} -> {resp.url}")
except Exception as e:
print(f"Prueba de redirecciones falló: {e}")
def test_download(self, url, save_path=None):
"""Probar descarga de archivos"""
print(f"=== Prueba de descarga para {url} ===")
try:
response = self.session.get(url, stream=True)
content_length = int(response.headers.get('content-length', 0))
print(f"Tamaño del archivo: {content_length} bytes")
if save_path:
with open(save_path, 'wb') as f:
downloaded = 0
chunk_size = 8192
for chunk in response.iter_content(chunk_size=chunk_size):
f.write(chunk)
downloaded += chunk_size
# Mostrar progreso
progress = (downloaded / content_length) * 100 if content_length > 0 else 0
print(f"\rDescarga: {progress:.1f}%", end="", flush=True)
print(f"\nArchivo guardado: {save_path}")
except Exception as e:
print(f"Descarga falló: {e}")
def performance_test(self, url, iterations=10):
"""Prueba de rendimiento"""
print(f"=== Prueba de rendimiento para {url} ===")
times = []
for i in range(iterations):
try:
start_time = time.time()
response = self.session.get(url)
end_time = time.time()
duration = end_time - start_time
times.append(duration)
if i % 5 == 0:
print(f"Petición {i+1}: {duration:.3f}s")
except Exception as e:
print(f"Petición {i+1} falló: {e}")
if times:
avg_time = sum(times) / len(times)
min_time = min(times)
max_time = max(times)
print(f"\nEstadísticas de rendimiento:")
print(f" Peticiones: {len(times)}")
print(f" Promedio: {avg_time:.3f}s")
print(f" Mínimo: {min_time:.3f}s")
print(f" Máximo: {max_time:.3f}s")
print(f" Peticiones/segundo: {1/avg_time:.2f}")
# Cliente API para APIs REST
class APIClient(HTTPClient):
def __init__(self, base_url):
super().__init__()
self.base_url = base_url.rstrip('/')
# Encabezados específicos de API
self.session.headers.update({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
def get_resource(self, resource, params=None):
"""GET Recurso"""
url = f"{self.base_url}/{resource.lstrip('/')}"
return self.get_request(url, params)
def create_resource(self, resource, data):
"""POST Recurso"""
url = f"{self.base_url}/{resource.lstrip('/')}"
return self.post_request(url, json_data=data)
def update_resource(self, resource, data):
"""PUT Recurso"""
url = f"{self.base_url}/{resource.lstrip('/')}"
try:
response = self.session.put(url, json=data)
self.print_response_info(response, 0)
return response
except Exception as e:
print(f"PUT Request falló: {e}")
return None
def delete_resource(self, resource):
"""DELETE Recurso"""
url = f"{self.base_url}/{resource.lstrip('/')}"
try:
response = self.session.delete(url)
self.print_response_info(response, 0)
return response
except Exception as e:
print(f"DELETE Request falló: {e}")
return None
# Main para demostraciones HTTP
def main():
import sys
# Demo de cliente HTTP
client = HTTPClient()
# Prueba de métodos HTTP
client.test_http_methods("https://httpbin.org")
# Prueba de características HTTPS
client.test_https_features("https://google.com")
# Prueba de redirecciones
client.test_redirects("http://httpbin.org/redirect/1")
# Prueba de cookies
client.test_cookies("https://httpbin.org/cookies/set/test/value")
# Prueba de rendimiento
client.performance_test("https://httpbin.org/get", 20)
# Demo de cliente API
api_client = APIClient("https://jsonplaceholder.typicode.com")
# GET Users
users_response = api_client.get_resource("/users")
if users_response:
print(f"Usuarios: {users_response.json()}")
# Crear Post
new_post = {
"title": "Test Post",
"body": "Test Body",
"userId": 1
}
create_response = api_client.create_resource("/posts", new_post)
# Prueba de descarga
client.test_download("https://httpbin.org/bytes/1024", "test_download.bin")
if __name__ == "__main__":
main()
Modelo OSI vs Pila TCP/IP
Modelo OSI (7 capas)
| Capa | Nombre | Función | Ejemplos |
|---|---|---|---|
| 7 | Application | Protocolos de aplicación | HTTP, FTP, SMTP |
| 6 | Presentation | Transformación de datos | SSL/TLS, JPEG |
| 5 | Session | Gestión de sesiones | NetBIOS, RPC |
| 4 | Transport | Comunicación end-to-end | TCP, UDP |
| 3 | Network | Encaminamiento y direccionamiento | IP, ICMP |
| 2 | Data Link | Transmisión de frames | Ethernet, WiFi |
| 1 | Physical | Transmisión de bits | Cables, radio |
Pila TCP/IP (4 capas)
| Capa | Nombre | Equivalencia OSI | Protocolos |
|---|---|---|---|
| 4 | Application | 5-7 | HTTP, FTP, DNS |
| 3 | Transport | 4 | TCP, UDP |
| 2 | Internet | 3 | IP, ICMP |
| 1 | Network Access | 1-2 | Ethernet, WiFi |
Números de puerto importantes
Well-Known Ports (0-1023)
| Puerto | Protocolo | Servicio |
|---|---|---|
| 20 | FTP | Transferencia de archivos (datos) |
| 21 | FTP | Transferencia de archivos (control) |
| 22 | SSH | Secure Shell |
| 23 | Telnet | Terminal remota |
| 25 | SMTP | Correo electrónico |
| 53 | DNS | Sistema de nombres de dominio |
| 80 | HTTP | Web (sin cifrar) |
| 110 | POP3 | Correo (Post Office) |
| 143 | IMAP | Correo (Internet Message) |
| 443 | HTTPS | Web (cifrado) |
| 993 | IMAPS | IMAP sobre SSL |
| 995 | POP3S | POP3 sobre SSL |
Registered Ports (1024-49151)
| Puerto | Protocolo | Servicio |
|---|---|---|
| 3306 | MySQL | Base de datos |
| 5432 | PostgreSQL | Base de datos |
| 8080 | HTTP-Alt | Web (alternativo) |
| 8443 | HTTPS-Alt | Web (alternativo) |
| 27017 | MongoDB | Base de datos NoSQL |
Comparativa TCP vs UDP
| Característica | TCP | UDP |
|---|---|---|
| Conexión | Orientado a conexión | Sin conexión |
| Confiabilidad | Garantizada | No garantizada |
| Orden | Garantizado | No garantizado |
| Control de flujo | Sí | No |
| Corrección de errores | Sí | No |
| Overhead | Alto | Bajo |
| Velocidad | Más lento | Más rápido |
| Aplicaciones | Web, correo | Streaming, videojuegos |
Códigos de estado HTTP
1xx Informativo
- 100 Continue: Continuar
- 101 Switching Protocols: Cambio de protocolo
2xx Éxito
- 200 OK: Exitoso
- 201 Created: Creado
- 204 No Content: Sin contenido
3xx Redirección
- 301 Moved Permanently: Movido permanentemente
- 302 Found: Encontrado (temporal)
- 304 Not Modified: No modificado
4xx Error del cliente
- 400 Bad Request: Solicitud inválida
- 401 Unauthorized: No autorizado
- 403 Forbidden: Prohibido
- 404 Not Found: No encontrado
- 429 Too Many Requests: Demasiadas solicitudes
5xx Error del servidor
- 500 Internal Server Error: Error interno del servidor
- 502 Bad Gateway: Error de puerta de enlace
- 503 Service Unavailable: Servicio no disponible
Tipos de registros DNS
| Tipo | Descripción | Ejemplo |
|---|---|---|
| A | Dirección IPv4 | example.com → 93.184.216.34 |
| AAAA | Dirección IPv6 | example.com → 2606:2800:220:1 |
| MX | Intercambio de correo | example.com → mail.example.com |
| NS | Servidor de nombres | example.com → ns1.example.com |
| CNAME | Nombre canónico | www.example.com → example.com |
| TXT | Información de texto | example.com → “v=spf1 include:_spf.google.com ~all” |
| PTR | Puntero (inverso) | 93.184.216.34 → example.com |
Fundamentos de programación con sockets
Ciclo de vida de un socket TCP
// Servidor
1. Crear ServerSocket
2. Esperar conexión (accept)
3. Crear InputStream/OutputStream
4. Intercambiar datos
5. Cerrar conexión
// Cliente
1. Crear Socket
2. Conectar con servidor
3. Crear InputStream/OutputStream
4. Intercambiar datos
5. Cerrar conexión
Ciclo de vida de un socket UDP
// Servidor
1. Crear DatagramSocket
2. Esperar paquetes (receive)
3. Procesar paquete
4. Enviar respuesta (send)
// Cliente
1. Crear DatagramSocket
2. Enviar paquete (send)
3. Esperar respuesta (receive)
4. Cerrar socket
Ventajas y desventajas
Ventajas de los protocolos de red
- Estandarización: Interoperabilidad entre sistemas
- Escalabilidad: Compatibilidad con redes grandes
- Confiabilidad: Manejo de errores y recuperación
- Flexibilidad: Diferentes protocolos para diferentes casos de uso
Desventajas
- Complejidad: Muchas capas y protocolos
- Overhead: Datos adicionales para información de protocolo
- Seguridad: Superficies de ataque para hackers
- Performance: Latencias de red
Preguntas frecuentes de examen
-
¿Cuál es la diferencia entre TCP y UDP? TCP es orientado a conexión y confiable, UDP es sin conexión, rápido pero no confiable.
-
¡Explica el modelo OSI! El modelo OSI describe 7 capas para comunicación de red, desde Physical hasta Application.
-
¿Qué es DNS y cómo funciona? DNS traduce nombres de dominio a direcciones IP y viceversa a través de servidores de nombres jerárquicos.
-
¿Cuándo se usa HTTP vs HTTPS? HTTP para comunicación sin cifrar, HTTPS para comunicación cifrada con SSL/TLS.
Fuentes principales
- https://www.ietf.org/rfc/rfc791.html (TCP)
- https://www.ietf.org/rfc/rfc768.html (UDP)
- https://www.ietf.org/rfc/rfc1035.html (DNS)
- https://www.ietf.org/rfc/rfc2616.html (HTTP/1.1)



