91
Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con sockets Luis López Fernández

Embed Size (px)

Citation preview

Page 1: Tema II: Programación con sockets Luis López Fernández

Tema II:Programación con

sockets

Luis López Fernández

Page 2: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Tema II: ContenidosTema II: Contenidos

2.1: Introducción

2.2: Streams en Java

2.3: Sockets TCP

2.4: Sockets UDP

2.5: Sockets con entrada/salida asíncrona en Java

Page 3: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Lección 2.1: IntroducciónLección 2.1: Introducción

2.1: Introducción

2.2: Streams en Java

2.3: Sockets TCP

2.4: Sockets UDP

2.5: Sockets con entrada/salida asíncrona en Java

Page 4: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•La API de sockets es un mecanismo que permite acceder a los servicios IPC proporcionados por los protocolos TCP y UDP

•Los sockets están en el nivel de abstracción del modelo de “paso de mensajes”

•Casi nadie desarrolla directamente sobre sockets hoy en día …

…pero todas las APIs de mayor abstracción se basan en sockets

•Es importante comprender los detalles de la API de sockets

•La API de sockets está disponible en la inmensa mayoría de los sistemas

•En ocasiones forma parte del núcleo del sistema operativo (i.e. Unix)

•En ocasiones es una librería externa (i.e. API Winsock de Windows)

•Traducción de socket

•“enchufe, toma eléctrica”

•La metáfora es que el socket es un punto en el proceso (toma) al que otros procesos pueden “enchufar un cable”

•El “cable” representa una conexión o un flujo de datos

La API de socketsLa API de sockets

Page 5: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La metáfora del socketLa metáfora del socket

Proceso A Proceso B

Page 6: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La metáfora del socketLa metáfora del socket

Proceso A Proceso B

CREATE SOCKET CREATE SOCKET

Page 7: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La metáfora del socketLa metáfora del socket

Proceso A Proceso B

PLUG SOCKET PLUG SOCKET

Page 8: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La metáfora del socketLa metáfora del socket

Proceso A Proceso B

SEND DATA RECEIVE DATA

Page 9: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La metáfora del socketLa metáfora del socket

Proceso A Proceso B

UNPLUG UNPLUG

Page 10: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Lección 2.2: Streams en JavaLección 2.2: Streams en Java

2.1: Introducción

2.2: Streams en Java

2.3: Sockets TCP

2.4: Sockets UDP

2.5: Sockets con entrada/salida asíncrona en Java

Page 11: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Traducción de Stream

“Corriente, chorro, flujo”

•El stream es una secuencia ordenada de unidades de información (bytes, caracteres, etc) que puede fluir en dos direcciones: hacia fuera de un proceso (de salida) o hacia dentro de un proceso (de entrada)

•Los streams están diseñados para acceder a los datos de manera secuencial

•Los streams no suelen estar preparados para realizar operaciones no secuenciales (volver hacia atrás, saltarse datos, realizar acceso aleatorio a datos, etc.)

•Los streams, en principio, van recorriendo una secuencia de datos (de entrada o de salida) y al mismo tiempo, la van “consumiendo”. Es decir, un dato leido/escrito no puede ser “desleido/desecrito” (nota: existen excepciones a esta regla)

•Pueden existir streams especializados en la lectura/escritura de determinados tipos de datos (bytes, enteros, flotantes, caracteres, objetos, etc.)

StreamsStreams

Page 12: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Funcionamiento básico de un streamFuncionamiento básico de un stream

E l g a t o e s p e r ó u n r a t o , p eSecuencia de bytes almacenados en un fichero

Stream

de entradaProceso

Page 13: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Funcionamiento básico de un streamFuncionamiento básico de un stream

E l g a t o e s p e r ó u n r a t o , p eSecuencia de bytes almacenados en un fichero

Stream

de entrada

OPEN STREAM

Page 14: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Funcionamiento básico de un streamFuncionamiento básico de un stream

E l g a t o e s p e r ó u n r a t o , p eSecuencia de bytes almacenados en un fichero

Stream

de entrada

READ BYTE

E

Page 15: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Funcionamiento básico de un streamFuncionamiento básico de un stream

E l g a t o e s p e r ó u n r a t o , p eSecuencia de bytes almacenados en un fichero

Stream

de entrada

READ BYTE

El

Page 16: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Funcionamiento básico de un streamFuncionamiento básico de un stream

E l g a t o e s p e r ó u n r a t o , p eSecuencia de bytes almacenados en un fichero

Stream

de entrada

READ BYTE

El_

Page 17: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Funcionamiento básico de un streamFuncionamiento básico de un stream

E l g a t o e s p e r ó u n r a t o , p eSecuencia de bytes almacenados en un fichero

Stream

de entrada

READ BYTE

El_g

Page 18: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Funcionamiento básico de un streamFuncionamiento básico de un stream

E l g a t o e s p e r ó u n r a t o , p eSecuencia de bytes almacenados en un fichero

Stream

de entrada

READ BYTE

El_ga

Page 19: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Funcionamiento básico de un streamFuncionamiento básico de un stream

E l g a t o e s p e r ó u n r a t o , p eSecuencia de bytes almacenados en un fichero

Stream

de entrada

CLOSE STREAM

El_ga

Page 20: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•En informática, existen dos mecanismos fundamentales para almacenar datos•En formato texto

•Utiliza el carácter como unidad de almacenamiento•El carácter es la representación de un símbolo “legible” por un humano

•En formato binario•Utiliza el byte como unidad de almacenamiento•Este formato no es, en general, legible por humanos

•En Java, existen clases “especialistas” para leer/escribir datos en estos formatos•Lectura de streams de texto: Reader•Escritura en steams de texto: Writer•Lectura de streams binarios: InputStream•Escritura en streams binarios: OutputStream

•Las cuatro clases mencionadas son abstractas (no se pueden instanciar directamente objetos de esa clase)•Cada una de esas cuatro clases actúa como padre de una jerarquía de herencia•En la citada jerarquía se han definido clases concretas “especialistas” en problemas concretos de entrada/salida de datos•Todas las clases de la jerarquía forman parte del paquete java.io de la API estándar

Streams, Readers y Writers en JavaStreams, Readers y Writers en Java

Page 21: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Jerarquía de herencia asociada a InputStreamJerarquía de herencia asociada a InputStream

Page 22: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Jerarquía de herencia asociada a OutputStreamJerarquía de herencia asociada a OutputStream

Page 23: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Jerarquía de herencia asociada a ReaderJerarquía de herencia asociada a Reader

Page 24: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Jerarquía de herencia asociada a WriterJerarquía de herencia asociada a Writer

Page 25: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Leyendo y escribiendo datos binarios con StreamsLeyendo y escribiendo datos binarios con Streamspackage es.urjc;

import java.io.*;

public class BasicDataStreams {

//Los datos que vamos a escribir en un ficheroprivate static byte[] primes = {1, 2, 3, 5, 7, 11, 13};

public static void readDataFile(String filename) throws IOException {FileInputStream fis = new FileInputStream(filename);int prime;while( (prime = fis.read()) != -1)

System.out.println("He leido " + (byte)prime);fis.close();

}

public static void writeDataFile(String filename) throws IOException {File file = new File(filename);FileOutputStream fos = new FileOutputStream(file);for(int i = 0; i < primes.length; i++){

fos.write(primes[i]);fos.flush();

}fos.close();

}...

}

Page 26: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Leyendo y escribiendo datos binarios con StreamsLeyendo y escribiendo datos binarios con Streamspackage es.urjc;

import java.io.*;

public class BasicDataStreams {

...

public static void main(String[] args){String filename = "primes.dat";if(args.length == 1)

filename = args[0];

try{writeDataFile(filename);

} catch(IOException ioe){System.err.println("No se ha podido escribir en el fichero " +

filename);}

try{readDataFile(filename);

} catch(IOException ioe){System.err.println("No se ha podido escribir en el fichero " +

filename);}

}

}

Page 27: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Cada tipo de stream es especialista en un tipo de entrada/salida•Por ejemplo

•FileInputSteam: especialista en leer bytes de un fichero•DataInputStream: especialista en leer datos complejos (enteros, flotantes, booleanos, etc.) de un stream de bytes

•Los streams se pueden “conectar” para combinar sus capacidades

Conectando streams para realizar entrada/salidaConectando streams para realizar entrada/salida

...FileInputStream fis = new FileInputStream("C:\\nombre\\fichero.dat");DataInputStream dis = new DataInputStream(fis);dis.readInt();dis.readFloat();dis.readDouble();dis.close();...

FileInputStream DataInputStream

Especialista en leer bytes de ficheros

Especialista en leer datos complejos de streams de bytes

Fichero en disco Datos leídos

1,2340,2345

2,23E-12

010110011101

doublesbytesHw

Page 28: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Los Data*Streams son especialistas en algunos datos complejos (int, double, etc.)

•Los Object*Streams son especialistas en leer/escribir objetos en/desde streams

•Los Object*Streams se basan en el mecanismo de la serialización

•En Java, la serialización es el proceso por el que un objeto se transforma en una secuencia de bytes que lo representa

•La deserialización es proceso por el que se reconstruye un objeto previamente serializado a partir de una secuencia de bytes

•En Java, para que un objeto sea serializable, debe ser instancia de una clase que implemente la interfaz Serializable, esta interfaz no define ningún método

Serialización y streams de objetosSerialización y streams de objetos

class MiClase implements Serializable {//atributos y métodos

...MiClase miObjeto = new MiClase();

Objetoen memoria

value = 14name = “Hello”p=0.23

Serialización

001010100010101

Objeto serializadoDeserialización

Objeto recuperadoen memoria

value = 14name = “Hello”p=0.23

Page 29: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

ObjectOutputStream•Especialista en transformar objetos en una secuencia de bytes (en serializar objetos)•Debe estar “conectado” a un OutputStream que se especifica en construcción•Ej: ObjectOutputStream oos = new ObjectOutputStream(outputStream)•La serialización se realiza invocando el método void writeObject (Object obj)

ObjectInputStream: •Especialista en recuperar objetos desde una secuencia de bytes (en recuperar objetos previamente serializados)•Debe estar “conectado” a un InputStream que se especifica en construcción•Ej: ObjectInputStream ois = new ObjectInputStream(inputStream)•La deserialización se logra invocando el método Object readObject()

ObjectIntputStream y ObjectOutputStreamObjectIntputStream y ObjectOutputStream

import java.util.ArrayList;public class Persona implements Serializable{

public String nombre;public int age;public ArrayList<Persona> friends;

}

Ejemplo de clase serializable

Page 30: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Ejemplo de uso de Object/Input/Output/StreamsEjemplo de uso de Object/Input/Output/Streamsimport java.io.*;public class BasicObjectStreams {

private static void writePersona(String filename){Persona p = new Persona();p.name = "Luis";p.age = 29;p.friends = null;try{

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename));

oos.writeObject(p);oos.close();

} catch(IOException ioe ){System.exit(-1);

}}private static Persona readPersona(String filename){

try{ObjectInputStream ois = new ObjectInputStream(new

FileInputStream(filename));return (Persona)ois.readObject(); //Habría que cerrar con

close()} catch(IOException ioe) {

System.err.println(ioe.getMessage());} catch (ClassNotFoundException cnfe) {

cnfe.printStackTrace();} return null;

}...

}

Page 31: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Ejemplo de uso de Object/Input/Output/StreamsEjemplo de uso de Object/Input/Output/Streamsimport java.io.*;

public class BasicObjectStreams {

...

public static void main(String[] args){String filename = "persona.dat";if(args.length == 1)

filename = args[0];

writePersona(filename);Persona p = readPersona(filename);System.out.println("p.name=" + p.name + " -- p.age=" + p.age);

}

}

# javac BasicObjectStreams.java# java BasicObjectStreamp.name=Luis -- p.age=29# ls ... persona.dat

Page 32: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Los objetos que sean instancias de clases Serializable se pueden serializar

•Para que un atributo no se serialice, debe ser marcado como transient

•Multitud de atributos no pueden ser serializados y deben ser marcados como transient (por ejemplo, un FileInputStream)

•Se serializan todos los atributos que no sean transient ni static

•Si un atributo es una referencia a un objeto, el objeto se serializa

•Por tanto, la serialización de un objeto con referencias crea un árbol de serialización

•Al deserializar el objeto, se recupera el árbol completo de objetos

•Si hay varias referencias a un objeto, los datos del mismo se serializan solo una vez

¿Qué se serializa?¿Qué se serializa?

Objeto A Objeto B

Objeto C Objeto E

Objeto D

transient

Objeto A Objeto B

Objeto E

Objeto D

SerializaciónObjeto A 001010110

DeserializaciónObjeto A

Page 33: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•La entrada/salida de caracteres de texto es compleja debido a la gran cantidad de formatos de codificación que existen (ASCII, UTF-8, Unicode, etc.)

•Hacer entrada/salida de texto directamente con streams es complejo

•Los Readers y los Writers simplifican enormemente la tarea

•Como en el caso de los streams, existen clases especialistas en tareas concretas

•Para entenderlas hay que comprender cómo Java procesa y representa caracteres

•En Java, los caracteres se representan internamente en formato Unicode de 16 bits

•Cuando se lee un carácter, este se convierte de su formato nativo a Unicode

•Cuando se escribe un carácter, este se convierte de Unicode a su formato objetivo

•En Java, al formato nativo/objetivo de una cadena se le denomina su charset

•Desde Java 1.5 existe la clase Charset (en el paquete java.nio.charset) que permite operar sobre la representación de caracteres

•En Java, se soportan múltiples charsets de entrata/salida, entre los que se incluyen: US-ASCII, ISO-8859-1, UTF-8, UTF-16BE, UTF-816LE, etc.

•Cada instancia de la máquina virtual tiene un charset por defecto que depende del formato utilizado por el sistema operativo subyacente•El charset por defecto se puede averiguar mediante el método estático defaultCharset() de la clase Charset

Readers, writers y charsetsReaders, writers y charsets

Page 34: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Vamos a ver algunos Readers y Writers básicos y en qué se especializan

InputStreamReader•Hace de “puente” entre streams de bytes y de caracteres•Debe estar conectado con un InputStream que se especifica en construcción•Tiene constructores que permiten especificar un Charset de preferencia•Los constructores que no toman un Charset, usan el charset por defecto•Puede leer caracteres de uno en uno utilizando el método int read()

OutputStreamWriter•Equivalente al InputStreamReader pero para salida de caractéres•Escribe caractéres usando el método write(int c)

Algunos Readers y WritersAlgunos Readers y Writers

Hijos deInputStream

InputStreamReader charsbytes

Hijos deOutputStream

OutputStreamWriter charsbytes

Page 35: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

BufferedReader•Permite leer texto de forma eficiente a través de un buffer intermedio•Debe estar conectado con un Reader que se especifica en construcción•Se utiliza el Charset que posea ese Reader•Puede leer líneas completas utilizando el método String readLine()

PrintWriter•Imprime texto formateado•Puede estar conectado a un Writer•Puede estar conectado a un OutputStream•Puede volcar directamente sobre un fichero•Existen constructores para cada uno de los casos•Se pueden imprimir líneas completas usando el método println(String line)

Algunos Readers y WritersAlgunos Readers y Writers

InputStreamReader isr = new InputStreamReader(new FileOutputStream(“C:\\file.txt”);BufferedReader br = new BufferedReader(isr);String line = br.readLine();...

PrintWriter pw = new PrintWriter(“C:\\file.txt”);pw.println(“Hola mundo”);...

Page 36: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Lección 2.3: Sockets TCPLección 2.3: Sockets TCP

2.1: Introducción

2.2: Streams en Java

2.3: Sockets TCP

2.4: Sockets UDP

2.5: Sockets con entrada/salida asíncrona en Java

Page 37: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•La API de sockets en modo stream proporciona acceso al servicio de transporte de datos proporcionado por el protocolo TCP

•El servicio TCP es orientado a conexión proporciona un transporte fiable

•La conexión se establece

•Un flujo de bytes sale del emisor

•El mismo flujo de bytes es recibido en el receptor

•El servicio proporcionado por el socket es full-duplex (el mismo socket puede ser utilizado para enviar y para recibir datos)

•Para que la conexión se pueda establecer,

•Uno de los sockets debe estar “a la espera de recibir conexiones”

•El otro socket debe “iniciar una conexión”

•Para que una conexión se libere (cierre)

•Basta con que uno de los sockets desee cerrarla

•Un socket recién creado no está conectado

•Vamos a estudiar en detalle el funcionamiento de los sockets TCP utilizando la funcionalidad proporcionada por la API estándar de Java

La API de sockets en modo stream (TCP sockets)La API de sockets en modo stream (TCP sockets)

Page 38: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•En Java hay dos tipos de stream sockets que tienen asociadas sendas clases

ServerSocket

•Proporciona un socket dispuesto a aceptar conexiones TCP de forma pasiva

•Para simplificar, lo denominaremos socket de conexión

•Al proceso que posee un ServerSocket se le denomina también servidor

Socket

•Proporciona un socket por el que se pueden intercambiar datos con otro socket

•Para simplificar, lo denominaremos socket de datos

•Al proceso que inicia una conexión desde un Socket se le denomina cliente

El proceso a seguir para lograr el intercambio de datos es:

•El servidor crea el ServerSocket y lo asocia a un puerto

•El cliente crea un Socket

•El cliente ordena a su Socket que se conecte con el ServerSocket remoto

•Al recibir la petición, el servidor crea un nuevo Socket y lo connecta con el remoto

•Se intercambian los datos

•La conexión se cierra cuando alguno de los dos sockets lo indica

Tipos de stream socket en JavaTipos de stream socket en Java

Page 39: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•La clase ServerSocket forma parte del paquete java.net de la API estándar

Constructores:

ServerSocket(): Crea un nuevo socket de conexión no atado a ningún puerto

ServerSocket(int port): Crea un nuevo socket de conexión y lo ata al puerto especificado. En número de puerto debe estar comprendido entre 0 y 65535 inclusive. Si port es 0, se toma un puerto libre “al azar”

ServerSocket(int port, int backlog): Permite además especificar el tamaño de la cola de solicitudes de conexión no atendidas. Si el número de solicitudes no atendidas supera el tamaño especificado se rechazán solicitudes de conexión

ServerSocket(int port, int backlog, InetAddress bindAddr): Permite además especificar la dirección IP en la que se atenderán las solicitudes de conexión (para ordenadores que dispongan de varias). Si bindAddr es null, el ServerSocket atenderá solicitudes de conexión en todas las interfaces de la máquina

La clase ServerSocketLa clase ServerSocket

Page 40: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Algunos Métodos:

Socket accept(): Escucha una solicitud de conexión y la acepta cuando se recibe. En ese momento, se crea nuevo Socket que es el que realmente se conecta con el cliente. Tras esto, el ServerSocket sigue disponible para realizar nuevos accept()

void bind(SocketAddress endpoint): Ata al ServerSocket a la dirección especificada (dirección IP y puerto). El ServerSocket no debe estar atado previamente

int getLocalPort(): Devuelve el puerto al que está atado el ServerSocket

void close(): Cierra el ServerSocket

La clase ServerSocket Cont.La clase ServerSocket Cont.

...ServerSocket serverSocket = new ServerSocket(0);System.out.println(“El puerto es “ + serverSocket.getLocalPort());Socket connection = serverSocket.accept();//hacemos cosas con connectionSocket otherConnection = serverSocket.accept();//hacemos cosas con otherConnectionserverSocket.close();...

Page 41: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La clase SocketLa clase Socket•La clase Socket forma parte del paquete java.net de la API estándar

Algunos Constructores:

Socket(): Crea un nuevo socket sin conectar

Socket(InetAddress address, int port): Crea un nuevo socket y lo conecta automáticamente con un socket remoto en la dirección IP y puertos especificados

Socket(InetAddress address, int port, InetAddress localAddr, int localPort): Permite además especificar la dirección IP local y el puerto local a los que se asociará el socket

Socket(String host, int port): El host remoto se puede especificar a través de su nombre de dominio. Se utilizarán los mecanismos de resolución del sistema operativo subyacente

Socket(Proxy proxy): Se crea un socket no conectado especificando el tipo de proxy que se desea usar

Page 42: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La clase Socket Cont.La clase Socket Cont.Algunos Métodos:

void connect(SocketAddress endpoing): Conecta este socket al servidor especificado (dirección IP y puerto)

InetAddress getInetAddress(): Devuelve la dirección (IP y puerto) a la que este socket se encuentra conectado

InputStream getInputStream(): Devuelve un InputStream que permite leer bytes del socket (recibir datos enviados desde el socket remoto) utilizando los mecanismos habituales de los streams. El socket debe estar conectado

OutputStream getOutputStream(): Devuelve un OutputStream que permite escribir bytes sobre el socket (enviar bytes al socket remoto) utilizando los mecanismos habituales de los streams. El socket debe estar conectado

void setKeepAlive(): Al activarlo, se chequea que la conexión entre dos sockets sigue activa enviando “paquetes basura” periódicamente (RFC 1122)

void setSoLinger(boolean on, int linger): Al activarlo, si hay datos pendientes de enviar cuando se invoca close(), el llamante se bloquea (durante un máximo de linger segundos) hasta que los datos pendientes han sido enviados

void setTcpNoDelay(): Permite activar/desactivar el algoritmo de Nagle para el envío en TCP (RFC 896)

void setTrafficClass(int tc): Permite seleccionar el byte TOS del paquete IP (no todas las redes lo tienen en cuenta) Más información en RFC 1349

Page 43: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de cliente y servidorSincronización de cliente y servidor

Servidor Cliente

server = new ServerSocket()

Conexión

server.accept()

client = new Socket(host, port)

read() sobre el Socket

Nuevo Socket conectado

write() sobre el socketMensaje

read() sobre el socket

write() sobre el SocketMensaje

socket.close() socket.close()

serverSocket.close()

Page 44: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de cliente y servidorSincronización de cliente y servidor

Servidor Cliente

server = new ServerSocket()

Conexión

server.accept()

client = new Socket(host, port)

read() sobre el Socket

Nuevo Socket conectado

write() sobre el socketMensaje

read() sobre el socket

write() sobre el SocketMensaje

socket.close() socket.close()

serverSocket.close()

ServerSocket server = new ServerSocket();

La creación del ServerSocket no requiere ninguna

sincronización entre servidor y cliente, la operación es

no -bloqueante

Page 45: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de cliente y servidorSincronización de cliente y servidor

Servidor Cliente

server = new ServerSocket()

Conexión

server.accept()

client = new Socket(host, port)

read() sobre el Socket

Nuevo Socket conectado

write() sobre el socketMensaje

read() sobre el socket

write() sobre el SocketMensaje

socket.close() socket.close()

serverSocket.close()

Socket socket = serverSocket.accept();

La llamada a accept() es síncrona y bloquea al hilo llamante

hasta que se recibe una solicitud de conexión destinada al

proceso servidor (dirección IP y puerto). Cuando se recibe la

citada solicitud, se crea un nuevo socket que sirve como

extremo de la conexión junto con el socket remoto del cliente

Page 46: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de cliente y servidorSincronización de cliente y servidor

Servidor Cliente

server = new ServerSocket()

Conexión

server.accept()

client = new Socket(host, port)

read() sobre el Socket

Nuevo Socket conectado

write() sobre el socketMensaje

read() sobre el socket

write() sobre el SocketMensaje

socket.close() socket.close()

serverSocket.close()

Socket client = new Socket(host, port);

La creación del socket cliente con este constructor requiere el

establecimiento de la conexión, motivo por el cual esta llamada

es bloqueante, haciendo que el hilo cliente se bloquee hasta

que el establecimiento de la conexión haya finalizado.

Page 47: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de cliente y servidorSincronización de cliente y servidor

Servidor Cliente

server = new ServerSocket()

Conexión

server.accept()

client = new Socket(host, port)

read() sobre el Socket

Nuevo Socket conectado

write() sobre el socketMensaje

read() sobre el socket

write() sobre el SocketMensaje

socket.close() socket.close()

serverSocket.close()

socket.getInputStream()read()

La llamada a read() sobre el InputStream del socket es

bloqueante, el hilo se detiene mientras no se reciban los datos

enviados por el proceso remoto. Si se especifica un tamaño

de datos esperado, la llamada se bloquea hasta que se reciben

suficiente información desde el socket remoto (i.e. readLine())

Page 48: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de cliente y servidorSincronización de cliente y servidor

Servidor Cliente

server = new ServerSocket()

Conexión

server.accept()

client = new Socket(host, port)

read() sobre el Socket

Nuevo Socket conectado

write() sobre el socketMensaje

read() sobre el socket

write() sobre el SocketMensaje

socket.close() socket.close()

serverSocket.close()

socket.getOutputStream()write()

La llamada a write() sobre el OutputStream del socket es

bloqueante, el hilo se detiene mientras TCP haya recibido el

asentimiento de los datos enviados. Puede que una llamada a

write() se desbloquee sin que se haya producido un read() en

el proceso remoto, pero esto sólo sucede cuando los datos

enviados “caben” en la ventana TCP del receptor

Page 49: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de cliente y servidorSincronización de cliente y servidor

Servidor Cliente

server = new ServerSocket()

Conexión

server.accept()

client = new Socket(host, port)

read() sobre el Socket

Nuevo Socket conectado

write() sobre el socketMensaje

read() sobre el socket

write() sobre el SocketMensaje

socket.close() socket.close()

serverSocket.close()

socket.close()

Las llamadas a cloase() son siempre no-bloqueantes,

excepto cuando SO_LINGER se ha activado sobre el socket,

En cuyo caso la llamada es bloqueante hasta que se terminan

de enviar los datos pendientes

Page 50: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Java stream sockets: ejemplo de aplicaciónJava stream sockets: ejemplo de aplicaciónimport java.io.*;import java.net.*;

public class BasicServer {public static void main(String[] args) throws IOException {

int port = 2345;if(args.length == 1)

port = Integer.parseInt(args[0]);BasicServer server = new BasicServer(port);server.launch();

}

private ServerSocket serverSocket;private BasicServer(int port) throws IOException {

serverSocket = new ServerSocket(port);}private void launch() throws IOException {

while(true){Socket connection = serverSocket.accept();BufferedReader br = new BufferedReader(

new InputStreamReader(connection.getInputStream()));String inputMessage = br.readLine();PrintWriter pw = new

PrintWriter(connection.getOutputStream());pw.println(inputMessage.toUpperCase());pw.close();//si no se cierra primero, hay que hacer flushbr.close();connection.close();

}}

}

Page 51: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Java stream sockets: ejemplo de aplicaciónJava stream sockets: ejemplo de aplicaciónimport java.io.*;import java.net.*;public class BasicClient {

public static void main(String[] args) throws Exception {String remoteHost = "localhost";int remotePort = 2345;BasicClient client = new BasicClient(remoteHost, remotePort);client.launch();

}private String host;private int port;private static final String message = "mensaje básico en iteración ";private BasicClient(String host, int port) throws IOException {

this.host = host; this.port = port;}private void launch() throws Exception {

for(int iteration = 0; iteration < 10; iteration++){Socket connection = new Socket(host, port);PrintWriter pw = new

PrintWriter(connection.getOutputStream());pw.println(message + iteration);pw.flush();System.out.println(">>>>>" + message + iteration);BufferedReader br = new BufferedReader(new

InputStreamReader(connection.getInputStream()));System.out.println("<<<<<" + br.readLine());pw.close();br.close();connection.close();Thread.sleep(1000);

}}

}

Page 52: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Lección 2.4: Sockets UDPLección 2.4: Sockets UDP

2.1: Introducción

2.2: Streams en Java

2.3: Sockets TCP

2.4: Sockets UDP

2.5: Sockets con entrada/salida asíncrona en Java

Page 53: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La API de sockets en modo datagama (UDP sockets)La API de sockets en modo datagama (UDP sockets)•La API de sockets en modo datagrama proporciona acceso al servicio de transporte de datos que implementa el protocolo UDP

•El servicio UDP no es orientado a conexión ni proporciona entrega fiable de datos

•El emisor envía un datagrama a un proceso remoto (dirección IP y puerto)

•Si el datagrama no se pierde, eventualmente será recibido en el receptor

•La API estándar de Java proporciona sockets orientados a datagrama que permiten utilizar directamente el servicio UPD que acabamos de describir

•Un socket en modo datagrama proporciona un servicio full-duplex

•Java también proporciona una abstracción adicional que provee de un servicio de transporte no fiable orientado a conexión basado en el protocolo UPD. Esta abstracción forma parte del runtime de Java y no supone modificación alguna sobre el funcionamiento del protocolo UDP

•Todos los paquetes que pertenecen a una de estas conexiones lógicas comparten 4 parámetros (el runtime de Java asigna paquetes a conexiones usándolos): Puerto de origen, Dirección IP de origen, Puerto de destino, Dirección IP de destino

•Vamos a estudiar en detalle el funcionamiento de los sockets UDP utilizando la funcionalidad proporcionada por la API estándar de Java

Page 54: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La clase DatagramSocketLa clase DatagramSocket•La clase DatagramSocket forma parte del paquete java.net de la API estándar

Constructores:

DatagramSocket(): Crea un socket de datagrama y lo ata a un puerto libre en la máquina local. Este constructor suele crearse para desarrollar el extremo cliente de una aplicación.

DatagramSocket(int port): Crea un nuevo socket de datagrama y lo ata al puerto especificado en todas las interfaces de la máquina local. Este constructor suele utilizarse para desarrollar el extremos servidor de una aplicación

DatagramSocket(int port, InetAddress laddr): Crea un nuevo socket de datagrama y lo ata al puerto especificado en la dirección IP dada sobre la máquina local (para máquinas con varias interfaces). Este constructor suele utilizarse para crear el extremo servidor de una aplicación

DatagramSocket(SocketAddress bindaddr): Crea un nuevo socket de datagrama y lo ata a la dirección especificada (puerto y dirección IP). Este constructor suele utilizarse para crear el extremos servidor de una aplicación

Page 55: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La clase DatagramSocketLa clase DatagramSocket

Algunos métodos:

void bind(SocketAddress addr): Ata el socket de datagrama a la dirección especificada (puerto y dirección IP)

int getLocalPort(): Devuelve el puerto al que el socket está atado

void receive(DatagramPacket p): Recibe un datagrama en el socket. Esta llamada es bloqueante. Cuando este método retorna, los datos se devuelven en el buffer del DatagramPacket. El DatagramPacket también contiene la dirección IP y el puerto del emisor.

void send(DatagramPacket p): Envía un datagrama desde el socket. El DatagramPacket contiene los datos, su longitud, la dirección IP del receptor y su número de puerto

void setTraffciClass(int tc): Permite establecer es campo TOS (type-of-service) de los paquetes IP que encapsulan los datagramas (RFC 1349)

void close(): Cierra el socket, no se podrán volver a enviar ni a recibir datragamas desde el socket

Page 56: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La clase DatagramPacketLa clase DatagramPacket•La clase DatagramPacket forma parte del paquete java.net de la API estándar

Constructores:

DatagramPacket(byte[] buf, int length): Construye un DatagamPacket para recibir datagramas con hasta length bytes de datos

DatagramPacket(byte[] buf, int length, InetAddress addr, int port): Crea un nuevo DatagramPacket para el envío de los datos especificados a la dirección y puerto remotos dados

DatagramPacket(byte[] buf, int offset, int length): Construye un DatagramPacket para la recepción de datos, que se depositarán con el offset especificado en el buffer dado

DatagramPacket(byte[] buf, int offset, int length, InetAddress addr, int port): Crea un DatagramPacket para enviar un datagrama UPD que contenga length bytes de datos a partir de buf[offset]

Page 57: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

La clase DatagramPacketLa clase DatagramPacketAlgunos métodos:

InetAddress getAddress(): Cuando el DatagramPacket es para envío, indica la dirección IP del receptor, cuando el DatagramPacket es de recepción, indica la dirección IP del emisor (siempre la dirección IP del lado remoto)

byte[] getData(): Devuelve el buffer de datos asociado a este DatagramPacket. Si el DatagramPacket es de recepción, el buffer contendrá los datos recibidos

int getLength(): Devuelve en número de bytes útiles en el datagrama. Este método permite determinar los bytes de datos útiles que se pueden extraer del buffer devuelto por getData()

int getPort(): Cuando el DatagramPacket es para envío, indica el puerto receptor, cuando el DatagramPacket es de recepción, indica el puerto del emisor (siempre el puerto del lado remoto)

void setData(byte[] buf): Permite asociar un nuevo buffer de datos con el DatagramPacket. Si el DatagramPacket es de recepción, los datos del datagrama recibido no podrán exceder buf.length, en caso contrario, los datos se truncarán

void setLength(int length): Indica el número de bytes útiles en el buffer asociado al DatagramPacket. Este método es usado cuando el DatagramPacket es de emisión para indicar cuantos bytes del buffer especificado se enviarán en el datagrama correspondiente

void setPort(int port): Indica el puerto asociado a un DatagramPacket. Este método se usa para indicar el puerto del receptor el datagramas de envío

Page 58: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de los DatagramSocketSincronización de los DatagramSocket

Servidor Cliente

server = new DatagramSocket(2345)

server.receive(datagramPacket)

client = new DatagramSocket()

client.send(datagramPacket)

Mensaje

client.receive(datagramPacket)

server.send(datagramPacket)

Mensaje

server.close()client.close()

Page 59: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de los DatagramSocketSincronización de los DatagramSocket

Servidor Cliente

server = new DatagramSocket(2345)

server.receive(datagramPacket)

client = new DatagramSocket()

client.send(datagramPacket)

Mensaje

client.receive(datagramPacket)

server.send(datagramPacket)

Mensaje

server.close()client.close()

DatagramSocket server = new DatagramSocket(2345);

La creación de un DatagramSocket atado a un puerto es

no -bloqueante

Page 60: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de los DatagramSocketSincronización de los DatagramSocket

Servidor Cliente

server = new DatagramSocket(2345)

server.receive(datagramPacket)

client = new DatagramSocket()

client.send(datagramPacket)

Mensaje

client.receive(datagramPacket)

server.send(datagramPacket)

Mensaje

server.close()client.close()

server.receive(datagramPacket);

La llamada a receive en un DatagramSocket es una operación

síncrona y, por tanto, bloqueante

Page 61: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de los DatagramSocketSincronización de los DatagramSocket

Servidor Cliente

server = new DatagramSocket(2345)

server.receive(datagramPacket)

client = new DatagramSocket()

client.send(datagramPacket)

Mensaje

client.receive(datagramPacket)

server.send(datagramPacket)

Mensaje

server.close()client.close()

client.send(datagramPacket);

La llamada a send en un DatagramSocket es una operación

no-bloqueante, el paquete se envía pero so se espera ningún

asentimiento

Page 62: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de los DatagramSocketSincronización de los DatagramSocket

Servidor Cliente

server = new DatagramSocket(2345)

server.receive(datagramPacket)

client = new DatagramSocket()

client.send(datagramPacket)

Mensaje

client.receive(datagramPacket)

server.send(datagramPacket)

Mensaje

server.close()client.close()

Cuando el mensaje se recibe, se desbloquea la llamada al

método receive de la clase DatagramSocket

Page 63: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Sincronización de los DatagramSocketSincronización de los DatagramSocket

Servidor Cliente

server = new DatagramSocket(2345)

server.receive(datagramPacket)

client = new DatagramSocket()

client.send(datagramPacket)

Mensaje

client.receive(datagramPacket)

server.send(datagramPacket)

Mensaje

server.close()client.close()

datagramSocket.close();

Las llamadas al método close son siempre

no -bloqueante

Page 64: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Java Datagram Sockets: ejemplo de aplicaciónJava Datagram Sockets: ejemplo de aplicación

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

public class BasicUDPServer {

public static void main(String[] args) throws IOException {

DatagramSocket udpSocket = new DatagramSocket(2345);DatagramPacket datagramReceived = new DatagramPacket(new byte[512],

512);while(true){

udpSocket.receive(datagramReceived);String messageReceived =

new String(datagramReceived.getData(), 0 ,datagramReceived.getLength());

byte[] answer = messageReceived.toUpperCase().getBytes();

DatagramPacket datagramSent = new DatagramPacket(answer, answer.length,datagramReceived.getAddress(),datagramReceived.getPort());

udpSocket.send(datagramSent);}

}}

Page 65: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Java Datagram Sockets: ejemplo de aplicaciónJava Datagram Sockets: ejemplo de aplicaciónimport java.net.*;

public class BasicUDPClient {

private final static String message = "el cliente envia el mensaje ";

public static void main(String[] args) throws Exception {DatagramSocket udpSocket = new DatagramSocket();

for(int i = 0; i< 10; i ++){String messageSent = message + i; byte[] datos = messageSent.getBytes();

DatagramPacket datagram = new DatagramPacket(datos,datos.length,

InetAddress.getByName("localhost"),2345);

udpSocket.send(datagram);System.out.println(">>>>>" + messageSent);

datagram = new DatagramPacket(new byte[512], 512);udpSocket.receive(datagram);String messageReceived = new String(datagram.getData(), 0,

datagram.getLength());System.out.println("<<<<<" + messageReceived);

Thread.sleep(1000);}

}}

Page 66: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Java DatagramSockets orientados a conexiónJava DatagramSockets orientados a conexión•La clase DatagramPacket proporciona una abstracción de envío de datagramas no fiable con orientación a conexión

Métodos implicados:

void connect(InetAddress address, int port): Conecta el DatagramSocket a la dirección remota especificada. Una vez conectado, el socket sólo podrá enviar o recibir datagramas de la dirección remota especificada. Si se envía o recibe un datagrama con dirección IP o puerto remoto diferentes, se elevará una excepción IllegalArgumentException.

En algunos sistemas, si el socket remoto no existe en la dirección especificada, o si éste no se puede alcanzar, el DatagramSocket local recibirá un paquete ICMP (destination unreachable), lo que se notificará con una excepción PortUnreachableException en cualquier llamada a send() or receive()

void disconnect(): Desconecta el DatagramSocket

boolean isConnected(): Devuelve true si el socket está conectado, false en caso contrario

Page 67: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Lección 2.5: Sockets con entrada/salida asíncrona Lección 2.5: Sockets con entrada/salida asíncrona en Java en Java

2.1: Introducción

2.2: Streams en Java

2.3: Sockets TCP

2.4: Sockets UDP

2.5: Sockets con entrada/salida asíncrona en Java

Page 68: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Operaciones bloqueantes en los sockets de JavaOperaciones bloqueantes en los sockets de Java

Socket TCPserver = new ServerSocket()

server.accept()

read() sobre el Socket

Nuevo Socket conectado

write() sobre el Socket

socket.close()

serverSocket.close()

Socket UDP

client = new DatagramSocket()

client.send(datagramPacket)

client.receive(datagramPacket)

client.close()

Page 69: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Las llamadas bloqueantes facilitan la sincronización entre los procesos que comunican, pero complican algunos aspectos del desarrollo

•En ciertas condiciones producen el bloqueo indefinido de un procesodatagramSocket.receive(datagramPacket) + pérdida del mensaje emitidodatagramSocket.receive(datagramPacket) + crash del proceso emisorserverSocket.accept() + crash del proceso remoto

•Pueden producir interbloqueo entre los procesos que comunican

•Para procesos que deben mantener varias comunicaciones simultáneas, requieren el uso intensivo de hilos de ejecución (threads)

•Los hilos limitan la escalabilidad del sistema•La gestión de muchos hilos hace menos eficiente el programa•Las llamadas bloqueantes dificultan la destrucción y reutilización de hilos

Problemas de las llamadas bloqueantesProblemas de las llamadas bloqueantes

Proceso A Proceso B

Proceso C

Proceso origen en esperade proceso destino

Page 70: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Para evitar los bloqueos indefinidos y permitir que los procesos puedan “reaccionar” ante situaciones “especiales”, Java proporciona la posibilidad de establecer temporizadores asociados a las llamadas bloqueantes

•Estos temporizadores funcionan del modo siguiente:

•El usuario elige un “tiempo máximo” (a través de una invocación)

•Cuando se produce la invocación al método bloqueante, se inicia un temporizador

•Si el método bloqueante retorna (es decir, se desbloquea) antes de que se alcance el “tiempo máximo”, el programa continúa con normalidad

•Si se alcanza el “tiempo máximo” sin que la llamada retorne, se eleva una excepción desde dentro del método bloqueante

•En todos los casos, el agotamiento del temporizador y el lanzamiento de la excepción correspondiente no afecta al estado del socket correspondiente•El desarrollador tiene la opción de manejar la excepción y dejar al programa continuar, o bien de tomar acciones adicionales que pueden incluir el cierre y destrucción del socket.

Uso de bloqueos con temporizadorUso de bloqueos con temporizador

Page 71: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Estableciendo el tiempo máximo de espera en la clase ServerSocket•serverSocket.setSoTimeout(int tiemout)•tiemout especifica el tiempo máximo de espera en milisegundos. Un valor de 0 indica un tiempo de espera infinito•Afecta a las llamadas a serverSocket.accept(). Si el timeout se agota, se elevará una excepción java.net.SocketTimeoutException

•Estableciendo el tiempo máximo de espera en la clase Socket•serverSocket.setSoTimeout(int tiemout)•tiemout especifica el tiempo máximo de espera en milisegundos. Un valor de 0 indica un tiempo de espera infinito•Afecta a las llamadas a read() en el InputStream asociado al socket. Si el timeout se agota, se elevará una java.net.SocketTimeoutException

•Estableciendo el tiempo máximo de espera en la clase DatagramSocket•serverSocket.setSoTimeout(int tiemout)•tiemout especifica el tiempo máximo de espera en milisegundos. Un valor de 0 indica un tiempo de espera infinito•Afecta a las llamadas a datagramSocket.receive(p). Si el timeout se agota, se elevará una java.net.SocketTimeoutException

Estableciendo el tiempo de espera máximoEstableciendo el tiempo de espera máximo

Page 72: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Queremos que un proceso (o hilo) no se bloquee de manera indefinida•Usamos temporizadores para desbloquearlo de manera periódica•Cada cierto tiempo chequeamos si el necesario “continuar con otra cosa”

Ejemplo de desarrollo con temporizadoresEjemplo de desarrollo con temporizadores

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

public class TimeoutServerSocket {public static void main(String[] args) throws IOException {

ServerSocket server = new ServerSocket(2345);server.setSoTimeout(250); //El valor del timeout depende de la

aplicaciónboolean continuar = true;while(continuar){

try{Socket connection = server.accept();

//Hacer cosas con la conexión//Podemos hacer continuar = false

} catch(SocketTimeoutException ste){//Chequear si debemos continuar//Podemos elevar una excepción//Podemos cerrar el socket y hacer continuar = false

}

}}

}

Page 73: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•El modelo basado en temporizadores es una solución a medias

•No evita el uso de múltiples hilos de ejecución

•Si se quieren tiempos de respuesta bajos (timeouts pequeños) se degradan las prestaciones

•Si se quieren prestaciones elevadas (timeouts grandes) se degradan los tiempos de respuesta

•Muchos lenguajes de programación/entornos de desarrollo ofrecen operaciones de entrada salida asíncronas

•Las operaciones de entrada/salida asíncronas ofrecen servicios de lectura y escritura no bloqueantes

•La estrategia habitual para desarrollar programas con entrada/salida asíncrona se basa en el uso de modelos de programación orientados a eventos

•El programa es un bucle a la espera de eventos

•Cuando se producen eventos, el bucle realiza una iteración para procesarlos

•Puede usarse un único hilo de ejecución o varios para procesar los eventos

•Java define un conjunto de facilidades de entrada/salida asíncronas en el paquete java.nio (Java New Input Output) que existe desde el JDK 1.4

•Vamos a ver los fundamentos de estas facilidades, pero no profundizaremos

Desarrollo con entrada/salida asíncronaDesarrollo con entrada/salida asíncrona

Page 74: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•El paquete java.nio proporciona facilidades para el desarrollo de aplicaciones que requieran entrada/salida de alta velocidad y/o altas prestaciones

•Delega algunas de las partes más tediosas (gestión de bufferes) al sistema nativo

•El modelo de streams (definido en java.io) se basa en corrientes de bytes, el modelo de java.nio, se basa en bloques que se almacenan en bufferes

•En java.nio existen dos conceptos (objetos) esenciales

•Channel: Es un objeto análogo a un stream, pero bidireccional (se puede leer y escribir sobre el mismo objeto) y que funciona sobre bloques y no sobre bytes

•Buffer: Es el objeto que se utiliza como contenedor de bloques para los channels

•En java.nio todos los datos que se leen o escriben se gestionan a través de un objeto Buffer

•Además de contener los datos, el objeto Buffer proporciona facilidades al programador que facilitan el desarrollo de aplicaciones

•Existen diferentes tipos de buffers “especialistas” en diferentes tipos de datos:

Java New Input Output Java New Input Output

Page 75: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•La clase java.nio.Buffer es abstracta•Para cada tipo de datos primitivos existe una subclase especialista: ByteBuffer, CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer y DoubleBuffer•El Buffer contiene un bloque (array) de datos de entrada/salida que puede ser manipulado a través de métodos específicos•El funcionamiento de estos métodos se basa en tres variables de estado

•La variable position•Especifica la siguiente posición del array que será escrita/leída•En cada operación de lectura/escritura, position se incrementa en una unidad

•La variable limit•Especifica cuantas posiciones del array pueden ser leidas/escritas en total•Es decir, indica cuanto espacio tiene el array para lectura/escritura•Siempre se cumple que position <= limit

•La variable capacity•Especifica la máxima cantidad de elementos que caben en el array•Es decir, indica el tamaño en memoria del array subyacente•Siempre se cumple que limit <= capacity

Bufferes en java.nioBufferes en java.nio

Page 76: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Bufferes en java.nio: Ejemplo de usoBufferes en java.nio: Ejemplo de usoCharBuffer charBuffer = CharBuffer.allocate(5);

charBuffer.put('a');

charBuffer.put('b');

charBuffer.put('c');

charBuffer.flip();

char c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();position

limit

capacity

Page 77: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Bufferes en java.nio: Ejemplo de usoBufferes en java.nio: Ejemplo de usoCharBuffer charBuffer = CharBuffer.allocate(5);

charBuffer.put('a');

charBuffer.put('b');

charBuffer.put('c');

charBuffer.flip();

char c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

a

position

limit

capacity

Page 78: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Bufferes en java.nio: Ejemplo de usoBufferes en java.nio: Ejemplo de usoCharBuffer charBuffer = CharBuffer.allocate(5);

charBuffer.put('a');

charBuffer.put('b');

charBuffer.put('c');

charBuffer.flip();

char c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

a b

position

limit

capacity

Page 79: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Bufferes en java.nio: Ejemplo de usoBufferes en java.nio: Ejemplo de usoCharBuffer charBuffer = CharBuffer.allocate(5);

charBuffer.put('a');

charBuffer.put('b');

charBuffer.put('c');

charBuffer.flip();

char c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

a b c

position

limit

capacity

Page 80: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Bufferes en java.nio: Ejemplo de usoBufferes en java.nio: Ejemplo de usoCharBuffer charBuffer = CharBuffer.allocate(5);

charBuffer.put('a');

charBuffer.put('b');

charBuffer.put('c');

charBuffer.flip();

char c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

a b c

position

limit

capacity

Page 81: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Bufferes en java.nio: Ejemplo de usoBufferes en java.nio: Ejemplo de usoCharBuffer charBuffer = CharBuffer.allocate(5);

charBuffer.put('a');

charBuffer.put('b');

charBuffer.put('c');

charBuffer.flip();

char c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

a b c

position

limit

capacity

c =‘a’

Page 82: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Bufferes en java.nio: Ejemplo de usoBufferes en java.nio: Ejemplo de usoCharBuffer charBuffer = CharBuffer.allocate(5);

charBuffer.put('a');

charBuffer.put('b');

charBuffer.put('c');

charBuffer.flip();

char c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

a b c

position

limit

capacity

c =‘b’

Page 83: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Bufferes en java.nio: Ejemplo de usoBufferes en java.nio: Ejemplo de usoCharBuffer charBuffer = CharBuffer.allocate(5);

charBuffer.put('a');

charBuffer.put('b');

charBuffer.put('c');

charBuffer.flip();

char c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

a b c

position

limit

capacity

c =‘c’

Page 84: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Bufferes en java.nio: Ejemplo de usoBufferes en java.nio: Ejemplo de usoCharBuffer charBuffer = CharBuffer.allocate(5);

charBuffer.put('a');

charBuffer.put('b');

charBuffer.put('c');

charBuffer.flip();

char c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

c = charBuffer.get();

a b c

position

limit

capacity

throws java.nio.BufferUnderflowException

Page 85: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Channel es una interfaz definida en el paquete java.nio.channels•El Channel representa una conexión abierta hacia una entidad sobre la que se puede realizar entrada/salida de datos•Un Channel recién creado está abierto, una vez que se cierra no es posible realizar operaciones posteriores de entrada/salida. Se puede saber si un Channel está abierto invocando el método (de la interfaz) isOpen()•La interfaz Channel cuenta con un conjunto de subinterfaces y de clases que la implementan

•Algunas Subinterfaces: ByteChannel, InterruptibleChannel, ReadableByteChannel, WritableByteChannel, etc.

•Algunas clases que lo implementan: •FileChannel: Especialista en asociarse a FileStreams para entrada/salida•SocketChannel: Especialista en asociarse a instancias de la clase Socket•ServerSocketChannel: Especialista en asociarse a instancias de ServerSocket•DatagramChannel: Especialista en asociarse a instancias de DatagramSocket•SelectableChannel: Channel que puede ser multiplexado a través de un Selector. Es superclase de todos los Channels que tienen que ver con Sockets

Channels en java.nioChannels en java.nio

Page 86: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Ejemplo de uso de la clase FileChannelEjemplo de uso de la clase FileChannelimport java.io.*;import java.nio.*;import java.nio.channels.*;

public class FileChannelExample {private static final String FILENAME = "channelFile.dat";private static final String MESSAGE = "hola nena";public static void main(String[] args) throws IOException {

writeFileWithChannel();readFileWithChannel();

}

public static void writeFileWithChannel() throws IOException {FileOutputStream fos = new FileOutputStream(FILENAME);ByteBuffer buffer = ByteBuffer.allocateDirect(1024);buffer.put(MESSAGE.getBytes("ISO-8859-1"));buffer.flip();FileChannel ch = fos.getChannel();ch.write(buffer);ch.close();

}

public static void readFileWithChannel() throws IOException {FileInputStream fis = new FileInputStream(FILENAME);FileChannel ch = fis.getChannel();ByteBuffer buffer = ByteBuffer.allocateDirect(1024);ch.read(buffer);buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data, "ISO-8859-1");ch.close();

}}

Page 87: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Este mecanismo permite leer/escribir sobre un socket de red sin bloqueos•El modelo de programación se basa en eventos•Hay que registrar “interés” por un evento de entrada/salida determinado •Cuando el evento “sucede”, el sistema informa del mismo•Se procesa el evento ocurrido•Se vuelve a esperar por eventos

•La clase Selector se utiliza para la gestión de eventos en subclases de SelectableChannels•Selector es una clase abstracta que proporciona un multiplexor de channels•Un SelectableChannel que se registra en un Selector, queda representado mediante un objeto de la clase SelectionKey•Las instancias de SelectionKey tienen múltiples métodos de interés

•SelectableChannel channel(): recupera el channel asociado a la key•Selector selector(): recupera el Selector en que se creó la key

•Al registrar el SelectableChannel hay que especificar los eventos de interés•Cuando todos los channels y sus eventos se han registrado, el Selector está listo

Entrada/Salida asíncrona con Sockets en JavaEntrada/Salida asíncrona con Sockets en Java

Page 88: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

•Partimos de instancias de subclases de SelectableChannel•Abrimos un Selector•Registramos los channels declarando los eventos de nuestro interés•Los eventos de interés son enteros potencias de dos•Se pueden activar varios eventos usando el operador | (bitwise)

•SelectionKey.OP_ACCEPT: Cuando se ha recibido una solicitud de conexión•SelectionKey.OP_CONNECT: Cuando se ha concluido el establecimiento de una conexión o bien cuando hay un error que afecta la conexión•SelectionKey.OP_READ: Cuando se puede leer•SelectionKey.OP_WRITE: Cuando se puede escribir

•El Selector tiene un método select() cuya invocación es bloqueante. Sólo retorna cuando se da alguna de las condiciones siguientes:

•Ocurre (al menos) un evento de interés sobre alguno de los canales registrados•Se interrumpe el hilo llamante (mediante llamada a interrupt())•Se invoca el método wakeup() del Selector

•La llamada devuelve un entero que coincide con el número de eventos ocurridos•El Selector puede recuperar todas las SelectionKeys de channels con eventos, para ello se utiliza el método siguiente: Set<SelectionKey> selectedKeys()

Uso de selectoresUso de selectores

Page 89: Tema II: Programación con sockets Luis López Fernández

Tema II: Programación con Sockets

Ejemplo de uso de un SelectorEjemplo de uso de un SelectorSelector selector = Selector.open();

ServerSocketChannel sschannel = ServerSocketChannel.open();

sschannel.configureBlocking(false);

ServerSocket ssocket = sschannel.socket();

ss.bind(new InetSocketAddress(2345));

sschannel.register(selector, SelectionKey.OP_ACCEPT);

//Otros SelectableChannels pueden registrarse en el selector

...

int num = selector.select(); //Llamada bloqueante

Set<SelectionKey> selectedKeys = selector.selectedKeys()

Iterator it = selectedKeys.iterator();

while(it.hasNext()){

SelectionKey key = it.next();

if(key.isAcceptable()){

ServerSocketChannel sschannel = (ServerSocketChannel)key.getChannel();

SocketChannel schannel = sshchannel.accept(); //Llamada no bloqueante

...

} else if (key.isReadable()){ //registrado con SelectionKey.OP_READ

...

} else is (key.isWritable()){ //registrado con SelectionKey.OP_WRITE

...

}

}

Page 90: Tema II: Programación con sockets Luis López Fernández

Tema II: Comentarios y referenciasTema II: Comentarios y referencias

Tema I: Introducción a las computación distribuidaLuis López Fernández

•Comentarios y reflexiones•Imagina que una aplicación requiere que el tráfico emitido responda a un determinado patrón temporal (VoIP, Video Conferencia, etc). Analiza las ventajas e inconvenientes de usar sockets TCP y sockets UDP en cada caso.•¿Crees que es importante la sincronización de los procesos cuando estos comunican? ¿Qué técnicas de programación habría que utilizar si no contásemos con la sincronización a través de bloqueos?•¿Por qué crees que Java utiliza Streams para realizar entrada/salida en sockets TCP, pero no los utiliza para sockets UDP?•¿Qué es el algoritmo de Nagle y como afecta al funcionamiento de un socket? ¿En qué casos hay que activarlo y en qué casos hay que desactivarlo?•Los sockets con entrada/salida asíncrona son más complejos de utilizar y aumentan la complejidad de los programas que los utilizan. ¿Qué ventajas ofrecen a cambio?

•Referencias•M.L. Liu, Computación Distribuida: Fundamentos y Aplicaciones, Pearson Addison Wesley, 2004. Capítulo 4•Bruce Eckel, Piensa en Java, Prentice Hall, 2003•Nunca desprecies el poder Wikipedia (www.wikipedia.org)

Page 91: Tema II: Programación con sockets Luis López Fernández

Tema II: ResumenTema II: Resumen

Tema I: Introducción a las computación distribuidaLuis López Fernández

•Contenidos•Introducción

•Concepto de socket•Servicios ofrecidos por la API de Sockets

•Streams•Concepto de stream•InputStreams y OutPutStreams•Readers y Writers•Interconexión de streams

•Sockets TCP•La API de stream sockets en Java•Sockets TCP y streams•Programas de ejemplo con sockets TCP

•Sockets UDP•La API de datagram sockets en Java•Entrada/salida en datagram sockets•Programas de ejemplo con sockets UDP

•Sockets con entrada/salida asíncrona en Java•El paquete java.nio•Buffers•Channels•Selectors