Download Sockets en Java

Document related concepts
no text concepts found
Transcript
El servicio de “echo” con
sockets
PRÁCTICA 2
E
l objetivo de esta práctica es crear y ejecutar una aplicación cliente servidor "echo"
basada en sockets TCP pero estructurándola según el modelo de objetos distribuidos.
La comunicación remota se realizará utilizando el método de RPC (Remote Procedure Call)
o invocaciones remotas a procedimientos (métodos de objetos en este caso). Esta primera
práctica de comunicación remota pretende demostrar cómo realizar los stubs y esqueletos
de la RPC manualmente. En las siguientes prácticas ya se generarán de forma automática.
2.1.- Estructura de la aplicación
La aplicación Echo se estructura en tres paquetes: la interfaz, el cliente y el servidor, los
cuales se describen a continuación. Ver figura 1 (pág. 16).
2.1.1.- El paquete interfaz (rmi)
Consta del siguiente fichero:
DYA
15
Estructura de la aplicación
Misma interfaz
Paquete: server
Paquete: client
Echo.java
Paquete: rmi
EchoInt.java
EchoStub.java
EchoServer.java
echo(s: String)
C
EchoObject.java
echo:(s: String)
S
Código de red
FIGURA 1.
Estructura de una RPC
• EchoInt.java: describe el servicio "echo". Su finalidad es proporcionar a este servicio una interfaz de invocación a objeto remoto, ocultando el hecho de que la comunicación se realiza
mediante sockets. Este fichero se encuentra completamente implementado. Visite este código y
observe que lo único sorprendente de este código es la propagación de la siguiente excepción de
RMI:
throws java.rmi.RemoteException: representa cualquier error de comunicación remota.
Cualquier excepción de comunicación con sockets debe ser reconvertido a esta excepción. Esto se
realiza para mantener una uniformidad de Interfaz con la práctica siguiente sobre RMI. Allí se comprenderá plenamente.
2.1.2.- El paquete servidor (server)
Consta, básicamente, de los siguientes ficheros:
• EchoObject.java: implementa la interfaz EchoInt y proporciona el servicio de "echo". La
implementación de ste servicio consiste en devolver la cadena que se envía, junto con el URL y
la hora de la máquina servidora al cabo de 3 segundos. Este retraso simula que el servicio tiene
un tiempo de cómputo largo y apreciable. Visite este código.
• EchoServer.java: es el esqueleto de un servidor secuencial que realiza las siguientes operaciones:
- Recibe una conexión a través de un socket
16
El servicio de “echo” con sockets
Realización de la parte básica de la práctica
- Invoca un objeto de l clase EchoObject.java
- Devuelve la respuesta de la anterior invocación por el socket
Este fichero se encuentra completamente implementado. Visite este código y observe el manejo de
sockets.
Existe también una segunda versión multihilo de EchoServer.java denominada EchoMultiServer.java que se analizará más adelante.
2.1.3.- El paquete cliente (client)
Consta, básicamente, de los siguientes ficheros:
• Echo.java: es el cliente propiamente dicho. Realiza el siguiente bucle:
- Leer de teclado
- Invocar el stub
- Imprimir el resultado por pantalla.
Visite este código y observe que existen EJERCICIOS.
• EchoObjectStub.java: es el proxy del objeto en el nodo del cliente (stub del cliente). Observe
que implementa la misma interfaz que el objeto: interfaz EchoInt y, adicionalmente, el método
setHostAndPort, para especificar con que host y port se van a realizar las conexiones. Visite
este código y observe que existen EJERCICIOS.
Existe también una segunda versión de este fichero denominada EchoObjectStub4.java que implementa una política diferente de conexión/desconexión con el servidor que se analizará más adelante.
2.2.- Realización de la parte básica de la práctica
Para la realización de la parte básica de la práctica cree un proyecto prj-sockets y siga la
metodología descrita en la práctica 1 para el desarrollo de una aplicación Java.
1.
2.
DYA
Descargue los ficheros de ayuda al directorio de descargas.
Cree un proyecto prj-sockets en el workspace según se indica en la práctica 1 cree también los
paquetes de que consta la aplicación: rmi, client, server.
17
Realización de la parte básica de la práctica
2.2.1.- Versión no distribuida
En primer lugar se desarollará una versión no distribuida de la práctica con “llamada local a
procedimiento”. Se asumirá que la parte cliente y la servidora se encuentran en la misma máquina y
el programa cliente invoca los servicios mediante invocación usual de un método Java. En esta versión no existen stubs. Ver figura 2 (pág. 18).
FIGURA 2.
Versión no distribuida de la práctica
Para ello:
3.
Incluya en el proyecto los ficheros necesarios para esta parte, copiándolos del directorio de descargas al workspace y actualizando la visión del Package explorer.
- Paquete rmi: fichero EchoInt.java . El fichero se encuentra completamente terminado.
- Paquete server: fichero EchoObject.java. El fichero se encuentra completamente terminado.
- Paquete client: fichero Echo.java. Incluya en este fichero una invocación local.
4.
Escriba el código necesario y ejecute la aplicación.
2.2.2.- Versión distribuida
Posteriormente incluya los stubs necesarios en cada paquete y desarrolle la versión distribuida de la
aplicación:
5.
Desarrolle el paquete server:
- Copie el fichero EchoServer.java del directorio de descargas al workspace.
- Realice los ejercicios propuestos.
- Ejecute y depure EchoSever.java utilizando el menú Run... y proporcionado los parámetros
de ejecución que sean necesarios.
6.
Desarrolle el paquete client:
- Copie el fichero EchoObjectStub.java del directorio de descargas al workspace.
- Realice los ejercicios propuestos.
18
El servicio de “echo” con sockets
Realización de variantes de la práctica
- Ejecute y depure Echo.java utilizando el menú Run... y proporcionado los parámetros de ejecución que sean necesarios.
7.
Compruebe el correcto funcionamiento del cliente y servidores del servicio de echo conectándose con servidores remotos desarrollados por sus compañeros (y viceversa).
2.3.- Realización de variantes de la práctica
En relación con esta práctica se proponen realizar dos ejercicios más avanzados con variantes del stub del cliente y del esqueleto del servidor. Estas variantes se proponen en los siguientes
puntos.
2.3.1.- Servidor de echo multihilo.
Un servidor de echo multihilo es un servidor que debe ser capaz de atender varias peticiones
concurrentemente. La ejecución solapada de las diferentes peticiones se puede observar creando
varios clientes e iniciando peticiones de servicio desde todos ellos de forma más o menos simultánea. La duración de tres segundos para la ejecución del servicio permitirá observar que las ejecuciones se solapan en el tiempo.
Para la realización de este servicio, deberá realizar un nuevo esqueleto del servidor cuya implementación parcial se proporciona en el fichero EchoMultiServer.java. que sustituirá al antiguo esqueleto monohilo del servidor EchoServer.java. El fichero EchoObject.java que implementa el
servicio, será el mismo que en el caso anterior.
2.3.2.- Stub del cliente con desconexión por timeout
En este apartado se propone sustituir el stub del cliente de echo EchoObjectStub. java por
un nuevo stub EschoObjectStub2. java que gestione las conexiones con el servidor con otra política. El stub original cabría una conexión al principio de cada petición y la cerraba al final de la
misma. Para evitar la sobrecarga de abrir y cerrar conexiones cuando se producen peticiones muy
frecuentes se propone realizar un nuevo stub del cliente con la siguiente política de gestión de
conexiones:
- Cuando termina una petición el stub del cliente programa la desconexión con el servidor para la cabo de 5 segundos.
- Si el cliente realiza una petición y existe una conexión establecida, se envían los datos
al servidor por la conexión existente, sino, se abre una nueva conexión y se envían los datos
al servidor.
DYA
19
Ficheros de apoyo
- Si durante los cinco segundos siguientes a una invocación no llegan nuevas peticiones,
se cierra automáticamente la conexión.
La figura figura 3 (pág. 20) (a) muestra un cronograma de conexiones y desconexiones que indica
cuándo debe hacerse una conexión, cuándo debe programarse una desconexión, cuando debe desconectarse y cuándo debe cancelarse una desconexión programada. Asimismo, la figura (b) ilustra
cómo debe evitarse que la desconexión ocurra estando una petición en marcha
FIGURA 3.
gestión de la desconexión con timeout en stubs
Se recomienda realizar una clase Timeout en base a las clases Timer y TimerTask:
public class Timer extends Object
A facility for threads to schedule tasks for future execution in a background thread.
Tasks may be scheduled for one-time execution, or for repeated execution at regular intervals.
public abstract class TimerTask extends Object implements Runnable
A task that can be scheduled for one-time or repeated execution by a Timer
2.4.- Ficheros de apoyo
2.4.1.- Fichero rmi/EchoInt.java
package rmi;
public interface EchoInt extends java.rmi.Remote {
public String echo(String input)throws java.rmi.RemoteException;
}
20
El servicio de “echo” con sockets
Ficheros de apoyo
2.4.2.- Fichero server/EchoObject.java
package server;
import java.net.*;
import java.io.*;
import java.text.*;
import java.util.*;
public class EchoObject implements EchoInt {
String myURL="localhost";
public EchoObject(){
try {
myURL=InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
myURL="localhost";
}
}
public String echo(String input) throws java.rmi.RemoteException {
Date h = new Date();
String fecha = DateFormat.getTimeInstance(3,Locale.FRANCE).format(h);
String ret = myURL + ":" + fecha + "> " + input;
System.out.println("Procesando: '" + input + "'");
try {
Thread.sleep(3000); ret = ret + " (retrasada 3 segundos)";
} catch (InterruptedException e) {}
System.out.println("Procesamiento de '"+ input +"' terminado.");
return ret;
}
}
2.4.3.- Fichero server/EchoServer.java
package server;
import java.net.*;
import java.io.*;
public class EchoServer {
private static EchoObject eo = new EchoObject();
private static String myURL="localhost";
private static ServerSocket serverSocket = null;
private static Socket clientSocket = null;
private static BufferedReader is = null;
private static PrintWriter os = null;
private static String inputline = new String();
public static void main(String[] args) {
try {
myURL=InetAddress.getLocalHost().getHostName();
DYA
21
Ficheros de apoyo
} catch (UnknownHostException e) {
System.out.println("Unknown Host :" + e.toString());
System.exit(1);
}
try {
serverSocket = new ServerSocket(7);
} catch (IOException e) {
System.out.println(myURL + ": could not listen on port: 7, " + e.toString());
System.exit(1);
}
System.out.println(myURL + ": EchoServer listening on port: 7");
try {
boolean listening = true;
while(listening){
clientSocket = serverSocket.accept();
is = new BufferedReader( new InputStreamReader(
clientSocket.getInputStream()));
os = new PrintWriter(clientSocket.getOutputStream());
while ((inputline = is.readLine()) != null) {
os.println(eo.echo(inputline));
os.flush();
}
os.close();
is.close();
clientSocket.close();
}
serverSocket.close();
} catch (IOException e) {
System.err.println("Error sending/receiving" + e.getMessage());
e.printStackTrace();
}
}
}
2.4.4.- Fichero server/EchoMultiServer.java
package server;
import java.net.*;
import java.io.*;
public class EchoMultiServer {
private static ServerSocket serverSocket = null;
public static void main(String[] args) {
try {
serverSocket = new ServerSocket(7);
} catch (IOException e) {
System.out.println("EchoMultiServer: could not listen on port: 7, " + e.toString());
22
El servicio de “echo” con sockets
Ficheros de apoyo
System.exit(1);
}
System.out.println("EchoMultiServer listening on port: 7");
boolean listening = true;
while (listening) {
//EJERCICIO: aceptar una nueva conexión
//EJERCICIO: y crear un Thread para que la gestione
}
try {
serverSocket.close();
} catch (IOException e) {
System.err.println("Could not close server socket." + e.getMessage());
}
}
}
//---------------------------------------------------------------------------// class EchoMultiServerThread
//---------------------------------------------------------------------------class EchoMultiServerThread extends Thread {
private static EchoObject eo = new EchoObject();
private Socket clientSocket = null;
private String myURL = "localhost";
private BufferedReader is = null;
private PrintWriter os = null;
private String inputline = new String();
EchoMultiServerThread(Socket socket) {
super("EchoMultiServerThread");
clientSocket = socket;
try {
is = new BufferedReader(new InputStreamReader( //EJERCICIO ... ));
os = new PrintWriter( //EJERCICIO ... );
} catch (IOException e) {
System.err.println("Error sending/receiving" + e.getMessage());
e.printStackTrace();
}
try {
myURL=InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
System.out.println("Unknown Host :" + e.toString());
System.exit(1);
}
}
public void run() {
try {
while ((inputline = is.readLine()) != null) {
//EJERCICIO: Invocar el objeto
//EJERCICIO: y devolver la respuesta por el socket
DYA
23
Ficheros de apoyo
}
os.close();
is.close();
clientSocket.close();
} catch (IOException e) {
System.err.println("Error sending/receiving" + e.getMessage());
e.printStackTrace();
}
}
}
2.4.5.- Fichero client/Echo.java
package client;
import java.io.*;
import java.net.*;
public class Echo {
private static EchoObjectStub ss;
public static void main(String[] args) {
if (args.length<2) {
System.out.println("Usage: Echo <host> <port#>");
System.exit(1);
}
ss = //EJERCICIO: crear una instancia del stub
ss.setHostAndPort(args[0],Integer.parseInt(args[1]));
BufferedReader stdIn = new BufferedReader( new InputStreamReader(System.in));
PrintWriter stdOut = new PrintWriter(System.out);
String input,output;
try {
//EJERCICIO: el bucle infinito:
//EJERCICIO: Leer de teclado
//EJERCICIO: Invocar el stub
//EJERCICIO: Imprimir por pantalla
} catch (UnknownHostException e) {
System.err.println("Don't know about host: "+ args[0]);
} catch (IOException e) {
System.err.println("I/O failed for connection to: "+args[0]);
}
}
}
2.4.6.- Fichero client/EchoObjectStub.java
package client;
import java.net.*;
import java.io.*;
24
El servicio de “echo” con sockets
Ficheros de apoyo
class EchoObjectStub implements EchoInt{
private Socket echoSocket = null;
private PrintWriter os = null;
private BufferedReader is = null;
private String host = "localhost";
private int port=7;
private String output = "Error";
private boolean connected = false;
public void setHostAndPort(String host, int port) {
this.host= host; this.port =port;
}
public String echo(String input)throws java.rmi.RemoteException {
connect();
if (echoSocket != null && os != null && is != null) {
try {
os.println(input);
os.flush();
output= is.readLine();
} catch (IOException e) {
System.err.println("I/O failed in reading/writing socket");
throw new java.rmi.RemoteException("I/O failed in reading/writing socket");
}
}
disconnect();
return output;
}
private synchronized void connect() throws java.rmi.RemoteException {
//EJERCICIO: Implemente el método connect
}
private synchronized void disconnect(){
//EJERCICIO: Implemente el método disconnect
}
}
2.4.7.- Fichero client/EchoStub2.java
package client;
import java.net.*;
import java.io.*;
class EchoObjectStub implements EchoInt, Runnable {
private Socket echoSocket = null;
private PrintWriter os = null;
private BufferedReader is = null;
private String host = "localhost";
DYA
25
Ficheros de apoyo
private int port=7;
private String output = "Error";
private boolean connected = false;
private Thread reloj = new Thread(this, "reloj");
private int timeout = 50;
private boolean firstTime = true;
public void setHostAndPort(String host, int port) {
this.host= host; this.port =port;
}
public String echo(String input)throws java.rmi.RemoteException {
connect();
if (echoSocket != null && os != null && is != null) {
try {
os.println(input);
os.flush();
output= is.readLine();
} catch (IOException e) {
System.err.println("I/O failed in reading/writing socket");
throw new java.rmi.RemoteException("I/O failed in reading/writing socket");
}
}
programDisconnection();
return output;
}
private synchronized void connect() throws java.rmi.RemoteException {
//EJERCICIO: lo mismo que en EchoObjectStub
}
private synchronized void disconnect(){
//EJERCICIO: lo mismo que en EchoObjectStub
}
private synchronized void programDisconnection(){
//EJERCICIO: programar un timeout para la cabo de 5 segundos
}
class Timeout {
Timer timer;
EchoObjectStub stub;
int seconds;
public Timeout (int seconds, EchoObjectStub stub) {
this.seconds = seconds;
this.stub = stub;
}
public void start() {
//EJERCICIO
26
El servicio de “echo” con sockets
Ficheros de apoyo
}
public void cancel() {
//EJERCICIO
}
class TimeoutTask extends TimerTask {
//EJERCICIO
}
}
}
DYA
27
Ficheros de apoyo
28
El servicio de “echo” con sockets