Sockets en Java
Sockets en Java
TCP es un protocolo orientado a conexión. No hay relaciones maestro/esclavo. Las aplicaciones, sin
embargo, utilizan un modelo cliente/servidor en las comunicaciones.
Un servidor es una aplicación que ofrece un servicio a usuarios de Internet; un cliente es el que pide
ese servicio. Una aplicación consta de una parte de servidor y una de cliente, que se pueden ejecutar
en el mismo o en diferentes sistemas.
Los usuarios invocan la parte cliente de la aplicación, que construye una solicitud para ese servicio y
se la envía al servidor de la aplicación que usa TCP/IP como transporte.
El servidor es un programa que recibe una solicitud, realiza el servicio requerido y devuelve los
resultados en forma de una respuesta. Generalmente un servidor puede tratar múltiples
peticiones(múltiples clientes) al mismo tiempo.
Algunos servidores esperan las solicitudes en puertos bien conocidos de modo que sus clientes
saben a que zócalo IP deben dirigir sus peticiones. El cliente emplea un puerto arbitrario para
comunicarse. Los clientes que se quieren comunicar con un servidor que no usa un puerto bien
conocido tienen otro mecanismo para saber a qué puerto dirigirse. Este mecanismo podría usar un
servicio de registro como Portmap, que utiliza un puerto bien conocido.
Sockets
Designa un concepto abstracto por el cual dos programas (posiblemente situados en computadoras
distintas) pueden intercambiar cualquier flujo de datos, generalmente de manera fiable y ordenada.
Los sockets de Internet constituyen el mecanismo para la entrega de paquetes de datos provenientes
de la tarjeta de red a los procesos o hilos apropiados. Un socket queda definido por un par de
direcciones IP local y remota, un protocolo de transporte y un par de números de puerto local y
remoto.
Protocolos de Transporte
« UDP (User Datagram Protocol): Es un protocolo no orientado a conexión. Es decir cuando una
maquina A envía paquetes a una maquina B, el flujo es unidireccional. La transferencia de datos es
realizada sin haber realizado previamente una conexión con la máquina de destino (maquina B), y el
destinatario recibirá los datos sin enviar una confirmación al emisor (la maquina A). Esto es debido a
que la encapsulación de datos enviada por el protocolo UDP no permite transmitir la información
relacionada al emisor. Por ello el destinatario no conocerá al emisor de los datos excepto su IP.
« TCP (Transmission Control Protocol): Contrariamente a UDP, el protocolo TCP está orientado a
conexión. Cuando una máquina A envía datos a una máquina B, la máquina B es informada de la
llegada de datos, y confirma su buena recepción. Aquí interviene el control CRC de datos que se basa
en una ecuación matemática que permite verificar la integridad de los datos transmitidos. De este
modo, si los datos recibidos son corruptos, el protocolo TCP permite que los destinatarios soliciten al
emisor que vuelvan a enviar los datos corruptos.
Socket TCP
Basicamente, este es el funcionamiento de los Socket que necesitamos para una conexión TCP. En el
que podemos distinguir dos tipos de Socket el del Servidor y el del Cliente.
La creación del socket en el servidor se remite a crear el socket, indicar por que puerto se harán las
escuchas y esperar a la llamada de un cliente para aceptar la conexión, en cambio un cliente creará el
socket e indicará donde se encuentra y por que puerto quiere conectarse, de está forma Cliente y
Servidor crearán una conexión.
Servidor:
Para crear los socket se crea un objeto del tipo ServerSocket, este método pertenece a la clase
java.net.Serversocket
Una vez que hemos creado el objeto socket mandamos un parámetro que indicará el puerto por el
que se realzará las comunicaciones.
Para realizar una conexión entre Cliente-Servidor, el servidor usará el método socket.accept para
confirmar que se ha iniciado la conexión.
Cliente:
Primero crea un objeto del tipo Socket que pertenece a la clase java.net.Serversocket, Después se
obtiene un objeto InetAddress, y usando el método getByName le indicamos donde se va a ejecutar
el cliente, en nuestro caso indicamos que será en localhost.
Finalmente creamos un objeto de tipo socket al que pasaremos la dirección donde se está
ejecutando el cliente, y el puerto por donde se conectará al servidor.
Ejemplo TCP: El servidor esperará a un cliente y mostrará todos los mensajes que el cliente le envíe.
El cliente solo mandará mensajes al servidor, y al escribir la palabra “fin” terminarán ambos
programas.
– Servidor TCP:
import java.net.*;
//importar la libreria java.net
import java.io.*;
//importar la libreria java.io
// declaramos la clase servidortcp
public class servidortcp {
// método principal main de la clase
public static void main(String argv[]) {
// declaramos un objeto ServerSocket para realizar la comunicación
ServerSocket socket;
// creamos una varible boolean con el valor a false
boolean fin = false;
// Declaramos un bloque try y catch para controlar la ejecución del subprograma
try {
// Instanciamos un ServerSocket con la dirección del destino y el
// puerto que vamos a utilizar para la comunicación
socket = new ServerSocket(6000);
// Creamos un socket_cli al que le pasamos el contenido del objeto socket después
// de ejecutar la función accept que nos permitirá aceptar conexiones de clientes
Socket socket_cli = socket.accept();
// Declaramos e instanciamos el objeto DataInputStream
// que nos valdrá para recibir datos del cliente
DataInputStream in =
new DataInputStream(socket_cli.getInputStream());
// Creamos un bucle do while en el que recogemos el mensaje
// que nos ha enviado el cliente y después lo mostramos
// por consola
do {
String mensaje ="";
mensaje = in.readUTF();
System.out.println(mensaje);
} while (1>0);
}
// utilizamos el catch para capturar los errores que puedan surgir
catch (Exception e) {
// si existen errores los mostrará en la consola y después saldrá del
// programa
System.err.println(e.getMessage());
System.exit(1);
}
}
}
– Cliente TCP:
import java.net.*;
// importar la libreria java.net
import java.io.*;
// importar la libreria java.io
// declararamos la clase clientetcp
public class clientetcp {
// método principal de la clase
public static void main(String argv[]) {
// Creamos una instancia BuffererReader en la
// que guardamos los datos introducido por el usuario
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
// declaramos un objeto socket para realizar la comunicación
Socket socket;
// declaramos e instanciamos un objeto de tipo byte
byte[] mensaje_bytes = new byte[256];
// declaramos una variable de tipo string
String mensaje="";
// Declaramos un bloque try y catch para controlar la ejecución del subprograma
try {
// Instanciamos un socket con la dirección del destino y el
// puerto que vamos a utilizar para la comunicación
socket = new Socket("127.0.0.1",6000);
// Declaramos e instanciamos el objeto DataOutputStream
// que nos valdrá para enviar datos al servidor destino
DataOutputStream out =
new DataOutputStream(socket.getOutputStream());
// Creamos un bucle do while en el que enviamos al servidor el mensaje
// los datos que hemos obtenido despues de ejecutar la función
// "readLine" en la instancia "in"
do {
mensaje = in.readLine();
// enviamos el mensaje codificado en UTF
out.writeUTF(mensaje);
// mientras el mensaje no encuentre la cadena fin, seguiremos ejecutando
// el bucle do-while
} while (!mensaje.startsWith("fin"));
}
// utilizamos el catch para capturar los errores que puedan surgir
catch (Exception e) {
// si existen errores los mostrará en la consola y después saldrá del
// programa
System.err.println(e.getMessage());
System.exit(1);
}
}
}
Socket UDP:
En este ejemplo vemos que cada paquete de datos podrá tansportar un máximo de 256 bytes por
paquete, que es el tamaño máximo que se intercambia el servidor y el cliente.
Además, cuando queremos enviar datos, especificamos el buffer de los datos que queremos enviar,
en nuestro caso 256, la longitud máxima de datos, la dirección y el puerto de destino del datagrama.
La dirección destino se especifica con el objeto InetAddress, mientras que el puerto es un número
entero (6000). El código esta bastante comentado y tiene bastantes explicaciones que pueden
ayudaros.
Comentando un poco el código, podemos ver que el cliente para enviar datos usará el método send()
de la clase DatagremSocket.
Por otro lado el servidor para recibir datos lo que hace es crear un DatagramSocket para recibir
paquetes especificando el número de puerto en el constructor. De esta forma, el servidor estará
esperando por el puerto especificado cualquier paquete entrante.
Ejemplo UDP: El servidor esperará a un cliente y mostrará respuesta si se le envía “hola” o “fin”. El
cliente solo mandará mensajes al servidor, y al escribir la palabra “fin” terminará su ejecución.
– Servidor UDP:
import java.net.*;
import java.io.*;
public class servidorudp {
public static void main(String argv[]) {
DatagramSocket socket;
boolean fin = false;
try {
//Creamos el socket
socket = new DatagramSocket(6000);
byte[] mensaje_bytes = new byte[256];
String mensaje ="";
mensaje = new String(mensaje_bytes);
String mensajeComp ="";
DatagramPacket paquete = new DatagramPacket(mensaje_bytes,256);
DatagramPacket envpaquete = new DatagramPacket(mensaje_bytes,256);
int puerto;
InetAddress address;
byte[] mensaje2_bytes = new byte[256];
//Iniciamos el bucle
do {
// Recibimos el paquete
socket.receive(paquete);
// Lo formateamos
mensaje = new String(mensaje_bytes).trim();
// Lo mostramos por pantalla
System.out.println(mensaje);
//Obtenemos IP Y PUERTO
puerto = paquete.getPort();
address = paquete.getAddress();
if (mensaje.startsWith("fin")) {
mensajeComp="chauuuuuuu cliente";
}
if (mensaje.startsWith("hola")) {
mensajeComp="hola cliente";
}
//formateamos el mensaje de salida
mensaje2_bytes = mensajeComp.getBytes();
//Preparamos el paquete que queremos enviar
envpaquete = new
DatagramPacket(mensaje2_bytes,mensajeComp.length(),address,puerto);
// realizamos el envio
socket.send(envpaquete);
} while (1>0);
}
catch (Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
}
– Cliente UDP:
import java.net.*;
import java.io.*;
//declaramos la clase udp
public class clienteudp {
public static void main(String argv[]) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
//Definimos el sockets, número de bytes del buffer, y mensaje.
DatagramSocket socket;
InetAddress address;
byte[] mensaje_bytes = new byte[256];
String mensaje="";
mensaje_bytes=mensaje.getBytes();
//Paquete
DatagramPacket paquete;
String cadenaMensaje="";
DatagramPacket servPaquete;
byte[] RecogerServidor_bytes = new byte[256];
try {
socket = new DatagramSocket();
address=InetAddress.getByName("localhost");
do {
mensaje = in.readLine();
mensaje_bytes = mensaje.getBytes();
paquete = new DatagramPacket(mensaje_bytes,mensaje.length(),address,6000);
socket.send(paquete);
RecogerServidor_bytes = new byte[256];
//Esperamos a recibir un paquete
servPaquete = new DatagramPacket(RecogerServidor_bytes,256);
socket.receive(servPaquete);
//Convertimos el mensaje recibido en un string
cadenaMensaje = new String(RecogerServidor_bytes).trim();
//Imprimimos el paquete recibido
System.out.println(cadenaMensaje);
} while (!mensaje.startsWith("fin"));
}
catch (Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
}
– Podremos elegir los puertos a usar con el comando p, si no ponemos puerto, se pondrá por defecto
el 2510.
Se tratan excepciones y control de errores, si activamos el cliente antes de abrir el servidor nos saldrá
la excepción, y así con todos los demás posibles errores que nos vayamos a encontrar.
El código no es, ni mucho menos perfecto, siempre se puede mejorar, pero espero que os de una
idea de lo que hay que hacer para hacer por ejemplo, un cliente / servidor FTP…o un cliente de email.
Compilar:
javac TCPServidor.java
Ejecutar:
Código:
1 import java.io.*;
2 import java.net.*;
3 class TCPServidor
4 {
5
6 public static void main(String args[]) throws Exception
7 {
System.out.println("El servidor va a ponerse a la escucha, un momento mientras se leen los
8
parametros");
9 int port = 2510;
10 if (args.length == 0 || args==null) {
System.out.println("El puerto no se ha especificado, se usara el puerto por defecto:
11
2510");
12 } else if (args[0].equals("p")) {
13 port = Integer.parseInt(args[1]);
14 System.out.println("Vamos a usar el puerto:"+port);
15 }
16 else {
17 System.out.println("Debes especificar la opcion p");
18 }
19 String fraseCliente;
20 String fraseMayusculas;
21 ServerSocket socketAtendido = null;
22 try {
23 socketAtendido = new ServerSocket(port);
24 } catch (IOException e)
25 {
26 System.out.println("No se ha podido levantar el servidor");
27 System.exit ( 0 );
28 }
29 System.out.println("Servidor a la escucha");
30 while (true) {
31 Socket socketConexion = null;
32 try {
33 socketConexion = socketAtendido.accept();
34 } catch (IOException e)
35 {
36 System.out.println("No se ha podido crear el nuevo socket");
37 }
38 //devuelve lo introducido en el cliente pero en mayusculas
39 BufferedReader entradaDesdeCliente = null;
40 try {
41 entradaDesdeCliente
= new BufferedReader(newInputStreamReader(socketConexion.getInputStream()));
42 } catch (IOException e)
43 {
44 System.out.println("Error en el flujo de datos de entrada");
45 }
46
47 DataOutputStream salidaACliente = null;
48 try {
49 salidaACliente = newDataOutputStream(socketConexion.getOutputStream());
50 } catch (IOException e)
51 {
52 System.out.println("Error en el flujo de datos de salida");
53 }
54
55 fraseCliente = entradaDesdeCliente.readLine();
56 fraseMayusculas = fraseCliente.toUpperCase() + 'n';
57 salidaACliente.writeBytes(fraseMayusculas);
58 //devuelve la direccion IP del cliente
59 OutputStream aux = null;
60 try {
61 aux = socketConexion.getOutputStream();
62 } catch (IOException e)
63 {
64 System.out.println("Error obtener socket");
65 }
66 DataOutputStream flujo = new DataOutputStream(aux);
67 try {
68 flujo.writeUTF ( "Tu direccion IP es:"+socketConexion.getInetAddress() );
69 } catch (IOException e)
70 {
71 System.out.println("Error al escribir al cliente");
72 }
73 //devuelve el puerto usado por el cliente
74 OutputStream aux2 = null;
75 try {
76 aux2 = socketConexion.getOutputStream();
77 } catch (IOException e)
78 {
79 System.out.println("Error obtener socket");
80 }
81 DataOutputStream flujo2 = new DataOutputStream(aux2);
82 try {
83 flujo2.writeUTF ( "Tu puerto es:"+socketConexion.getLocalPort() );
84 } catch (IOException e)
85 {
86 System.out.println("Error al escribir al cliente");
87 }
88 }
89 }
90 }
Compilar:
javac TCPCliente.java
Ejecutar:
java TCPCliente.java d <DIRECCION IP> m <MENSAJE> p <PUERTO (OPCIONAL)>
Código:
1 import java.io.*;
2 import java.net.*;
4 class TCPCliente {
6 public static void main(String args[]) {
System.out.println("El cliente se pone en marcha, un momento mientras se leen los
7
parametros");
8 int port = 2510;
12
15 System.exit(0);
16 } else if (args.length < 4) {
20
21 }
22
23 else if (args.length > 4) {
24
25 if (args[4].equals("p")) {
26 if (args.length < 6) {
27
30 } else {
31 port = Integer.parseInt(args[5]);
32 }
33
34 System.out.println("Vamos a usar el puerto:" + port);
35
36 }
37 }
38
39 if (args[0].equals("d")) {
40 direccion = (args[1]);
44 System.exit(0);
45 }
46
47 if (args[2].equals("m")) {
48 frase = args[3];
52 }
53
55 try {
57 } catch (UnknownHostException e) {
59 System.exit(0);
60 } catch (IOException e) {
61 System.out.println("***No se ha podido conectar: El host no esta levantado");
62 System.exit(0);
63 }
65 try {
66 salidaServidor = newDataOutputStream(socketCliente.getOutputStream());
67 } catch (IOException e1) {
69 }
entradaDesdeServidor
72
= new BufferedReader(newInputStreamReader(socketCliente.getInputStream()));
73 } catch (IOException e1) {
75 }
76 try {
77 salidaServidor.writeBytes(frase + 'n');
78 } catch (IOException e) {
80 }
81 try {
82 fraseModificada = entradaDesdeServidor.readLine();
83 } catch (IOException e) {
85 }
87
89 try {
90 aux = socketCliente.getInputStream();
91 } catch (IOException e) {
93 }
95 try {
96 System.out.println(flujo.readUTF());
97 } catch (IOException e) {
100
102 try {
104 } catch (IOException e) {
106 }
109 System.out.println(flujo2.readUTF());
110 } catch (IOException e) {
112 }
113
114 try {
115 socketCliente.close();
116 } catch (IOException e) {
117 System.out.println("Error al cerrar el socket");
118 }
119 }
120 }
Las clases URL y URLConnector proporcionan mecanismos a muy alto nivel de acceder a
recursos de Internet, básicamente creábamos aplicaciones clientes que se conectaban a un
servidor Web que nos proporcionaba el recurso requerido.
Ahora vamos a crear nuestros propios servicios y clientes que puedan acceder a esos servicios,
es decir, vamos a desarrollar aplicaciones cliente-servidor, para ello necesitamos mecanismos a
más bajo nivel de comunicaciones en red.
La comunicación tanto a través de TCP como de UDP utiliza la abstracción de sockets para
comunicar procesos entre sí. Esta comunicación consiste en la transmisión de un mensaje entre
el socket de un proceso (cliente) y el socket de otro proceso (servidor).
En esta primera práctica veremos como da soporte Java a Socket TCP.
ENUNCIADO
En este práctica veremos las clases de Java que nos permite trabajan de forma sencilla con
sockets TCP. Antes, vamos a ver una facilidad de Java para manejar direcciones de Internet.
Clase InetAddress
Este programa al pasarle un nombre de un host, nos devuelve la dirección IP que tiene
asignada, asi:
java DireccionIP lm000.it.uc3m.es
NOTA:Aunque en el ejemplo, no se han tratado excepciones, este método puede devolver una
excepción del tipo UnknownHostException (host desconocido). Modificad el programa para
capturar esta excepción.
El interfaz Java que da soporte a sockets TCP está constituida por las
clases ServerSocket y Socket.
1. Crear un objeto de la clase Socket, indicando host y puerto donde corre el servicio.
2. Obtener las referencias al stream de entrada y al de salida al socket.
3. Leer desde y escribir en el stream de acuerdo al protocolo del servicio. Para ello
emplear alguna de las facilidades del paquete java.io.
4. Cerrar los streams.
5. Cerrar el socket.
Vamos a ver todo esto con un sencillo ejemplo: una aplicación cliente/servidor de eco, es decir,
el servidor repite lo mismo que le envía el cliente, hasta que el cliente quiere finalizar el servicio,
para lo cual envía la palabra "Adios" al servidor.
import java.net.*;
import java.io.*;
String linea;
// Libera recursos
salida.close();
entrada.close();
stdIn.close();
socketCliente.close();
}
}
import java.io.*;
import java.net.*;
} catch (IOException e) {
System.out.println("IOException: " + e.getMessage());
}
salida.close();
entrada.close();
socketCliente.close();
socketServidor.close();
}
}
Para probar como funciona este sencillo programa, debereis arrancar primero el servidor, que
se quedará a la escucha de peticiones, y despues el cliente. Si lo haceis en orden inverso, al
arrancar el cliente se producirá una IOException.
CONSEJO: Probad que ocurre cuando se producen errores: se muere el programa cliente, se
muere el programa servidor...y modificad los programas si lo considerais oportuno.
MÁS EJERCICIOS
NOTA: Considerad el caso más sencillo: los ficheros que se transmiten son de texto. Para
indicar la finalización de una transferencia utilizad "\0". Para transmitir un error del servidor al
cliente, utilizad un código que no se correspondan a un caracter alfanumérico, por ejemplo,
ESC(0x1B).
Ejercicio 1:Cread un servidor que abra un fichero solicitado por un cliente y se lo envíe
a través de la red. El servidor aceptará el siguiente formato de peticiones:
get "nombre_completo_fichero"
2. Para dar por finalizado el servicio:
bye
lock "nombre_fichero"
En la ejecución del cliente, deberá pasarle como parámetro el nombre o dirección del servidor. Ejemplo:
Java Clientest localhost
En este ejemplo, el cliente establecerá la comunicación en el puerto 8001 y en el ordenador local, ya que como parámetro se le ha
pasado localhost
El lado cliente de una aplicación TCP/IP descansa en la clase Socket. De nuevo, mucho del trabajo
necesario para establecer la conexión lo ha realizado la clase Socket.
import java.io.*;
import java.net.*;
public class ClientTest {
public static void pausa(){
try {
Thread.sleep(3000);
} catch (Exception ignored) {}
}
client = new Client (args[0], 8001); //la clase Client Implementa el socket cliente al que se le pasa el
argumento 0 que contendrá la dirección o nombre del servidor
try {
//creamos los canales de entrada/salida para comunicarnos con el servidor
reader = new BufferedReader (new InputStreamReader (client.in));
writer = new PrintWriter (new OutputStreamWriter (client.out), true);
//leemos la bienvenida que nos da el servidor
welcome = reader.readLine();
pausa();
System.out.println ("Mensaje procedente del servidor: '"+ welcome +"'");
//para ver el funcionamiento, enviamos los comandos
System.out.println ("Enviando comando NAME");
writer.println("NAME");
response = reader.readLine();
System.out.println ("Respuesta del servidor: '"+ response +"'");
//pausa
pausa();
} catch (IOException e) {
System.out.println ("IOException en client.in.readln()");
System.out.println(e);
}
try {
Thread.sleep(2000);
} catch (Exception ignored) {}
}
}
//-------------------------------------------------------------------
class Client {
// El socket cliente
private Socket client;
Puede comprobar el funcionamiento del servidor mediante el uso de un programa cliente, o bien, mediante el uso de telnet. Para ello,
ejecute telnet con la dirección ip donde este ejecutandose el servidor, si está en el mismo ordenador, la ip es 127.0.0.1 y el puerto de
comunicaciones es el 8001
Haga click en este enlace para lanzar el telnet en el ordenador local y el puerto de escucha 8001.
En el siguiente ejemplo, se desarrolla un mínimo servidor TCP. La aplicación servidor TCP depende de
una clase de comunicaciones proporcionada por Java: ServerSocket. Esta clase realiza la mayor parte
del trabajo de crear un servidor. El servidor implementado escucha por el puerto de comunicaciones
8001, y acepta órdenes del tipo HELP, DATE,QUIT y NAME.
import java.util.*;
import java.io.*;
import java.net.*;
public class ServerTest {
// En cuanto se establece una conexión por parte del cliente, enviamos un saludo
writer.println ("Bienvenido al Servidor: " + new Date() + "/n");
while (true) {
try {
// leemos del canal de entrada la petición del cliente
clientRequest = reader.readLine();
else {
writer.println ("ERROR: Comando :'" + clientRequest +"' no reconocido, use HELP");
}
}
}
} catch (IOException e) {
System.out.println ("Excepción en el servidor " + e);
System.exit(0);
}
}
}
}
//-------------------------------------------------------------------
private ServerSocket server;
private Socket socket;
public InputStream in;
public OutputStream out;
try {
server = new ServerSocket (port);
System.out.println ("Servidor Java Activo! \n");
System.out.println(""+server+"\n");
// Espera suspendido hasta que un cliente establece una conexión
socket = server.accept();
in = socket.getInputStream();
out = socket.getOutputStream();
} catch (IOException e) {
System.out.println ("Excepción en el constructor server: " + e);
}
}
}
Un "Bridge" o "puente" es un dispositivo que comunica redes de distinta naturaleza FÍSICA, por lo
que se dice que opera a nivel 2 (o nivel de enlace físico). Por encima, completamente ignorado queda
el nivel 3 (de protocolo de red), por lo que no importa que los protocolos usados sean distintos (ej IP
e IPX). ¿El uso más típico de un puente? Por ejemplo para conectar una ethernet normal (802.3) con
una de paso en anillo tipo Token Ring (802.4). También se usan para limitar y/o controlar trafico en
una misma red.
Gateway se suele llamar al dispositivo que comunica dos redes, o más bien una red con "el exterior".
Según esta definición un gateway puede ser cualquier equipo de red con al menos 2 puertos a los
que se conectan redes distintas (tanto lógicas como físicas).
Un gateway -puede- ser un ordenador, pero normalmente la función de gateways la hacen los
routers. Un router como sabes es un dispositivo FÍSICO (serie 7200 de Cisco) que se encarga de dirigir
paquetes entre dos redes distintas en función de su red destino. Esta función también puede ser
hecha por software (seria el caso en que utilizases un ordenador) y para esto te recomendaría un
sistema Linux con el kernel convenientemente depurado y realizando unicamente esta función.
Según interfaz
Puentes homogéneos
Interconecta LAN con el mismo protocolo MAC (el nivel físico puede diferir), es decir, no hay
conversión de protocolos a nivel 2, simplemente almacenamiento y reenvío de tramas. Un ejemplo
de dispositivo homogéneo es un Switch Ethernet.
Puentes heterogéneos
Puentes locales
Se conectan en parejas enlazando dos o más redes locales y formando una red de área extensa
(WAN) a través de líneas telefónicas.
Autoaprendizaje
Los puentes de red usan una tabla de reenvío para enviar tramas a lo largo de los segmentos de la
red. Si una dirección de destino no se encuentra en la tabla, la trama es enviada por medio
de flooding a todos los puertos del bridge excepto por el que llegó. Por medio de este envío “masivo”
de tramas el dispositivo de destino recibirá el paquete y responderá, quedando así registrada la
dirección destino como una entrada de la tabla. Dicha tabla incluye tres campos: dirección MAC,
interfaz a la que está conectada y la hora a la que llegó la trama (a partir de este campo y la hora
actual se puede saber si la entrada está vigente en el tiempo). El bridge utilizará esta tabla para
determinar qué hacer con las tramas que le llegan.
En el caso de un bridge de dos puertos, la tabla de reenvío puede considerarse como un filtro:
el bridge lee en la trama la dirección del destinatario y decide si enviarlo o filtrarlo (desechando dicha
trama). Es decir, si el bridge determina que el nodo de destino está ubicado en otro segmento de la
red, lo retransmite. En caso de detectar que la trama lleva como destino un nodo del mismo
segmento de red, la trama se descarta.
El término de autoaprendizaje se utiliza también para dispositivos con más de dos puertos. Como
ejemplo, considerando tres equipos (A, B y C) conectados a los puertos (1, 2 y 3, respectivamente) de
un bridge; inicialmente la tabla está vacía y ocurre lo siguiente: el equipo “A” envía una trama al “B”,
por lo que el bridge examina la dirección de origen y al no existir ninguna entrada, la crea para “A”. A
continuación comprueba la dirección de destino y la busca en la tabla. Como no existe se envía dicha
trama por los puertos 2 y 3. Una vez la trama sea recibida por “B”, este responde a dicha trama y se
crea una nueva entrada para “B” en la tabla. Cuando “C” recibe el envío, al no ser este el
destinatario, simplemente se desecha el paquete. A partir de este momento es posible enviar
paquetes entre “A” y “B” utilizando sólo el ancho de banda necesario. En el caso de “C” se repetirá el
mismo proceso anterior cuando sea conveniente, quedando guardada la información en la tabla.
Bridges frente a conmutadores
Bridges frente a hubs
La principal diferencia entre un bridge y un hub es que el segundo repite todas las tramas con
cualquier destino para el resto de los nodos conectado; en cambio el primero sólo reenvía las tramas
pertenecientes a cada segmento. De esta forma se aíslan dominios de colisión mejorando el
rendimiento de las redes interconectadas: se disminuye el tráfico inútil, permite un mayor caudal de
transmisión, proporciona mayor cobertura geográfica y permite dar servicio a más dispositivos.
Bridges frente a routers
Tanto un bridge como un router son dispositivos que se utilizan para encaminar datos, pero lo hacen
de diferente manera. Los bridges operan en la capa 2 (nivel de enlace de datos), mientras que
los routers lo hacen en la capa 3 (nivel de red) del modelo OSI. Es decir, el bridge toma sus decisiones
sobre la base de la dirección MAC y el router lo hará a partir de una dirección IP. Esto se traduce en
que los bridges no son capaces de discernir entre subredes, mientras que los routers sí lo son.
Cuando se diseña una red se puede optar por múltiples opciones, como juntar varios segmentos
mediante un bridge o dividirla en subredes e interconectarla mediante routers. Para este último caso,
si un equipo conectado a una subred se mueve físicamente a otra subred, ha de cambiarse la IP para
tener conexión. Sin embargo, si un equipo se mueve dentro de una red conectada
mediante bridges no haría falta reconfigurar nada.