COMUNICACIONES
APARTADOS
Sockets |
InetAddress |
Arquitectura Cliente-Servidor |
Soportar conexiones múltiples(Hilos) |
Un socket es el punto final de comunicación entre dos puntos.
Los sockets se usarán para el proceso de intercambio de datos entre dos procesos. La información transmitida se manipulará de forma muy similar a como la realiza un archivo.
Los dos tipos de sockets que nos encontraremos serán los "streams" y los "datagramas".
Datagramas (UDP, User Datagram Protocol). Servicio de envío sin conexión. Cada mensaje es envíada de una máquina a otra basándose únicamente en la información contenida en el propio datagrama. Cuando se envían varios datagramas pueden "tomar caminos distintos" y llegar en "cualquier orden".
Streams (TCP, Transport Control Protocol). Servicio orientado a la conexión. Se establece primero la conexión y luego se envían los paquetes.
A continuación se va a ver un ejemplo de uso de la clase InetAddress
import java.net.*;
public class comunicaciones1 |
||
public static void main(String args[]) throws UnknownHostException { byte []direccionIP; InetAddress maquina; String nombreMaquina; |
||
//Primero hay que obtener una
referencia a la máquina local mediante el método estático //public static InetAddress getLocalHost() maquina = InetAddress.getLocalHost(); //Se va a usar el siguiente método: direccionIP = maquina.getAddress(); nombreMaquina = maquina.getHostName(); nombreMaquina = maquina.getHostAddress(); |
||
} | ||
} |
Haz clic aquí para obtener el código
El servidor ofrecerá un servicio (identificado por el número de puerto) a todos los clientes que se conecten al mismo.
Un ejemplo es el servidor HTTP, el cual devolverá páginas HTML a los clientes que accedan a él mediante un browser.
Cuando un cliente se conecta a una máquina servidora tendrá que especificar:
* Dirección IP del servidor
* Servicio que quiere utilizar (número de puerto)
Se va a ver primero un ejemplo sencillo en el cual el servidor solamente podrá aceptar una única petición.
Este ejemplo estará formado por dos archivos: Cliente2.java y Servidor2.java (haz clic en cada uno de los archivos para descargartelos).
Para compilar tendrás que realizar la siguiente secuencia:
1)javac Cliente2.java
2)javac Servidor2.java
Para la ejecución habrá que realizar cada una de las sentencias en interfaces de comandos distintas (cada uno en una shell):
1)java Servidor2 numeroPuerto
2)java Cliente2 direccionIPServidor numeroPuerto
Ejecutar primero el servidor y posteriormente el cliente.
El parámetro "numeroPuerto" será un entero que indicará el puerto sobre el cual se prestará el servicio. El parámetro "direccionIPServidor" será la dirección IP del servidor.
Código del servidor:
//Se va a crear el servidor.
Para ello se usará la clase "ServerSocket" //Tendrá una gran limitación: Solamente podrá atender a un cliente, los demás //tendrían que esperar para ser atendidos import java.net.*; import java.io.*; public class Servidor2 |
||
static int puerto; ServerSocket servidor; Socket solicitudCliente;
BufferedReader entrada; String lineaTextoRecibida = null; public Servidor2(int puerto) |
||
this.puerto = puerto; try { //Para crear un servidor solamente tendremos que indicar el número de puerto sobre el cual queremos realizar el servicio //servidor = new ServerSocket(puerto); System.out.println("Dir. servidor: " + InetAddress.getLocalHost().getHostAddress()); System.out.println("Escuchando en el puerto " + String.valueOf(puerto)); //Se espera a que un cliente se conecte
solicitudCliente = servidor.accept(); //Se crea entrada/salida con cliente entrada = new BufferedReader( salida.println("Saludos del servidor " + InetAddress.getLocalHost().getHostAddress() + ", que tenga un buen dia"); System.out.println("Esperando a que el cliente introduzca un texto..."); |
||
}
catch(Exception e) |
||
}
public static void main(String args[]) |
||
int numPuerto;
if (args.length!=1) |
||
} //Método que será llamado automáticamente (pertenece a la clase Object) public void finalize() throws Throwable { servidor.close(); solicitudCliente.close(); } } |
Código del cliente:
//Cliente que se conectará
con el servidor import java.net.*; import java.io.*; public class Cliente2 public static void main(String args[]) |
||
Socket cliente = null;
//Una vía para la lectura
y otra vía para la escritura String direccionIP = null; String lineaTextoRecibida = null; //Recogida de parámetros |
||
try { direccionIP = new String(args[0]); puerto = new String(args[1]); //Las siguientes sentencias son para preparar
la entrada //Conexión con el servidor... //Antes habrá que obtener un objeto
del tipo InetAddress y a partir
System.out.println("Entrada, salida creada con exito..."); //Se lee el mensaje de bienvenida del
servidor y se escribe en pantalla //Mientras no se escriba el texto "Adios".... entrada.close(); } |
||
} } |
||
} |
EJERCICIO: Mediante sockets implementar un applet-cliente que obtenga la fecha y hora de un servidor(éste último será una aplicación independiente, es decir, stand-alone)
Soportar conexiones múltiples(Hilos)
Se verá una nueva versión del servidor que permita la conexión múltiple (hacer clic en este enlace para bajar el archivo)
Lógicamente habrá que compilar la nueva versión y luego en tres interfaces de comando distintas escribir las siguientes órdenes(primero la orden para arrancar el servidor):
1)java Server3 numeroPuerto
2)java Cliente2 direccionIP numeroPuerto
3)java Cliente2 direccionIP numeroPuerto
Experimenta la ejecución de cada uno de ellos para ver cómo puede atender de forma simultánea a los clientes que le soliciten el servicio.
Antes se va a ver la diferencia existente entre procesos e hilos.
Procesos
Un proceso tendrá una copia completa del código, tendrá
su "propio espacio de memoria". El usuario puede estar viendo una
única aplicación y sin embargo se están ejecutando múltiples
procesos de forma simultánea y además en modo cooperativo (persiguen
un objetivo o meta común). Para faciliar la comunicación entre
procesos muchos sistemas operativos ofrecen recursos de Comunicación
entre Procesos(IPC), como sockets y tuberías(pipes).
Hilos(Threads)
Los hilos comparten los recursos como memoria y archivos. Esta característica
los hace más eficientes, pero la comunicación es un poco más
compleja(pero esto generalmente será transparente para el programador).
Para implementar un hilo habrá que extender la clase "Thread"
e implementar el método "run". Para lanzar un hilo habrá
que crear un objeto y después habrá que llamar al método
"start" para arrancar el hilo.
Si examinamos el código del archivo bajado podremos encontrar los siguientes puntos importantes:
1) El constructor del hilo llama al método "start" lo cual provoca la llamada al método "run" del propio hilo
2) Existe un bucle infinito. En cada iteración se espera a que llegue algún cliente, y después se crea un hilo con dicho cliente:
//Bucle infinito
while(true)
{
//Se espera a que un cliente se conecte
solicitudCliente = servidor.accept();
//Cuando se conecte un cliente "lanzamos un hilo"
//totalmente dedicado a él. Por tanto habrá un hilo
//por petición.
new hiloCliente( servidor, solicitudCliente, puerto );
}
EJERCICIO: Hacer un applet que permita chatear (lógicamente el servidor será una aplicación independiente, stand-alone). Para realizarlo de forma sencilla, el texto que envíe un cliente lo recibirán todos los clientes(incluido el mismo cliente que envío dicho texto).