0% encontró este documento útil (0 votos)
140 vistas

Sockets en Java

El documento describe el modelo cliente-servidor y los protocolos de transporte TCP y UDP. Explica que un servidor ofrece servicios a clientes y que TCP es orientado a conexión mientras que UDP no, y proporciona ejemplos de código de sockets en Java para implementar comunicaciones cliente-servidor usando ambos protocolos.

Cargado por

Franc Müller
Derechos de autor
© © All Rights Reserved
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
140 vistas

Sockets en Java

El documento describe el modelo cliente-servidor y los protocolos de transporte TCP y UDP. Explica que un servidor ofrece servicios a clientes y que TCP es orientado a conexión mientras que UDP no, y proporciona ejemplos de código de sockets en Java para implementar comunicaciones cliente-servidor usando ambos protocolos.

Cargado por

Franc Müller
Derechos de autor
© © All Rights Reserved
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 25

El modelo cliente - servidor

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.

Figura: El modelo de aplicación cliente/servidor

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 en Java (UDP y TCP)

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);
 }
 }
}

Taller de Prácticas : Cliente y Servidor TCP en Java

Os presento una aplicación hecha en Java por mí.

Consiste en un Cliente / Servidor TCP muy sencillo, el funcionamiento es el siguiente:

– En el cliente ponemos una palabra, el servidor la leerá y la transformará en mayúsculas, a


continuación el servidor se la manda al cliente junto con la IP y el puerto usado por el cliente.

– 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.

Modo de ejecución (GNU/Linux) del SERVIDOR:

Compilar:

javac TCPServidor.java

Ejecutar:

java TCPServidor ó java TCPServidor p <puerto>

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  }

Si lo copiáis y pegáis, el código funcionará perfectamente y sin fallos.

Ahora vamos con el cliente:

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;

9  String frase = null;

10  String fraseModificada = null;


11  String direccion = null;

12

13 if (args.length == 0 || args == null) {

14  System.out.println("La direccion IP es obligatoria, el programa terminara");

15  System.exit(0);

16  } else if (args.length < 4) {

17  // ERROR SIEMPRE: EL NUMERO DE ARGUMENTOS OBLIGATORIOS ES 4

 System.out.println("El numero de argumentos es erroneo, minimo direccion y palabra: d


18
direccion m palabra");
19  System.exit(0);

20

21 }

22

23 else if (args.length > 4) {

24

25 if (args[4].equals("p")) {

26  if (args.length < 6) {

27

28 System.out.println("No se ha especificado puerto con opcion p; el programa terminara");


29  System.exit(0);

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]);

41  System.out.println("Vamos a usar la direccion IP:" + direccion);

42  } else { // si no ponemos d, que es obligatorio, fallara

43  System.out.println("El parametro debe ser d, el programa terminara");

44  System.exit(0);

45  }

46

47 if (args[2].equals("m")) {

48  frase = args[3];

49  } else {// si no ponemos m, que es obligatorio, fallara

50  System.out.println("El parametro debe ser m, el programa terminara");


51  System.exit(0);

52  }

53

54 Socket socketCliente = null;

55  try {

56  socketCliente = new Socket(direccion, port);

57  } catch (UnknownHostException e) {

58  System.out.println("El host no existe");

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  }

64  DataOutputStream salidaServidor = null;

65  try {

66  salidaServidor = newDataOutputStream(socketCliente.getOutputStream());

67  } catch (IOException e1) {

68  System.out.println("No se ha podido obtener el DataOutputStream");

69  }

70  BufferedReader entradaDesdeServidor = null;


71  try {

 entradaDesdeServidor
72
= new BufferedReader(newInputStreamReader(socketCliente.getInputStream()));

73  } catch (IOException e1) {

74  System.out.println("No se ha podido obtener el BufferedReader");

75  }

76  try {
77  salidaServidor.writeBytes(frase + 'n');

78  } catch (IOException e) {

79  System.out.println("No se ha podido escribir en cliente");

80  }

81  try {

82  fraseModificada = entradaDesdeServidor.readLine();

83  } catch (IOException e) {

84  System.out.println("No se ha podido leer la linea");

85  }

86  System.out.println("DEL SERVIDOR: " + fraseModificada);

87

88 InputStream aux = null;

89  try {

90  aux = socketCliente.getInputStream();

91  } catch (IOException e) {

92  System.out.println("No se ha podido obtener el InputStream");

93  }

94  DataInputStream flujo = new DataInputStream(aux);

95  try {

96  System.out.println(flujo.readUTF());

97  } catch (IOException e) {

98  System.out.println("Error al obtener datos del servidor");


99  }

100

101 InputStream aux2 = null;

102  try {

103  aux2 = socketCliente.getInputStream();

104  } catch (IOException e) {

105  System.out.println("No se ha podido obtener el InputStream");

106  }

107  DataInputStream flujo2 = new DataInputStream(aux2);


108  try {

109  System.out.println(flujo2.readUTF());

110  } catch (IOException e) {

111  System.out.println("Error al obtener datos del servidor");

112  }

113

114 try {

115  socketCliente.close();

116  } catch (IOException e) {
117  System.out.println("Error al cerrar el socket");

118  }

119  }
120  }

Trabajando con sockets TCP


 OBJETIVOS

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. 

OBLIGATORIO ENTREGAR EL EJERCICIO 3

 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

La clase java.net.InetAddress nos permite representar en Java direcciones IP. Con ella nos


podemos referir a los computadores por sus nombres de host en el DNS (Domain Name
Server), y es el propio Java quien realiza la consulta correspondiente para conocer la dirección
IP. 
Típicamente para crear instancias de InetAddress, se invoca el método
estático getByName(String) pasándole el nombre DNS del host como parámetro. Este objeto
representará la dirección de Internet de ese host, y se podrá utilizar para construir sockets. El
siguiente código muestra cómo se puede emplear (código fuente: DireccionIP.java):
import java.net.*;

public class DireccionIP {


public static void main(String[] args) throws Exception {
if(args.length != 1) {
System.err.println(
"Usage: DireccionIP NombreHost");
System.exit(1);
}
InetAddress a = InetAddress.getByName(args[0]);
System.out.println(a);
}
}

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

Obtenemos el siguiente resultado:


lm000.it.uc3m.es/163.117.139.214

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.

Comunicación cliente/servidor con Socket TCP

El interfaz Java que da soporte a sockets TCP está constituida por las
clases ServerSocket y Socket.

1. ServerSocket: es utilizada por un servidor para crear un socket en el puerto en el que


escucha las peticiones de conexión de los clientes. Su método accept toma una petición
de conexión de la cola, o si la cola está vacía, se bloquea hasta que llega una petición.
El resultado de ejecutar accept es una instancia de Socket, a través del cual el servidor
tiene acceso a los datos enviados por el cliente.
2. Socket: es utilizada tanto por el cliente como por el servidor. El cliente crea un socket
especificando el nombre DNS del host y el puerto del servidor, así se crea el socket
local y además se conecta con el servicio. 
Esta clase proporciona los métodos getInputStream y getOutputStream para acceder a
los dos streams asociados a un socket (recordemos que son bidireccionales), y
devuelve tipos de datos InputStream y OutputStream, respectivamente, a partir de los
cuales podemos construir BufferedReader y PrintWriter, respectivamente, para poder
procesar los datos de forma más sencilla.

Si nos centramos en la parte de comunicaciones, la forma general de implementar un cliente


será:

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.

La forma de implementar un servidor será:


1. Crear un objeto de la clase ServerSocket para escuchar peticiones en el puerto
asignado al servicio.
2. Esperar solicitudes de clientes
3. Cuando se produce una solicitud:
o Aceptar la conexión obteniendo un objeto de la clase Socket
o Obtener las referencias al stream de entrada y al de salida al socket anterior.
o Leer datos del socket, procesarlos y enviar respuestas al cliente, escribiendo en
el stream del socket.Para ello emplear alguna de las facilidades del paquete
java.io.
4. Cerrar los streams.
5. Cerrar los sockets.

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.

 Código del Cliente

Analizad el siguiente código del cliente EcoCliente.java:

import java.net.*;
import java.io.*;

public class EcoCliente {


public static void main(String[] args) throws IOException {
Socket socketCliente = null;
BufferedReader entrada = null;
PrintWriter salida = null;

// Creamos un socket en el lado cliente, enlazado con un


// servidor que está en la misma máquina que el cliente
// y que escucha en el puerto 4444
try {
socketCliente = new Socket("localhost", 4444);
// Obtenemos el canal de entrada
entrada = new BufferedReader(new
InputStreamReader(socketCliente.getInputStream()));
// Obtenemos el canal de salida
salida = new PrintWriter(new BufferedWriter(new
OutputStreamWriter(socketCliente.getOutputStream())),true);
} catch (IOException e) {
System.err.println("No puede establer canales de E/S para la
conexión");
System.exit(-1);
}
BufferedReader stdIn =
new BufferedReader(new InputStreamReader(System.in));

String linea;

// El programa cliente no analiza los mensajes enviados por


el
// usario, simplemente los reenvía al servidor hasta que este
// se despide con "Adios"
try {
while (true) {
// Leo la entrada del usuario
linea = stdIn.readLine();
// La envia al servidor
salida.println(linea);
// Envía a la salida estándar la respuesta del servidor
linea = entrada.readLine();
System.out.println("Respuesta servidor: " + linea);
// Si es "Adios" es que finaliza la comunicación
if (linea.equals("Adios")) break;
}
} catch (IOException e) {
System.out.println("IOException: " + e.getMessage());
}

// Libera recursos
salida.close();
entrada.close();
stdIn.close();
socketCliente.close();
}
}

 Código del Servidor

Analizad el siguiente código del servidor EcoServidor.java:

import java.io.*;
import java.net.*;

public class EcoServidor {


public static final int PORT = 4444;
public static void main(String[] args) throws IOException {
// Establece el puerto en el que escucha peticiones
ServerSocket socketServidor = null;
try {
socketServidor = new ServerSocket(PORT);
} catch (IOException e) {
System.out.println("No puede escuchar en el puerto: " +
PORT);
System.exit(-1);
}

Socket socketCliente = null;


BufferedReader entrada = null;
PrintWriter salida = null;

System.out.println("Escuchando: " + socketServidor);


try {
// Se bloquea hasta que recibe alguna petición de un
cliente
// abriendo un socket para el cliente
socketCliente = socketServidor.accept();
System.out.println("Connexión acceptada: "+ socketCliente);
// Establece canal de entrada
entrada = new BufferedReader(new
InputStreamReader(socketCliente.getInputStream()));
// Establece canal de salida
salida = new PrintWriter(new BufferedWriter(new
OutputStreamWriter(socketCliente.getOutputStream())),true);

// Hace eco de lo que le proporciona el cliente, hasta que


recibe "Adios"
while (true) {
String str = entrada.readLine();
System.out.println("Cliente: " + str);
salida.println(str);
if (str.equals("Adios")) break;
}

} 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. 

Ahora tendreis que hacer algunas modificaciones en los programas anteriores:

 Modificación 1:Modificad el código considerando que el servidor se encuentra en un


máquina remota, cuyo nombre se pasa como parámetro al programa cliente
 Modificación 2:El servidor anterior es poco eficiente, porque sólo atiende a un cliente
simultaneamente, ¿cómo mejoraríais su eficiencia?. Modificad el código del servidor y
verificad su funcionamiento.

 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:

1. Para proporcionar un fichero al cliente:

get "nombre_completo_fichero"
2. Para dar por finalizado el servicio:

bye

 Ejercicio 2:Modificad el programa anterior para aumentar el servicio proporcionado por


nuestro servidor, permitiendo modificar el contenido de un fichero remoto, para ello se
deberá seguir el siguiente protocolo:

1. Bloquear en el servidor el fichero que va a modificar el cliente, comando "lock".

lock "nombre_fichero"

2. Descargar el fichero al sistema de ficheros del cliente, comando "get" ( como el


comando anterior especifica el nombre de fichero, en este comando no se
incluye el nombre).
3. Una vez modificado el fichero en el sistema local, enviar el fichero modificado al
servidor, comando "put". Se guardará con el mismo nombre que tenía.
4. Desbloquear el fichero, comando "unlock nombre_fichero".

 Ejercicio 3:Modificad el programa anterior para que el servidor pueda atender


peticiones de varios clientes

 Ejercicio 4:Implentad una sencilla interfaz gráfica para el programa cliente.

Cliente TCP. Código JAVA

El siguiente código nos muestra un ejemplo de diálogo entre el cliente y el servidor.

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) {}
}

public static void main (String args[]) {


String welcome, response;
Client client;
BufferedReader reader;
PrintWriter writer;

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();

System.out.println ("Enviando comando HELP");


writer.println ("HELP");
response = reader.readLine();
System.out.println ("Respuesta del servidor: '"+ response +"'");
pausa();

System.out.println ("Enviando comando DATE");


writer.println("DATE");
response = reader.readLine();
System.out.println ("Respuesta del servidor: '"+ response +"'");
pausa();

//el siguiente comando no es entendido por el protocolo implementado por el servidor//como


vemos en la respuesta que nos devuelve
System.out.println ("Enviando comando xl");
writer.println("xl");
response = reader.readLine();
System.out.println ("Respuesta del servidor: '"+ response +"'");
pausa();

System.out.println ("Enviando comando QUIT");


writer.println("QUIT");

} catch (IOException e) {
System.out.println ("IOException en client.in.readln()");
System.out.println(e);
}
try {
Thread.sleep(2000);
} catch (Exception ignored) {}
}
}
//-------------------------------------------------------------------
class Client {

// establece los canales de entrada y de salida a disposicion de las clases de usuario


public InputStream in;
public OutputStream out;

// El socket cliente
private Socket client;

public Client (String host, int port) {


try {
client = new Socket (host, port);
System.out.println ("Datos del socket: " + client);
in = client.getInputStream();
out= client.getOutputStream();
} catch (IOException e) {
System.out.println("IOExc : " + e);
}
}
}

Servidor TCP. Código JAVA

Para poder ejecutar el servidor, siga las siguientes instrucciones:


 Seleccione el código de esta página y péguelo en un documento de texto normal, al que salvara con el
nombre: ServerTest.java.
 Instale el jdk 1.3 o superior de Sun.En la página Enlaces encontrará la dirección.
 Compile el fichero mediante la utilidad javac, para ello escriba la siguiente orden en su linea de comandos : javac
ServerTest.java
 Por último, lanze la ejecución del servidor mediante la orden:java ServerTest

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 {

final static int SERVER_PORT = 8001; // puerto de escucha de nuestro servidor

public static void main (String args[]) {


Server server;
String clientRequest;
BufferedReader reader;
PrintWriter writer;

// creamos el servidor y los canales de entrada y salida

server = new Server (SERVER_PORT);


reader = new BufferedReader (new InputStreamReader (server.in));
writer = new PrintWriter (new OutputStreamWriter (server.out), true);

// 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();

// Sacamos por pantalla la peticion del cliente


System.out.println ("Recibido :" + clientRequest);

// El protocolo de nuestro servidor solo acepta ordenes : HELP, QUIT,NAME,DATE


if (clientRequest.startsWith ("HELP")) {
writer.println ("Órdenes: HELP QUIT NAME DATE");
} else {
if (clientRequest.startsWith ("QUIT")) {
System.exit(0);

else {
if (clientRequest.startsWith("NAME"))
{InetAddress host;
host=InetAddress.getLocalHost();
writer.println("Nombre del host :"+ host.getHostName());}
else 
if (clientRequest.startsWith("DATE"))
(writer.println("Fecha del sistema :"+ new Date()));

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);

}
}
}
}
//-------------------------------------------------------------------

// Esta clase es la que implementa el socket del servidor (ServerSocket)


class Server {

private ServerSocket server;
private Socket socket;

public InputStream in;
public OutputStream out;

public Server (int port) {

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.

Un switch es básicamente un bridge multipuerto de alta velocidad. El switch es la evolución


tecnológica del bridge, al incorporar más puertos y realizar la conmutación de tramas a alta
velocidad, por lo tanto es mucho más caro.
Un Hub es un repetidor de la señal eléctrica. Es decir coge la señal que le llega por un puerto y la
reproduce íntegramente por los demás. Nada de comprobación de paquetes ni toma de decisiones.
Es un dispositivo más bien "tonto". Por eso es tan barato.

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.

Puente de red (en inglés: bridge)

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

El puente dispone de una entidad superior encargada de la transformación de cabeceras entre


distintos tipos de interfaces. Recibe tramas por una interfaz (por ejemplo: Wi-Fi) para enviarlas por
otra de otro tipo (por ejemplo: Ethernet). Un ejemplo de dispositivo, con las interfaces de ejemplo
anteriores, es un punto de acceso en una red wi-fi.

Según localización geográfica

 Puentes locales

Sirven para enlazar directamente dos redes físicamente cercanas.

 Puentes remotos o de área extensa

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

Ejemplo: Tabla del puente de red.

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.

Originalmente fue desarrollado por Digital Equipment Corporation (DEC), en la década de 1980.

Bridges frente a conmutadores

La diferencia más importante entre un bridge y un conmutador es que los bridges normalmente


tienen un número pequeño de interfaces (de dos a cuatro), mientras que los switches pueden llegar a
tener docenas; por tanto, este último necesita un diseño de prestaciones elevadas.

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.

También podría gustarte