Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Programación de Servicios y Procesos
Programación de Servicios y Procesos
y procesos
Desarrollo de Aplicaciones Multiplataforma
Inazio Claver
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Índice
Presentación .......................................................................................................................4
Introducción a los procesos ..................................................................................................5
Estados del proceso .................................................................................................................. 5
Procesos en los SSOO ................................................................................................................ 6
Procesos en C ......................................................................................................................8
EXECL ......................................................................................................................................... 8
System ....................................................................................................................................... 8
PID ............................................................................................................................................. 8
PIPE.......................................................................................................................................... 11
FIFO (o PIPE con nombre) ....................................................................................................... 13
Señales y sincronización .......................................................................................................... 16
Programación concurrente................................................................................................. 20
Programación paralela ............................................................................................................ 21
Programación distribuida .................................................................................................... 22
Hilos.................................................................................................................................. 24
Hilos en Java ............................................................................................................................ 24
Runnable. Heredar de la clase Thread ................................................................................ 27
Applets ................................................................................................................................ 28
Programación multihilo ..................................................................................................... 34
Estados de un hilo ................................................................................................................... 34
Gestión de un hilo ................................................................................................................... 35
Prioridad en los hilos ........................................................................................................... 38
Ejercicios de gestión de hilos .............................................................................................. 39
Procesos concurrentes ............................................................................................................ 49
Algoritmo de Dekker ........................................................................................................... 49
Algoritmo de Peterson ........................................................................................................ 54
Programación en red ........................................................................................................ 61
TCP/IP ..................................................................................................................................... 61
Nivel de Transporte ............................................................................................................. 61
Paquete java.net....................................................................................................................... 61
1
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Clase InetAddress.................................................................................................................... 61
Clase URL ............................................................................................................................... 63
Clase URLConnection ............................................................................................................ 66
Scripts del lado del servidor ................................................................................................ 68
Programación de sockets .................................................................................................. 71
¿Qué son los sockets?.............................................................................................................. 71
Funcionamient en general de un socket .................................................................................. 71
Clase ServerSocket.................................................................................................................. 72
Clase Socket ............................................................................................................................ 73
Gestión de Sockets TCP .......................................................................................................... 74
Conexión de múltiples clientes. Hilos ..................................................................................... 76
Clases para Sockets UDP ........................................................................................................ 77
Clase DatagramPacket......................................................................................................... 78
Clase DatagramSocket ........................................................................................................ 78
Gestión de sockets UDP .......................................................................................................... 79
MulticastSocket ....................................................................................................................... 84
Envío de objetos a través de Socket TCP ................................................................................ 87
ObjectInputStream & ObjectOutputStream ....................................................................... 87
Serialización......................................................................................................................... 87
Envío de objetos a través de Sockets UDP .............................................................................. 89
RMI .......................................................................................................................................... 90
Introducción a las aplicacione RMI ..................................................................................... 90
Objetos distribuidos ............................................................................................................ 90
Pasaje de objetos ................................................................................................................ 91
Ejemplo RMI ........................................................................................................................ 92
Ejemplo RMI. Código ........................................................................................................... 93
Conclusión ........................................................................................................................... 95
Resumen rápido .................................................................................................................. 95
Ejercicio simple. La hipoteca ............................................................................................... 97
Programación segura ....................................................................................................... 103
Buenas prácticas en programación ....................................................................................... 103
Criptografía............................................................................................................................ 105
Certificados digitales ............................................................................................................. 108
Control de acceso .................................................................................................................. 110
Seguridad en entorno Java .................................................................................................... 111
APIs JAVA para seguridad .................................................................................................. 113
Ficheros de políticas en Java ................................................................................................. 114
2
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
3
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Presentación
Bienvenido a los apuntes del módulo de Programación de servicios y procesos, estudiado en el
Ciclo Superior de Desarrollo de Aplicaciones Multiplataforma.
En las siguientes páginas intentaré explicar de manera clara y concisa las bases que te
permitirán entender la programación basada en varios hilos y procesos, usada para obtener un
mejor rendimiento del sistema y permitir crear aplicaciones que optimicen los tiempos de
respuesta para el usuario.
Inazio Claver
4
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Cada una de las CPU se tienen que compartir a ratos con todos los procesos cargados en la
memoria RAM.
Cuando un programa usa la CPU y sale, hay que hacer una especie de instantánea para guardar
el contador de procesos pendientes, donde se ha quedado, etc.
Esto se conoce como el BCP (bloque de control de procesos). Es una tabla del sistema
operativo que guarda la información de la situación de como se había quedado el programa en
el momento de finalizar su tiempo en la CPU y dejar espacio a otro proceso.
La contienda es como se llama a la pugna que hacen los procesos listos para conquistar la CPU.
5
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Para verlos desde la línea de comandos, en CMD escribiremos tasklist. Además al hacerlo por
línea de comandos veremos el PID, el identificador del proceso.
6
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
7
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Procesos en C
Vamos a realizar las primeras prácticas con los procesos, los PID y los PPID en el lenguaje C.
Para ello hemos instalado un entorno VitaLinux que trae ya incorporado el compilador gcc,
pero podéis utilizar cualquier herramienta de vuestra elección que os permita trabajar
programando en C.
EXECL
Execl sirve para ejecutar comandos del sistema y cualquier programa. Los argumentos son
(const char *fichero, cons char *arg0, …, char *argN, (char*)NULL).
Devolverá -1 si hay condición de error.
#include <stdio.h>
#include<unistd.h>
void main(){
System
A diferencia de execl, system ejecutará sólo comandos del sistema, como podemos ver abajo.
Vamos a ver el siguiente programa para hacernos una idea.
#include <stdio.h>
#include <stdlib.h>
void main(){
PID
En la arquitectura cliente – servidor tenemos dentro de un servidor web un proceso que está
escuchando, en listen. Le hacen una petición y este proceso crea un hijo que sirva la página
web al cliente. Una vez finalizada la tarea, el hijo sale de la memoria al finalizar su tarea.
8
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Vamos a ver un programa que nos muestre los identificadores del proceso actual y de su
padre.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void main(){
id_pactual = getpid();
id_padre = getppid();
Para poder diferenciar uno de otro lo conseguimos con el valor devuelto por fork(). Con un
ejemplo se verá más claro.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void main(){
if (pid == -1){
printf(“Ha habido un error”);
exit(-1);
}
if(pid == 0){
// Nos encontramos dentro del proceso hijo
printf("soy el proceso hijo \n\t Mi PID es %d. El PID
de mi padre es: %d. \n", getpid(), getppid());
9
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}
else{
hijo_pid = wait(NULL);
printf("Soy el proceso padre: \n\t Mi PID es %d. El
PID de mi padre es: %d. \n\t Mi hijo %d terminó. \n", getpid(),
getppid(), pid); }
}
Hay varios puntos que comentar. El primero es que como ya hemos dicho, los procesos pese a
estar en un mismo programa, tanto el hijo como el padre son independientes en el momento
de ejecutar fork().
Si nos devuelve un valor igual a 0, sabremos que estamos tratando con el hijo, mientras que
cualquier otro valor que no indique una condición de error nos hará saber que está
funcionando el padre.
Con ésta línea indicaremos que va a esperar a la finalización del proceso hijo, y la variable pid
guardará el PID del padre.
Ejercicio. Coged un proceso, cread una variable y guardad un valor. 7, por ejemplo. El proceso
crea un hijo que le suma 5, y el padre le resta 5. Visualiza por pantalla el resultado de ambas
operaciones así como el PID y el PPID de ambos procesos.
Solución:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void main(){
pid = fork();
if (pid == -1){
printf(“Error \n”);
exit(-1);
}
if (pid == 0){
n = n + 5;
printf("Soy el hijo. Valor de n = %d.\n Proceso %d,
padre %d \n\n", n, getpid(), getppid());
}
else{
n = n – 5;
printf("Soy el padre. Valor de n = %i.\n Proceso %d,
padre %d \n\n", n, getpid(), getppid());
}
10
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
PIPE
Las PIPE (tuberías) son un mecanismo para poner en comunicación los procesos padre e hijo.
Se comporta como un falso fichero en el que ambos pueden leer y escribir.
Conceptualmente hablando, tendremos dos procesos, padre e hijo. Entre ellos se creará una
tubería que emplearán para leer y escribir.
Para ello usaremos un array de enteros de dos posiciones. El [0] será de lectura, y el [1] de
escritura. Es bidireccional pero sólo puede usarse en uno de los dos sentidos.
Si necesitamos una comunicación que circule en los dos sentidos, habrá que crear dos PIPE,
uno para cada dirección.
Para poder utilizar este método de comunicación deberemos cargar la librería unistd, y tener
en cuenta que se escribe como los ficheros, usando la función write y read.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void main(){
int fd[2];
char buffer[30];
pid_t pid;
//#include<unistd.h>
// int pipe(int fd[2]);
// fd[0] contiene el descriptor para lectura
switch(pid){
11
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
En el código podemos ver que después de la declaración de cada proceso, tenemos una lína
que reza:
close(fd[1]);
Con esto lo que hacemos es cerrar la parte de la tubería que no vamos a utilizar. Me explico, si
el que escribe es el hijo, en su lado cerraremos la posición cero, y si el padre es el que va a leer,
cerraremos la posición 1. Así conseguiremos algo más de seguridad en nuestro programa. Es
una práctica recomendable, aunque es verdad que nuestro código funcionará exactamente
igual sin hacerlo.
Ejercicio. Haz tres procesos (padre, hijo y nieto) que se comuniquen entre ellos a través de
PIPEs.
Solución.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void main(){
pipe(fd1);
pipe(fd2);
pid = fork();
switch(pid){
//Creación de nieto
pidNieto = fork();
12
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
switch(pidNieto){
case -1: // Error
printf("Ha habido un error \n");
exit(-1);
break;
case 0:
close(fd2[0]);
printf("Escribe el nieto \n");
write(fd2[1], "Soy el nieto", 13);
break;
default:
close(fd2[1]);
wait(NULL);
printf("El padre lee \n");
read(fd2[0], buffer2, 13);
printf("Mensaje leído: %s", buffer2);
break;
}
break;
default: // Padre
close(fd1[1]);
wait(NULL); // Espero que finalice el nieto
printf("\nEl abuelo lee: \n");
read(fd1[0], buffer, 20);
printf("Mensaje leído: %s \n", buffer);
break;
}
}
Las PIPE en principio solo sirven para comunicaciones padre – hijo, por lo que para
comunicarnos entre procesos independientes necesitamos otro tipo estructura.
Esta son los FIFO, archivos que ya no se crean en el proceso padre, sino que lo genera el
sistema operativo. Es un ente que genera el SO y que podrá ser utilizado por diversos procesos
estén o no emparentados.
Es decir, los mecanismos de comunicación no tienen que estar necesariamente emparentados.
Para operar con ellos en C usaremos comandos de ficheros de bajo nivel (write, open…)
El comando que permite crear una FIFO se mknod, al igual que la función de C que permite
generarlas en ese lenguaje.
13
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Con el parámetro mode indicamos los permisos que tendrá nuestro FIFO.
Una vez generado podemos comprobarlos haciendo un ls-l
Hecho esto, y sabiendo que funciona como una pila, podemos meterle información para que lo
vaya leyendo otro proceso que acceda al FIFO.
Vamos a introducirle el contenido de visualizar los directorios, con un ls, para luego visualizar
por pantalla su contenido con un cat.
En C podemos programar nuestros procesos para que mientras uno crea y está a la escucha
para leer el FIFO, otro introduzca información en él y sea leído por el primero.
Para ello usaremos la función mknod (tiene el mismo nombre que el comando usado para
generar el fichero).
Donde:
Pathname: Nombre del dispositivo
Modo: Especifica tanto los permisos de uso como el tipo de nodo que se creará
Dev: Debe ser una combinación (utilizando OR bit a bit) de uno de los tipos de fichero
que se enumeran a continuación.
• S_IFREG
• S_IFCHR
• S_IFBLK
• S_IFIFO – Para crear un FIFO
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
14
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
#include <fcntl.h>
int main(void){
int fp;
int p, bytesLeidos;
char saludo[] = "Un saludo!!!\n", buffer[10];
if (p == -1){
printf("Ha ocurrido un error \n");
exit(0);
}
while(1){
fp = open("FIFO3", 0);
bytesLeidos = read(fp, buffer, 1);
printf("Obteniendo información... \n");
while(bytesLeidos != 0){
printf("%s", buffer);
bytesLeidos = read(fp, buffer, 1); // lee otro
byte
}
close(fp);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
int fp;
char saludo[] = "Un saludo!!!\n";
fp = open("FIFO3", 1); // Abre el fichero FIFO ya creado
if(fp == -1){
printf("Error al abrir el fichero...\n");
exit(1);
}
close(fp);
return 0;
}
15
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Al mostrar el texto por pantalla lo vemos de esta forma porque uno trabaja en ASCII y otro en
UNICODE, pero para hacernos una idea de que se comunican correctamente sirve.
Señales y sincronización
En un sistema, los procesos que se ejecutan simultáneamente interactúan entre sí. Esta
interacción se produce incluso en el caso de procesos independientes, es decir, los que no
necesitan cooperar para completar sus tareas.
Esto ocurre cuando varios procesos quieren acceder a los mismos recursos, y para resolver
esta situación el SO dispone de un gestor de procesos para determinar el orden de acceso a
estos recursos.
Las señales pueden considerarse un tipo de mensajes, aunque, comparado con otros medios
de comunicación (sockets, pipes, etc.) resultan un mecanismo más pobre porque no permiten
transmitir datos, pero sí proporcionan dos servicios fundamentales:
Defensa del proceso establecido frente a incidencias comunicadas por el kernel. Si las
señales no son gestionadas (o ignoradas, o capturadas) por el proceso al que van
dirigidas, éste concluye inmediatamente lo que puede provocar una pérdida
irrecuperable de datos.
Mecanismo de comunicación entre dos procesos. Resulta útil y sencillo para avisar a
un proceso de la aparición de eventos excepcionales, aunque no debe ser la forma
habitual de que se comuniquen. Por ejemplo, cuando un usuario desea interrumpir un
proceso de impresión que ha mandado por error.
Las señales pueden llegar en cualquier momento, por lo que los procesos no pueden limitarse
a verificar una variable para comprobar si ha llegado una señal, sino que deben lanzar una
rutina de tratamiento de la señal para gestionar automáticamente su recepción en el
momento que aparezca.
16
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
17
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Una de las principales utilidades de las señales es la sincronización entre dos procesos. Esto es,
un proceso realizará un conjunto de instrucciones cuando otro proceso se lo indique, o
paralizará su actividad hasta que se cumpla una condición determinada, teniendo en cuenta la
región crítica, que sería el trozo de código de un proceso que puede interferir con otro
proceso.
Esta secuencia de acciones se ejecutan en paralelo y pueden repetirse infinitamente, con lo
que el bucle tendría una traza tal que así:
El comando kill en Linux envía una señal a un proceso, indicando primero la señal y luego el
PID.
Por ejemplo,termina kill -9 PID el proceso con el PID que hemos indicado.
Para crear una señal en C debemos tener en cuenta la estructura de las funciones que
podemos utilizar
Esto envía una señal invocando a un manejador por puntero para que la reciba y la trate:
void (*signal(int señal, void(*Func)(int)(int));
int pause(void);
Veamos un ejemplo de como un padre invoca a una señal que recibirá el hijo.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
18
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
int main(){
int pid_hijo;
pid_hijo = fork(); // creamos hijo
switch(pid_hijo){
case -1:
printf("ERROR AL CREAR EL PROCESO HIJO... \n");
exit(-1);
break;
case 0: // HIJO
signal(SIGUSR1, manejador); // Invocamos al
puntero al que referencia la función
while(1){};
break;
default: // PADRE
sleep(1);
kill(pid_hijo, SIGUSR1);
sleep(1);
kill(pid_hijo, SIGUSR1);
sleep(1);
break;
}
return 0;
}
19
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Programación concurrente
Recordemos dos definiciones antes de seguir con la programación concurrente.
Beneficios
Problemas inherentes
20
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Cuando dos p más procesos comparten una variable, el acceso a dicha variable debe
efectuarse siempre dentro de una región crítica asociada a la variable. Solo uno de los
procesos podrá acceder para actualizarla y los demás deberán esperar. El tiempo de
estancia es finito.
Ejemplo:
int var = 7;
while(var <= 100){
printf(“%i”, var);
var++;
}
Si mientras sale ese proceso entra otro y aumentar var en 71 (es un decir), el primer
proceso ya no se llegará a ejecutar 93 veces, porque la variable ha sido alterada por
otro proceso. Este trozo de código es la región crítica.
Condición de sincronización. Hace referencia a la necesidad de coordinar los procesos
con el fin de sincronizar sus actividades. Puede ocurrir que un proceso P1 llegue a un
estado X que no pueda continuar su ejecución hasta que otro proceso P2 haya llegado
a un estado Y de su ejecución. La programación concurrente proporciona mecanismos
para bloquear procesos a la espera de que ocurra un evento y para desbloquearlos
cuando este ocurra.
Programación paralela
Ventajas
Inconvenientes
21
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Programación distribuida
Consecuencias
Ventajas
Inconvenientes
22
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
23
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Hilos
¿Qué es?
La diferencia entre un hilo y un proceso es que el proceso, al crear un hijo, se duplica y tiene su
espacio de direcciones, compitiendo en igualdad de condiciones con el proceso padre. Sin
embargo, en un hilo (o proceso ligero) lo que hace el proceso es crear un espacio de
direcciones dentro de su espacio de direcciones, por lo que la pugna por competir por el
tiempo de procesador se verá en “inferioridad de condiciones” respecto a un proceso padre –
hijo.
Ventajas
Al tener un marco común es fácil crear una variable y que varios procesos accedan a él.
Desventajas
Hilos en Java
Aparte del método run() hay otros métodos dentro de la clase Thread que tendremos que
usar:
24
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
yield() Hace que el hilo en ejecución deje la ejecución para permitir que otros hilos se
ejecuten.
En el siguiente código vamos a ver una clase que herede de Thread (HiloEjemplo) donde
estarán los métodos y propiedades que estimemos oportunos y que tendrá que implementar
obligatoriamente el método run().
La segunda clase será la que contenga el código principal (Main). Creará un objeto de esta
clase y será donde se lance el objeto.
ARCHIVO HILOEJEMPLO.JAVA
// Propiedades
private int c; // Contador de cada hilo
private int hilo;
// Constructor
public HiloEjemplo(int hilo){
this.hilo = hilo;
System.out.println("CREANDO HILO: " + hilo);
}
// Métodos
public void run(){
c = 0;
while (c <= 5){
System.out.println("Hilo: " + hilo + " C = " + c);
c++;
}
} // fin del run
}
ARCHIVO MAIN.JAVA
Si pruebas a lanzar la ejecución varias veces verás que el resultado que se muestra por pantalla
es variable. Esto es porque actualmente no controlamos el orden en el que los hilos entrán en
la CPU.
25
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Ejercicio. Crea dos clases (hilos) Java que extiendan la clase Thread. Uno de los hilos debe
visualizar por pantalla en un bucle infinito la palabra TIC y el otro hilo la palabra TAC.
Dentro del bucle utiliza el método sleep() para que nos dé tiempo a ver las palabras que se
imprimen cuando lo ejecutamos (tendrás que añadir un bloque try-catch para capturar la
excepción InterruptedException). Crea después la clase Main que haga uso de los hilos
anteriores. ¿Se visualizan los textos de forma ordenada (es decir TIC TAC TIC TAC TIC TAC…)?
ARCHIVO TIC.JAVA
// Propiedades
private int hilo;
// Constructor
public Tic(int hilo){
this.hilo = hilo;
}
// Métodos
public void run(){
while(true){
try{
//this.notify();
System.out.println("TIC");
sleep(1000);
//yield();
//this.wait();
}
catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
ARCHIVO TAC.JAVA
// Propiedades
private int hilo;
// Constructor
public Tac(int hilo){
this.hilo = hilo;
}
// Métodos
public void run(){
while(true){
try{
//this.notify();
System.out.println("TAC");
sleep(1000);
26
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
yield();
//this.wait();
}
catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
ARCHIVO MAIN.JAVA
En Java solo puede heredarse de una clase, de modo que implementaremos la interface
Runnable para conseguir algo parecido a la multiherencia.
La usaremos cuando ya hayamos heredado de la clase Thread para poder crear un segundo
hilo.
ARCHIVO PRIMERHILOR.JAVA
// Propiedades
int x;
// Constructor
public PrimerHiloR(int x){
this.x = x;
}
27
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
ARCHIVO MAIN.JAVA
Applets
Un Applet es una aplicación Java que se incrusta en una página HTML, aunque los
navegadores, por defecto, no permiten este tipo de programas en las webs porque son una
brecha de seguridad.
Estos applets se extienden de la clase Applet, así que la creación de los hilos, en caso de que
queramos hacerlo todo en una única clase, habrá que dejarlo a Runnable.
import java.applet.Applet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.text.SimpleDateFormat;
import java.util.Calendar;
@Override
public void run() {
Thread hiloActual = Thread.currentThread();
while (hilo == hiloActual){
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
Calendar cal = Calendar.getInstance();
horaActual = sdf.format(cal.getTime());
repaint(); //se actualiza el contenido del Applet
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
28
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}
}
}//fin de run
import java.applet.Applet;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
setBackground(Color.yellow);//color de fondo
add(b1=new Button("Iniciar contador"));
b1.addActionListener(this);
add(b2=new Button("Parar contador"));
b2.addActionListener(this);
fuente = new Font("Verdana", Font.BOLD, 26);//tipo letra
parar=false;
Thread hiloActual = Thread.currentThread();
while (h == hiloActual && !parar) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
29
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}
repaint();
CONTADOR++;
}
}
b1.setLabel("Continuar");
if(e.getSource()==b1) //comienzo
{
if(h!=null && h.isAlive()) {
} //Si el hilo está corriendo no hago nada,
else { //lo creo
h = new Thread(this);
h.start();
}
}else if(e.getSource()==b2) //parada
parar=true;
}//fin de actionPerformed
}//fin applet
Pero no tiene porqué ser así. Partiendo del ejemplo anterior, podemos realizar un applet que
separe el hilo en una clase aparte dentro del applet que extienda Thread. El applet que ahora
no implementará Runnable, debe quedar así:
30
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Para hacernos una idea, vamos a crear la misma aplicación de contador que antes pero que
lance dos hilos y muestre dos botones para finalizarlos. Algo así
Es decir, definimos en la clase HiloContador un constructor que reciba el valor inicial del
contador a partir del cual empezará a contar; y el método getContador() que devuelva el valor
actual del contador.
El applet debe crear e iniciar dos hilos de esta clase, cada uno debe empezar con un valor.
Mostrará dos botones, uno para detener el primer hilo y el otro el segundo. Para detener los
hilos usa el método stop(): hilo.stop() (Veremos más adelante que este método está en desuso
y no se debe emplear). Cambiamos también el texto de los botones cuando se pulsen, para
que muestre algún mensaje dependiendo del botón pulsado.
import java.applet.Applet;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
// Propiedades
long CONTADOR = 0;
boolean parada = false;
// Constructor
public HiloContador(int hilo){
CONTADOR = hilo;
}
// Métodos
public void pararHilo(){
parada = true;
}
31
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
// Propiedades
private Font fuente;
private Button btn1, btn2;
HiloContador t1, t2;
int a, b;
// Métodos
public void init(){
// Los genero con inicio aleatorio
t1 = new HiloContador((int) (Math.random()*100+1));
t2 = new HiloContador((int) (Math.random()*100+1));
setBackground(Color.orange);
fuente = new Font("Verdana", Font.BOLD, 26);
add(btn1 = new Button("Parar contador 1"));
btn1.addActionListener(this);
add(btn2 = new Button("Para contador 2"));
btn2.addActionListener(this);
t1.start();
t2.start();
}
if(e.getSource() == btn1)
{
if(t1.isAlive()) {
t1.pararHilo();
a = (int) t1.CONTADOR;
btn1.setLabel("Activar contador 1");
32
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}
else {
t1.activarHilo();
t1 = new HiloContador(a+1);
t1.start();
btn1.setLabel("Parar contador 1");
}
}else if(e.getSource() == btn2){
if(t2.isAlive()){
t2.pararHilo();
b = (int) t2.CONTADOR;
btn2.setLabel("Activar contador 2");
}
else{
t2.activarHilo();
t2 = new HiloContador(b+1);
t2.start();
btn2.setLabel("Parar contador 2");
}
}
} // Fin actionPerformed
}
33
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Programación multihilo
Estados de un hilo
New (Nuevo). Es el estado cuando se crea un objeto hilo con el operador new. En este
estado el hilo aún no se ejecuta; es decir, el programa no ha comenzado la ejecución
del código del método run() del hilo.
Runnable (Ejecutable). Cuando se invoca al método start(), el hilo pasa a este estado.
El sistema operativo tiene que asignar tiempo de CPU al hilo para que se ejecute; por
tanto, en este estado el hilo puede estar o no en ejecución.
Dead (Muerto). Un hilo muere por varias razones:
• De muerte natural, porque el método run() finaliza con normalidad.
• Repentinamente debido a alguna excepción no capturada en el método run().
• Invocando al método stop(), pero este método está en desuso.
// Propiedades
private boolean stopHilo = false;
// Métodos
public void pararHilo(){
stopHilo = true;
}
// Main
34
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Blocked (Bloqueado). En este estado podría ejecutarse el hilo, pero hay algo que lo
evita. Un hilo entra en estado bloquedo cuando ocurre una de las siguientes acciones:
• Alguien llama al método sleep() del hilo, es decir, se ha puesto a dormir.
• El hilo está esperando a que se complete una operación de entrada / salida.
• El hilo llama al método wait(). El hilo no se volverá ejecutable hasta que reciba
los mensajes notify() o notifyAll().
• El hilo intenta bloquear un objeto que está actualmente bloqueado por otro
hilo.
• Alguien llama al método suspend() del hilo. No se volverá a ejecutar de nuevo
hasta que recia el mensaje resume().
Gestión de un hilo
Crear y arrancar
// Se crea un hilo
MiHilo h = new MiHilo(“Hilo 1”, 200);
// Se arranca el hilo
h.start(); // Si la clase extienda a Thread
new Thread(h).start(); // Si implementa a Runnable
Lo que hace el método start() es llamar al método run() del hilo que es donde se colocan las
acciones que queremos que haga el hilo, cuando finalice el método finalizará también el hilo.
Suspensión
El método suspend() permite detener la actividad del hilo durante un intervalo de tiempo
indeterminado.
Para suspender de forma segura el hilo se debe introducir dentro de este una variable, por
ejemplo suspender, y comprobar su valor dentro del método run().
35
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
// Propiedades
private SuspendRequestor suspender = new SuspendRequestor();
// Métodos
public void requestSuspend(){
suspender.set(true);
}
class SuspendRequestor{
// Propiedades
private boolean suspendRequested;
// Métodos
public synchronized void set(boolean b){
suspendRequested = b;
notifyAll();
}
Parada
Los métodos stop(), suspend(), resume() y destroy() han sido abolidos en Java 2 para reducir la
posibilidad de interbloqueo.
El método isAlive() devuelve true si el hilo está vivo, es decir, ha llamado a su método run() y
aún no ha terminado su ejecución o no ha sido detenido con stop(); en caso contrario devuelve
false.
36
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
El método join() provoca que el hilo que hace la llamada espere la finalización de otros hilos.
private int n;
37
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
h1.start();
h2.start();
h3.start();
try{
h1.join();
h2.join();
h3.join();
}
catch(InterruptedException e){}
En el lenguaje de programación Java, cada hilo tiene una prioridad. Por defecto un hilo hereda
la prioridad del hilo padre que lo crea.
La prioridad va de 1 a 10
• MIN_PRIORITY 1
• MAX_PRIORITY 10
• NORM_PRIORITY 5
Si dos o más hilos tienen la misma prioridad, la máquina virtual va cediendo la prioridad de
forma cíclica (round-robin).
Cuando un hilo entra en ejecución y no cede voluntariamente el control para que puedan
ejecutarse otros hilos, se dice que es un “hilo egoísta”. Windows combate esta situación con
una estrategia de planificación por división de prioridad que compiten por la CPU.
38
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Vamos a ver cuatro tipos de este problema. Intentad haced cada uno por vuestra cuenta y
comprobad el resultado posteriormente.
1. Modifica la clase Productor para que envíe las cadenas PING y PONG (de forma
alternativa, una vez PING y otra vez PONG) a la cola y la clase Consumidor tome la cadena de
la cola y la visualice.
La salida tiene que mostrar lo siguiente: PING PONG PING PONG PING PONG PING PONG
PING PONG PING PONG PING PONG PING PONG PING PONG PING PONG PING PONG PING
PONG PING PONG PING....
ARCHIVO COLA.JAVA
package Actividad26_productorConsumidor1;
while(disponible == false){
try{
wait();
}catch (InterruptedException e){}
}//fin de while
System.out.println("PONG");
disponible=false;
notifyAll();
return numero; //se devuelve
}//Fin de get
}//Fin de put
}//Fin de Cola
39
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
ARCHIVO PRODUCTOR.JAVA
package Actividad26_productorConsumidor1;
while(true){
cola.put();
try {
sleep(100);
} catch (InterruptedException e) { }
}
}//Fin de run
}//Fin de Productor
ARCHIVO CONSUMIDOR.JAVA
package Actividad26_productorConsumidor1;
cola = c;
this.n = n;
}
int valor = 0;
while(true) {
}//Fin de run
}//Fin de Consumidor
40
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
ARCHIVO PRODUC_CONSUM.JAVA
package Actividad26_productorConsumidor1;
p.start();
c.start();
}
}
2. Modifica la clase Productor para que el constructor reciba un argumento String que será el
mensaje que debe enviar a la cola y la clase Consumidor toma la cadena de la cola y la
visualice. El programa principal creará dos hilos Productor, uno que escribirá el mensaje
"TIC", y otro que escriba el mensaje "TAC".
La salida tiene que mostrar lo siguiente: TIC TAC TIC TAC TIC TAC TIC TAC TIC TAC TIC TAC TIC
TAC TIC TAC TIC TAC TIC TAC TIC TAC TIC TAC TIC TAC TIC TAC TIC TAC TIC TAC TIC TAC TIC
TAC TIC TAC….
ARCHIVO COLA.JAVA
package Actividad26_productorConsumidor2;
import java.util.Stack;
}//Fin de get
41
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}//Fin de put
}//Fin de Cola
ARCHIVO PRODUCTOR.JAVA
package Actividad26_productorConsumidor2;
while(true){
cola.put(cadena);
try {
sleep(100);
} catch (InterruptedException e) { }
}
}//Fin de run
}//Fin de Productor
ARCHIVO CONSUMIDOR.JAVA
package Actividad26_productorConsumidor2;
cola = c;
this.n = n;
}
42
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
while(true) {
r = cola.get(); //recoge el número
System.out.println(r);
}
}//Fin de run
}//Fin de Consumidor
ARCHIVO PRODUC_CONSUM.JAVA
package Actividad26_productorConsumidor2;
p1.start();
c.start();
p2.start();
}
}
3. “Refactoriza” el nombre de la clase Cola por con un nuevo nombre Monitor. Modifica la
clase Monitor para que el constructor reciba el número de productores que se van a
sincronizar. El programa principal creará 7 hilos Productor, cada uno respectivamente
escribirá el nombre de un día de la semana.
ARCHIVO MONITOR.JAVA
package Actividad26_productorConsumidor3;
import java.util.Stack;
// Propiedades
private int numero;
private boolean turnoConsumidor = false; //inicialmente cola vacía
private int turnoProductor = 1;
private Stack<String> contenido = new Stack<String>();
private String resultado;
private int contador = 0;
// Constructor
public Monitor(int numero){
this.numero = numero;
}
43
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
// Métodos
public synchronized String get() {
while(turnoConsumidor == false){
try{
wait();
}catch (InterruptedException e){}
}//fin de while
resultado = contenido.pop();
turnoConsumidor=false;
notifyAll();
return resultado; //se devuelve
}//Fin de get
}//Fin de Cola
ARCHIVO PRODUCTOR.JAVA
package Actividad26_productorConsumidor3;
while(true){
monitor.put(cadena, n);
try {
sleep(100);
} catch (InterruptedException e) { }
44
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}//Fin de run
}//Fin de Productor
ARCHIVO CONSUMIDOR.JAVA
package Actividad26_productorConsumidor3;
this.monitor = monitor;
this.n = n;
}
while(true) {
r = monitor.get(); //recoge el número
System.out.println(r);
}
}//Fin de run
}//Fin de Consumidor
ARCHIVO PRODUC_CONSUM.JAVA
package Actividad26_productorConsumidor3;
c.start();
p1.start();
p2.start();
p3.start();
p4.start();
45
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
p5.start();
p6.start();
p7.start();
}
}
4. Modifica el programa principal anterior para que se creen 12 hilos Productor con los
nombres de los meses. Si no lo has hecho ya, crea un vector de productores.
ARCHIVO MONITOR.JAVA
package Actividad26_productorConsumidor4;
import java.util.Stack;
// Propiedades
private int numeroProductores = 0;
private boolean turnoConsumidor = false; //inicialmente cola vacía
private int turnoProductor = 1;
private Stack<String> contenido = new Stack<String>();
private String resultado;
private int contador = 0;
// Constructor
public Monitor(int numeroProductores){
this.numeroProductores = numeroProductores;
}
// Métodos
public void setNumProductores(int n){
this.numeroProductores = n;
}
}//Fin de get
46
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
if (turnoProductor == numeroProductores)
turnoProductor = 1;
else
turnoProductor++;
}
notifyAll();
}//Fin de put
}//Fin de Cola
ARCHIVO PRODUCTOR.JAVA
package Actividad26_productorConsumidor4;
while(true){
monitor.put(cadena, n);
try {
sleep(100);
} catch (InterruptedException e) { }
}
}//Fin de run
}//Fin de Productor
ARCHIVO CONSUMIDOR.JAVA
package Actividad26_productorConsumidor4;
this.monitor = monitor;
this.n = n;
}
47
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
while(true) {
r = monitor.get(); //recoge el número
System.out.println(r);
}
}//Fin de run
}//Fin de Consumidor
ARCHIVO PRODUC_CONSUM.JAVA
package Actividad26_productorConsumidor4;
import java.util.Vector;
48
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Procesos concurrentes
Esta sección ha sido extraída del libro Sistemas Operativos, de William Stallings.
1. Debe cumplirse la exclusión mutua: Sólo un proceso, de entre todos los que poseen
secciones críticas por el mismo recurso u objeto compartido, debe tener permiso para
entrar en ella en un instante dado.
2. Un proceso que se interrumpe en una sección no crítica debe hacerlo sin estorbar a los
otros procesos.
3. Un proceso no debe poder solicitar acceso a una sección crítica para después ser
demorado indefinidamente; no puede permitirse el interbloqueo o la inanición.
4. Cuando ningún proceso está en su sección crítica, cualquier proceso que solicite entrar en
la suya debe poder hacerlo sin dilación.
5. No se pueden hacer suposiciones sobre la velocidad relativa de los procesos o su número.
6. Un proceso permanece en su sección crítica sólo por un tiempo finito.
Hay varias formas de satisfacer los requisitos de exclusión mutua. Una manera es dejar la
responsabilidad a los procesos que deseen ejecutar concurrentemente. Así pues, tanto si son
programas del sistema como de aplicación, los procesos deben coordinarse unos con otros
para cumplir la exclusión mutua, sin ayuda por parte del lenguaje de programación o del
sistema operativo. Estos métodos se conocen corno soluciones por software. Aunque las
soluciones por software son propensas a errores y a una fuerte carga de proceso, resulta útil
estudiar estos métodos para tener un mejor entendimiento de la complejidad del proceso
concurrente. Un segundo método propone el uso de instrucciones de la máquina a tal efecto.
Estas tienen la ventaja de reducir la sobrecarga pero, sin embargo, no son interesantes. El
tercer método consiste en dar algún tipo de soporte en el sistema operativo.
Pueden implementarse soluciones de software para los procesos concurrentes que ejecuten
en máquinas monoprocesador o multiprocesador con una memoria principal compartida.
Formalmente, estas soluciones suponen que existe una exclusión mutua elemental en el
acceso a memoria ([LAMP91]). Es decir, los accesos simultáneos (lecturas y/o escrituras) a la
misma posición de memoria se hacen en serie, por medio de algún tipo de árbitro de memoria,
aunque el orden en el que se conceden los accesos no se conoce por adelantado. Aparte de
esto, no se requiere ningún soporte del hardware, del sistema operativo o del lenguaje de
programación.
Algoritmo de Dekker
Dijkstra [DIJK65] presentó un algoritmo de exclusión mutua para dos procesos que diseñó el
matemático holandés Dekker. Según Dijkstra, la solución se desarrolla por etapas. Este método
49
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Primer intento
Como se mencionó anteriormente, cualquier intento de exclusión mutua debe depender de
algunos mecanismos básicos de exclusión en el hardware. El más habitual es la restricción de
que sólo se puede realizar un acceso a una posición memoria en cada instante. Como metáfora
de este arbitrio de la memoria, la figura 4.3 muestra el "protocolo del iglú". Tanto la entrada
como el mismo iglú son tan pequeños que sólo puede entrar una persona a la vez en el iglú.
Dentro, hay una pizarra en la que se puede escribir un único valor.
El protocolo es el siguiente. Un proceso (P0 o P1) que desee ejecutar su sección crítica entra
primero en el iglú y examina la pizarra. Si su número está escrito en ella, el proceso puede
abandonar el iglú y continuar con su sección crítica. En otro caso, abandona el iglú y se ve
obligado a esperar. De vez en cuando, el proceso vuelve a entrar en el iglú para mirar la
pizarra. Esta operación la repite hasta que se le permite entrar en su sección crítica. Este
procedimiento se denomina espera activa porque un proceso frustrado no puede hacer nada
productivo hasta que obtiene permiso para entrar en su sección crítica. En su lugar, debe
persistir y comprobar periódicamente el iglú; así pues, consume tiempo del procesador (está
activo) mientras espera su oportunidad.
Después de que un proceso haya obtenido acceso a su sección crítica y una vez que termine
con ella, debe volver al iglú y escribir el número del otro proceso en la pizarra.
PROCESO 0 PROCESO 1
… …
… …
while turno != 0 do {nada} while turno != 1 do {nada}
<sección crítica> <sección crítica>
turno := 1 turno := 0
Esta solución garantiza el cumplimiento de la exclusión mutua. Hay dos inconvenientes en esta
solución. Primero, los procesos deben alternarse de forma estricta en el uso de sus secciones
50
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
críticas; así pues, el ritmo de ejecución viene dictado por el más lento. Si P0 usa su sección
crítica sólo una vez por hora, pero P1 quiere usarla con una tasa de 1000 veces por hora, P1
está obligado a adoptar el ritmo de P0. Un problema mucho más serio es que si un proceso
falla (por ejemplo, se lo come un oso polar en su camino hacia el iglú), el otro proceso se
bloquea permanentemente. Esto es cierto tanto si un proceso falla en su sección crítica como
fuera de ella.
La estructura anterior es la de una corrutina. Las corrutinas se diseñaron para poder pasar el
control de la ejecución de un proceso a otro. Aunque es una técnica de estructuración útil para
un solo proceso, no resulta apropiada para dar soporte al proceso concurrente.
Segundo intento
El problema de la primera tentativa es que se almacenaba el nombre del proceso que podía
entrar en su sección crítica cuando, de hecho, lo que hace falta es tener información del
estado de ambos procesos. En realidad, cada proceso debería tener su propia llave de la
sección crítica para que, si un oso polar elimina a uno de ellos, el otro pueda seguir accediendo
a su sección crítica.
Esta filosofía queda ilustrada en la figura 4.4. Cada proceso tiene ahora su propio iglú y puede
mirar la pizarra del otro, pero no modificarla. Cuando un proceso debe entrar en su sección
crítica, comprueba periódicamente la pizarra del otro hasta que encuentra escrito en ella
"falso", lo que indica que el otro proceso no está en su sección crítica. Entonces, se dirige
rápidamente hacia su propio iglú, entra y escribe "cierto" en la pizarra. El proceso puede ahora
continuar con su sección crítica. Cuando deja su sección crítica, cambia su pizarra para que
ponga "falso".
Que está inicializada con falso. El programa para los dos procesos es:
PROCESO 0 PROCESO 1
while señal[1] do {nada}; while señal[0] do {nada};
señal[0] := cierto; señal[1] := cierto;
<sección crítica> <sección crítica>
señal[0] := falso; señal[1] := falso;
Ahora, si uno de los procesos falla fuera de la sección crítica, incluyendo el código para dar
valor a las señales, el otro proceso no se queda bloqueado. De hecho, el otro proceso puede
entrar en su sección crítica tantas veces como quiera, porque la señal del otro proceso está
51
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
siempre puesta a falso. Sin embargo, si un proceso falla en su sección crítica, el otro proceso
está bloqueado permanentemente.
En realidad, esta solución es, si acaso, peor que el primer intento, pues no siempre se garantiza
la exclusión mutua. Considérese la siguiente secuencia:
Puesto que ambos procesos están en sus secciones críticas, el programa es incorrecto. El
problema está en que la solución propuesta no es independiente de la velocidad de ejecución
relativa de los procesos.
Tercer intento
El segundo intento falla porque un proceso puede cambiar su estado después de que el otro
proceso lo ha comprobado pero antes de que pueda entrar en su sección crítica. Quizá se
pueda arreglar este problema con un simple intercambio de dos líneas:
PROCESO 0 PROCESO 1
… …
… …
señal[0] := cierto; señal[1] := cierto;
while señal[1] do {nada}; while señal[0] do {nada};
<sección crítica> <sección crítica>
señal[0] := falso; señal[1] := falso;
Como antes, si un proceso falla dentro de la sección crítica, incluyendo el código para dar valor
a las señales que controlan el acceso a la sección crítica, el otro proceso se bloquea y si un
proceso falla fuera de su sección crítica, el otro proceso no se bloquea.
Esta solución garantiza la exclusión mutua, pero origina un problema más. Si ambos procesos
ponen sus señales a "cierto" antes de que ambos hayan ejecutado la sentencia while, cada uno
pensará que el otro ha entrado en su sección crítica. El resultado es un interbloqueo.
Cuarto intento
En el tercer intento, un proceso fijaba su estado sin conocer el estado del otro. El interbloqueo
se produce porque cada proceso puede insistir en su derecho para entrar en la sección crítica;
no hay opción para volver atrás desde esta situación. Se puede intentar arreglar esto haciendo
que los procesos sean más educados: Deben activar su señal para indicar que de¬sean entrar
en la sección crítica, pero deben estar listos para desactivar la señal y ceder la preferencia al
otro proceso:
52
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
PROCESO 0 PROCESO 1
… …
… …
señal[0] := cierto; señal[1] := cierto;
while señal[1] while señal[0]
begin begin
señal[0] := falso; señal[1] := falso;
<espera cierto tiempo> <espera cierto tiempo>
señal[0] := cierto señal[1] := cierto
end; end;
<sección crítica> <sección crítica>
señal[0] := falso; señal[1] := falso;
Esta solución se aproxima a la correcta pero todavía es defectuosa. La exclusión mutua aún
está garantizada con un razonamiento similar al seguido en el estudio del tercer intento. Sin
embargo, considérese la siguiente secuencia de sucesos:
53
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Es posible describir esta solución en términos de iglúes fijándose en la figura 4.5. Ahora hay un
iglú "árbitro" con una pizarra llamada "turno". Cuando P0 quiere entrar en su sección crítica,
pone su señal a "cierto". A continuación, va y mira la señal de P1. Si ésta está puesta a falso, P0
puede entrar inmediatamente en su sección crítica. En otro caso, P0 va a consultar al árbitro. Si
encuentra el turno = 0, sabe que es momento de insistir y comprueba periódicamente el iglú
de P1. Este otro se percatará en algún momento de que es momento de ceder y escribirá
"falso" en su pizarra, permitiendo continuar a P0. Después de que P0 haya ejecutado su
sección crítica, pone su señal a "falso" para liberar la sección crítica y pone turno a 1 para
traspasar el derecho de insistir a P1.
La figura 4.6 ofrece una especificación del algoritmo de Dekker. La demostración se deja como
ejercicio:
Algoritmo de Peterson
El algoritmo de Dekker resuelve el problema de la exclusión mutua pero con un programa
complejo, difícil de seguir y cuya corrección es difícil de demostrar. Peterson [PETE81] ha
desarrollado una solución simple y elegante. Como antes, la variable global señal indica la
54
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
posición de cada proceso con respecto a la exclusión mutua y la variable global turno resuelve
los conflictos de simultaneidad. El algoritmo se expone en la figura 4.7.
Se puede demostrar fácilmente que se cumple la exclusión mutua. Considérese el proceso P0.
Una vez que ha puesto señal[0] a cierto, P1 no puede entrar en su sección crítica. Si P1 está
aún en su sección crítica, señal[l] = cierto y P0 está bloqueado para entrar en su sección crítica.
Por otro lado, se impide el bloqueo mutuo. Supóngase que P0 está bloqueado en su
bucle while. Esto significa que señal[1] es cierto y turno = 1. P0 puede entrar en su sección
crítica cuando señal[l] se ponga a falso o cuando turno se ponga a 0. Considérense ahora los
siguientes casos exhaustivos:
Así pues, se tiene una solución simple al problema de la exclusión mutua para dos procesos. Es
más, el algoritmo de Peterson se puede generalizar fácilmente al caso de n procesos [HOFR90].
Crea una clase para gestionar el saldo de una Cuenta. Debe tener métodos para obtener el
saldo actual, hacer un ingreso (se incrementa al saldo), hacer un reintegro (se le resta al saldo),
controlar si hay algún error, por ejemplo, si se hace un reintegro y no hay saldo; o si se hace un
55
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
ingreso y el saldo supera el máximo; mostrar mensajes con los movimientos que se realicen. Si
ocurre alguno de los errores anteriores finaliza el proceso. La cuenta recibe en su constructor
el saldo actual y el valor máximo que puede tener. Los métodos de ingreso y reintegro deben
definirse como synchronized.
Crea después la clase Persona que extienda Thread y que realice en su método run() ingresos
y/o reintegros de forma aleatoria y con algún sleep(int tiempo) en medio; hasta que ocurra
alguno de los errores nombrados anteriormente.
• Para simular clientes muy activos, que van muchas veces al banco, y clientes
tranquilos, genera otro número aleatorio para el tiempo que transcurrirá entre dos
movimientos, TIEMPOMIN y TIEMPOMAX.
Implementar la solución usando los algoritmos de Dekker o Peterson (mínimo para dos
clientes).
ARCHIVO CUENTA.JAVA
package CompruebaTuAprendizaje4;
import java.util.Vector;
// Propiedades
private boolean[] bandera = new boolean[2];
private int turno = 0;
private int saldoActual;
private int saldoMaximo;
// Constructor
56
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
// Metodos
public synchronized void ingresar(int cantidad, int turno, Persona
ultimoCliente){
bandera[turno] = true;
if (turno == 0)
this.turno = 1;
else
this.turno = 0;
if (turno == 0){
while(bandera[1] && turno == 1){
try{
Thread.sleep(300);
}
catch(InterruptedException e){}
} // Fin While
// SECCION CRITICA
seccionCriticaIngreso(cantidad, ultimoCliente);
} // Fin if Thread 0
if (turno == 1){
while(bandera[0] && turno == 0){
try{
Thread.sleep(300);
}
catch(InterruptedException e){}
} // Fin While
// SECCION CRITICA
seccionCriticaIngreso(cantidad, ultimoCliente);
} // Fin if Thread 1
bandera[turno] = false;
notifyAll();
} // Fin ingresar
if (turno == 0)
this.turno = 1;
else
this.turno = 0;
if (turno == 0){
while(bandera[1] && turno == 1){
try{
Thread.sleep(300);
}
catch(InterruptedException e){}
57
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
} // Fin While
// SECCION CRITICA
seccionCriticaRetirada(cantidad, ultimoCliente);
} // Fin if Thread 0
if (turno == 1){
while(bandera[0] && turno == 0){
try{
Thread.sleep(300);
}
catch(InterruptedException e){}
} // Fin While
// SECCION CRITICA
seccionCriticaRetirada(cantidad, ultimoCliente);
} // Fin if Thread 1
bandera[turno] = false;
notifyAll();
} // Fin retirar
58
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
ARCHIVO PERSONA.JAVA
package CompruebaTuAprendizaje4;
// Propiedades
private String nombre;
private int numCliente;
private Cuenta cuenta;
// Constructor
public Persona(String nombre, int numCliente, Cuenta cuenta){
this.nombre = nombre;
this.numCliente = numCliente;
this.cuenta = cuenta;
}
// Métodos
public void run(){
while(true){
cuenta.ingresar(generarCifra(), numCliente, this);
try{
sleep(300);
}
catch(InterruptedException e){}
ARCHIVO MAIN.JAVA
package CompruebaTuAprendizaje4;
import java.util.Vector;
59
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}
}
}
60
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Programación en red
TCP/IP
Nivel de Transporte
TCP: Protocolo basado en la conexión, garantiza que los datos enviados desde un extremo de la
conexión llegan al otro extremo y en el mismo orden en que fueron enviados. De lo contrario, se
notifica un error.
UDP: No está basado en la conexión como TCP. Envía paquetes de datos independientes,
denominados datagramas, de una aplicación a otra; el orden de entrega no es importante y no se
garantiza la recepción de los paquetes enviados.
Paquete java.net
Proporciona las clases para la implementación de aplicaciones de red. Se pueden dividir en dos
secciones:
Clase InetAddress
Es la abstracción que representa una dirección IP (Internet Protocol).
Tiene dos subclases: Inet4Address para direcciones IPV4 en Inet6Address para direcciones
IPv6; pero en la mayoría de los casos no es necesario recurrir a ellas.
61
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Ejemplo:
Archivo TestInetAddress.java
import java.net.*;
System.out.println("==========================================="
);
System.out.println("SALIDA PARA LOCALHOST: ");
try {
//LOCALHOST
dir = InetAddress.getByName("PC-ProfeB02");
pruebaMetodos(dir);
//URL www.google.es
System.out.println("==========================================")
;
System.out.println("SALIDA PARA UNA URL:");
dir = InetAddress.getByName("www.google.es");
pruebaMetodos(dir);
System.out.println("\t\t"+direcciones[i].toString());
System.out.println("==========================================")
;
} catch (UnknownHostException e1) {
e1.printStackTrace();
}
62
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}// main
}//fin de pruebaMetodos
}//fin de TestInetAddress
Clase URL
Represneta un puntero a un recurso en la Web. Un recurso puede ser algo tan simple como un
fichero o un directorio, o puede ser una referencia a un objeto más complicado, como una
consulta a una base de datos o a un motor de búsqueda.
En general una URL se divide en varias partes. Por ejemplo en la siguiente URL:
63
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Constructores
Algunos métodos
Ejemplos:
Archivo Ejemplo1URL.java
import java.net.*;
URL url;
try {
System.out.println("Constructor simple para una
URL:");
url = new URL("http://docs.oracle.com/");
Visualizar(url);
64
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
System.out.println("============================================
==");
}// EjemplolURL
Archivo Ejemplo2URL.java
import java.net.*;
import java.io.*;
65
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
URL url=null;
try {
url = new URL("http://www.elaltozano.es");
} catch (MalformedURLException e) {
e.printStackTrace();
}
BufferedReader in;
try {
InputStream inputstream = url.openStream();
in = new BufferedReader(new
InputStreamReader(inputstream));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}//Fin de Ejemplo2URL
Clase URLConnection
Es una clase abstracta que contiene métodos que permiten la comunicación entre aplicaciones y
una URL.
Para conseguir un objeto de este tipo se invoca al método openConnection(), con ello
obtenemos una conexión al objeto URL referenciado.
Las instancias de esta clase se pueden utilizar tanto para leer como para escribir al recurso
referenciado por la URL.
Algunos métodos
66
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Ejemplo
Archivo Ejemplo1urlCon.java
import java.net.*;
import java.io.*;
URL url=null;
URLConnection urlCon=null;
try {
url = new URL("http://www.elaltozano.es");
urlCon= url.openConnection();
BufferedReader in;
InputStream inputStream = urlCon.getInputStream();
in = new BufferedReader(new
InputStreamReader(inputStream));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
catch (MalformedURLException e) {e.printStackTrace();}
catch (IOException e) {e.printStackTrace();
}
}//fin de main
}//Fin de Ejemplo1urlCon
67
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Archivo index.html
<html>
<body>
<form action="vernombre.php" method="post" >
<p>Escribe tu nombre:
<input name="nombre" type="text" size="15">
</p>
<p>Escribe tus apellidos:
<input name="apellidos" type="text" size="15">
</p>
<input type="submit" name="ver" value="Ver">
</form>
</body>
</html>
Archivo vernombre.php
<?php
$nom=$_POST[nombre];
$ape=$_POST[apellidos];
echo "El nombre recibido es: $nom, y ";
echo "los apellidos son: $ape ";
?>
Desde Java, usando la clase URLConnection, podemos interactuar con scripts del lado del
servidor y podemos enviar valores a los campos del script sin necesidad de abrir un formulario
HTML.
En el siguiente ejemplo se puede ver esta interacción con el script del lado servidor.
Archivo Ejemplo2urlCon.java
import java.io.*;
import java.net.*;
try {
68
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
//ESCRIBIR EN LA URL
PrintWriter output = new PrintWriter
(conexion.getOutputStream());
output.write(cadena);
output.close(); //cerrar flujo
//LEER DE LA URL
BufferedReader reader = new BufferedReader (new
InputStreamReader(conexion.getInputStream()));
String linea;
while ((linea = reader.readLine()) != null) {
System.out.println(linea);
}
reader.close();//cerrar flujo
}//fin de main
}//Ejemplo2urlCon
En el ordenador del profesor PC-ProfeB02 tenemos instalado un servidor web Apache y dentro
de htcdocs tenemos la carpeta DAM2PSP con el script PHP vernombre.php.
Normalmente cuando se pasa información a algún script PHP, este realiza alguna acción y
después envía la información de vuelta por la misma URL. Por tanto, si queremos ver lo que
devuelve será necesario leer desde la URL.
Archivo Ejemplo3urlCon
import java.net.*;
import java.io.*;
import java.util.*;
@SuppressWarnings("rawtypes")
public static void main(String[] args) throws Exception {
String cadena;
URL url = new URL("http://localhost/2014/vernombre.html");
URLConnection conexion = url.openConnection();
System.out.println("Direccion [getURL()]:" +
conexion.getURL());
69
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
System.out.println("============================================
===============");
System.out.println("TODOS LOS CAMPOS DE CABECERA CON
getHeaderFields(): ");
System.out.println("============================================
");
System.out.println("CAMPOS 1 Y 4 DE CABECERA:");
System.out.println("getHeaderField(l)=> "+
conexion.getHeaderField(1));
System.out.println("getHeaderField(4)=> " +
conexion.getHeaderField(4));
System.out.println("============================================
");
System.out.println("CONTENIDO DE [url.getFile()]:"+
url.getFile());
BufferedReader pagina = new BufferedReader(new
InputStreamReader(url.openStream()));
}//Fin de main
}//Fin de Ejemplo3urlCon
}
Nota: Para recorrer una estructura Map podemos usar una estructura Iterator. Para obtener un
iterador sobre el map se invoca a los métodos entrySet() e iterator(). Para mover el iterador
utilizaremos el método next() y para comprobar si ha llegado al final usamos el método
hasNext(). De la estructura recuperaremos los valores mediante getKey(), para la clave y
getValue(), para el valor.
70
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Programación de sockets
Para los procesos receptores de mensajes, su conector debe tener asociado dos campos:
La dirección IP del host en el que la aplicación está corriendo.
El puerto local a través del cual la aplicación se comunica y que identifica el proceso.
Si todo va bien, el servidor acepta la conexión. Una vez aceptada, el servidor obtiene un nuevo
socket sobre un puerto diferente. Esto se debe a que por un lado debe seguir atendiendo las
peticiones de conexión mediante el socket original y por otro debe antender las necesidades del
cliente que se conectó.
71
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
En el lado del cliente, si se acepta la conexión, se crea un socket y el cliente puede utilizarlo
para comunicarse con el servidor. Este socket utiliza un número de puerto diferente al usado
para conectarse al servidor. El cliente y el servidor pueden ahora comunicarse escribiendo y
leyendo por sus respectivos sockets.
Clase ServerSocket
Se utiliza para implementar el extremo de la conexión que corresponde al servidor, donde se
crea un conector en el puerto del servidor que escucha las peticiones de conexión de los clientes.
Constructores
Ejemplo
Archivo SocketServidor.java
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
System.out.println("Escuchando en " +
Servidor.getLocalPort());
72
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}//fin de main
}//fin de SocketServidor
Clase Socket
Implementa un extremo de la conexión TCP.
Constructores
Ejemplo
Archivo SocketCliente.java
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
73
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
// ABRIR SOCKET
Socket Cliente = new Socket(Host, Puerto);//conecta
}//fin de main
}//fin de SocketCliente
Cuando el cliente solicita una conexión, el servidor abrirá la conexión al socket con el método
accept().
El cliente establece una conexión con la máquina host a través del uerto especificado mediante
el método Socket(host, port).
74
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
De 1024 a 49151 están reservados para aplicaciones concretas (por ejemplo el 3306 lo usa
MySQL, el 1521 Oracle).
Ejemplo.
Archivo Ejemplo1Servidor.java
import java.io.*;
import java.net.*;
}// main
75
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Archivo Ejemplo1Cliente.java
import java.io.*;
import java.net.*;
Cuando un cliente se conecta, el método accept() devuelve un objeto Socket, éste se usará para
crear un hilo cuya misión es atender a este cliente.
Después se vuelve a invocar a accept() para esperar a un nuevo cliente; habitualmente la espera
de conexiones se hace dentro de un bucle infinito.
76
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Archivo Servidor.java
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
ServerSocket servidor;
servidor = new ServerSocket(6000);
System.out.println("Servidor iniciado...");
while (true) {
Socket cliente = new Socket();
cliente=servidor.accept();//esperando cliente
HiloServidor hilo = new HiloServidor(cliente);
hilo.start(); //Se atiende al cliente
No es necesario establecer una “conexión” entre cliente y servidor, como en el caso de TCP.
77
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Clase DatagramPacket
Constructores
Clase DatagramSocket
Constructores
78
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Podemos considerar servidor al que espera un mensaje y responde; y cliente al que inicia la
comunicación.
Tanto uno como otro si desean ponerse en contacto necesitan saber en qué ordenador y en qué
puerto está escuchando el otro.
79
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Ejemplo
Archivo ServidorUDP.java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//ESPERANDO DATAGRAMA
System.out.println("Esperando Datagrama ................");
DatagramPacket recibo = new DatagramPacket(bufer,
bufer.length);
socket.receive(recibo);//recibo datagrama
int bytesRec = recibo.getLength();//obtengo numero de bytes
String paquete= new String(recibo.getData());//obtengo
String
//VISUALIZO INFORMACIÓN
System.out.println("Número de Bytes recibidos: " +
bytesRec);
System.out.println("Contenido del Paquete : " +
paquete.trim());
System.out.println("Puerto origen del mensaje: " +
recibo.getPort());
System.out.println("IP de origen : " +
recibo.getAddress().getHostAddress());
System.out.println("Puerto destino del mensaje:" +
socket.getLocalPort());
80
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}//Fin de main
Archivo ClienteUDP.java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//ENVIO DATAGRAMA
socket.send(envio);
socket.close(); //cierro el socket
}//Fin de main
}//Fin de ClienteUDP
El programa cliente envía un mensaje al servidor (máquina destino, en este caso es la máquina
local, localhost) al puero 12345 por el que espera peticiones. Visualiza el nombre del host de
destino y la dirección IP. También visualiza el puerto local del socket y el puerto al que envía el
mensaje.
81
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Ejemplo 2
Archivo ClienteUDP2.java
import java.io.*;
import java.net.*;
//cerrar socket
clientSocket.close();
}//Fin de main
}//Fin de ClienteUDP2
El programa cliente envía un texto tecleado en su entrada estándar al servidor (en un pueto
pactado), el servidor lee el datagrama y devuelve al cliente el texto en mayúscula. El programa
82
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
cliente recibe un datagrama del servidor y muestra información del mismo en pantalla (IP,
puerto del servidor y el texto en mayúscula).
Archivo ServidorUDP2.java
import java.io.*;
import java.net.*;
while(true) {
System.out.println ("Esperando datagrama.....");
//RECIBO DATAGRAMA
recibidos = new byte[1024];
DatagramPacket paqRecibido = new DatagramPacket
(recibidos, recibidos.length);
serverSocket.receive(paqRecibido);
cadena = new String(paqRecibido.getData());
//DIRECCION ORIGEN
InetAddress IPOrigen = paqRecibido.getAddress();
int puerto = paqRecibido.getPort();
System.out.println ("\tOrigen: " + IPOrigen + ":" +
puerto);
System.out.println ("\tMensaje recibido: " +
cadena.trim());
//Para terminar
if(cadena.trim().equals("*")) break;
}//Fin de while
serverSocket.close();
System.out.println ("Socket cerrado...");
}//Fin de main
}//Fin de ServidorUDP2
83
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
MulticastSocket
La clase MulticastSocket es útil para enviar paquetes a múltiples destinos simultáneamente.
Para poder recibir estos paquetes es necesario establecer un grupo multicast, que es un grupo de
direcciones IP que comparten el mismo número de puerto.
Cuando se envía un mensaje a un grupo de multicast, todos los que pertenezcan a ese grupo
recibirán el mensaje.
Grupo multicast
Un grupo multicast sse especifica mediante una dirección IP de clase D y un número de puerto
UDP estándar.
Las direcciones desde la 224.0.0.0 a la 239.255.255.255 están destinadas para ser direcciones de
multicast.
Constructores
84
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
ms.send(paquete);
Se cierra el socket
ms.close();
Ejemplo
En este ejemplo tenemos un servidor multicast que lee datos por teclado y los envía a todos
los clientes que pertenezcan al grupo multicast, el proceso terminará cuando se introduzca un
asterisco.
El programa cliente visualiza el paquete que recibe el servidor, su proceso finaliza cuando
recibe un asterisco.
Archivo ServidorMC.java
import java.io.*;
import java.net.*;
while(!cadena.trim().equals("*")) {
85
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
// ENVIANDO AL GRUPO
DatagramPacket paquete = new DatagramPacket
(cadena.getBytes(), cadena.length(), grupo, Puerto);
ms.send (paquete);
}//Fin de while
//cierro socket
ms.close ();
System.out.println ("Socket cerrado...");
}//Fin de main
}//Fin de ServidorMC
Archivo ClienteMC.java
import java.io.*;
import java.net.*;
while(!msg.trim().equals("*")) {
}//Fin de while
//cierra socket
ms.close();
System.out.println("Socket cerrado...");
}//Fin de main
}//Fin de ClienteMC
86
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Utilizaremos los métodos readObject() para leer el objeto del stream y writeObject() para
escribir el objeto al stream.
Serialización
Para que un programa java pueda convertir un objeto en un montón de bytes y pueda luego
recuperarlo, el objeto necesita ser Serializable. Al poder convertir el objeto a bytes, ese objeto
se puede enviar a través de red, guardarlo en un fichero, y después reconstruirlo al otro lado
de la red, leerlo del fichero…
Para que un objeto sea serializable basta con que implemente la interfaz Serialzable. Como la
interfaz Serializable no tiene métodos, es muy sencillo implementarla, basta con un
implements Serializable y nada más.
Si dentro de la clase hay atributos que son otras clases, éstos a su vez también deben ser
Serializables. Con los tipos de java (String, Integer, etc.) no hay problema porque lo son. Si
ponemos como atributo nuestras propias clases, éstas a su vez deben implementar
Serializable.
Ejemplo
En el siguiente ejemplo vamos a ver como el programa servidor crea un objeto Persona,
dándole valores y se lo envía al programa cliente, el programa cliente realiza los cambios
oportunos en el objeto y se lo devuelve modificado al servidor.
ARCHIVO SERVIDOROBJETO.JAVA
import java.io.*;
import java.net.*;
87
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
//enviando objeto
System.out.println("Envio: " + per.getNombre() +"*"+
per.getEdad());
ARCHIVO CLIENTEOBJETO.JAVA
import java.io.*;
import java.net.*;
//recibo objeto
System.out.println("Recibo: " + dato.getNombre() + "*" +
dato.getEdad());
//Modifico el objeto
dato.setNombre("Juan Ramos");
dato.setEdad(22);
88
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
// Se envía el objeto
perSal.writeObject(dato);
System.out.println("Envio: " + dato.getNombre() + "*" +
dato.getEdad());
ARCHIVO PERSONA.JAVA
import java.io.Serializable;
@SuppressWarnings("serial")
public class Persona implements Serializable {
String nombre;
int edad;
// Cerrar stream
out.close();
89
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
// Objeto en bytes
byte[] bytes = bs.toByteArray();
Para convertir los bytes recibidos por el datagrama en un objeto Persona escribimos:
// Recibo datagrama
byte[] recibidos = new byte[1024];
DatagramPacket paqRecibido = new DatagramPacket(recibidos,
recibidos.length);
socket.receive(paqRecibido); // recibo el datagrama
// Obtengo objeto
Persona persona = (Persona)in.readObject();
RMI
RMI proporciona el mecanismo por el que se comunican y se pasan información del cliente al
servidor y viceversa. Cuando es una aplicación algunas veces nos referimos a ella como
Aplicación de Objetos Distribuidos.
Objetos distribuidos
Elementos principales:
Interfaces remotas
Objetos remotos
Objetos serializables
Stubs
Servicio de nombres
Interfaces remotas. Es una interfaz acordada entre el servidor y el cliente. Un método que el
cliente puede invocar.
Las clases de los parámetros y del resultado han de ser serializables (en Java simplemente una
interfaz) o remotos.
90
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Cada método del interface declara que lanza una java.rmi.RemoteException además de
cualquier excepción específica de la aplicación.
Objetos remotos. Son objetos cuyos mensajes pueden ser invocados remotamente (desde
objetos corriendo en otro proceso. En el caso de Java sería desde otra JVM).
La clase del objeto remoto podría incluir implementaciones de otros interfaces (locales o
remotos) y otros métodos (que sólo estarán disponibles localmente). Si alguna clase local va a
ser utilizada como parámetro o cómo valor de retorno de alguno de esos métodos, también
debe ser implementanda.
Stubs. Actúan como referencias a objetos remotos en el cliente. Es una clase usada por el
cliente en sustitución de la remota.
Cuando un cliente invoca una operación remota que devuelve una referencia a un objeto
remoto, obtiene una instancia del stub correspondiente.
Pasaje de objetos
Cualquiera de estos objetos (parámetros o respuesta) deben ser remotos y si no lo son deben
ser serializables.
Si son remotos:
Estará tipado con una clase que extiende de UnicastRemoteObject.
Se pasan por referencia.
Los objetos remotos se convierten en stubs al pasar del servidor al cliente.
91
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Si no son objetos que se pueden acceder de manera remota (deben al menos ser serializables):
No están tipados con una clase que extienda UnicastRemoteObject (o alguna que
indique que es remoto).
Deben implementar java.io.Serializable (de lo contrario se produce una excepción).
Son pasados por valor.
RMI se ocupa de la serialización de forma transparente para el desarrollador.
Ejemplo RMI
Programa Servidor
Programa cliente
92
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
La clase java.rmi.Naming
Ejecución
Interfaz
package rmi_sample;
import java.rmi.Remote;
import java.rmi.RemoteException;
93
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Servidor
package rmi_sample;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
if(System.getSecurityManager() == null){
System.setSecurityManager(new RMISecurityManager());
}
name = "//localhost/StringerInterface";
try{
robject = new Stringer("Hi\n");
Naming.rebind(name, robject);
System.out.println("Stringer bounded");
}
catch(Exception e){
System.err.println("*******ComputeEngine exception:
*******");
e.getMessage();
}
}
}
Cliente
package rmi_sample;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
94
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
if (System.getSecurityManager() == null){
System.setSecurityManager(new RMISecurityManager());
}
try{
String name = "//" + args[0] + "/StringerInterface";
StringerInterface robject =
(StringerInterface)Naming.lookup(name);
String result = robject.getString();
System.out.println(result);
}
catch(Exception e){
System.err.println("******** Problemas en la
invocación ********" + e.getMessage());
}
}
}
Conclusión
RMI es un sistema que nos permite el intercambio de objetos, el cual se realiza de manera
transparente de un espacio de direcciones a otro, puesto que utiliza una técnica de
serialización. Además nos pemite el llamado de los métodos remotamente, sin tener la
necesidad de tener los métodos localmente.
Debido a que los sistemas necesitan manejo de datos en sistemas distribuidos cuando estos
residen en direcciones de distintos hosts, los métodos de invocación remota son una buena
alternativa para solucionar estas necesidades. RMI es un sistema de programación para la
distribución e intercambio de datos entre distintas aplicaciones existentes en un entorno
distribuido.
Se debe tener en cuenta que es más lento porque los objetos tienen que serializarse y luego
deserializarse, se debe chequear la seguridad, los paquetes tienen que ser ruteados a través de
switches.
Resumen rápido
En RMI intervienen dos progamas java que están ejecutándose en el mismo o distintos
ordenadores.
Uno de ellos, llamado servidor, “publica” algunos de los objetos/clases que tiene instanciadas,
es decir, los pone visibles desde la red, de forma que otros programas java puedan llamar a sus
métodos.
95
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
El otro programa, llamado cliente, localiza en el servidor el objeto publicado y llama a sus
métodos. Estos métodos se ejecutan en el programa servidor y devuelven un resultado (por el
return habitual) que recoge el cliente en su ordenador.
El cliente se queda “colgado” en la llamada hasta que el servidor termina de ejecutar el código
y devuelve un resultado.
a) Se crea una interface (remota) con los métodos que queremos que se publiquen, es
decir, los métodos que queremso que pueda llamar el cliente
• Esta interface debe heredar de la interface Remote.
• Todos los métodos de ésta interface deben lanzar una RemoteException.
b) Se implementa la interface remota creando una clase que herede de
UnicastREmoteObjet con un constructor que lance una RemoteException.
rmic paquete.ObjetoRemoto
e) Se inicia el servicio de registro RMI. Antes de lanzar nuestro servidor, debemos lanzar
un programa llamado rmiregistry que también viene con java (está en
$JAVA_HOME/bin). Este programa es el que realmente sabe todos los objetos que se
publican en este ordenador. La llamada Naming.rebind() en realidad coneca con
rmiregistry en este ordenador y le avisa que ObjetoRemoto debe publicarse al
exterior.
96
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
System.setProperty("java.rmi.server.codebase", "file:/un_path/");
f) Se lanza el servidor
g) En el cliente. Se escribe la clase cliente
InterfaceRemota objetoRemoto =
(InterfaceRemota)Naming.lookup("//IP_del_Servidor/ObjetoRemoto");
System.out.println("2 + 3 = ");
System.out.println(objetoRemoto.suma(2, 3));
97
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Ejercicio de la Hipoteca
1. Como los pagos son mensuales, comenzamos calculando el plazo expresados en meses y el
tipo de interés mensual.
100000 * 0.33333
Cuota = = 605.96
0.33333 −240
100 * (1 − (1 + )
100
Vamos a suponer que realizar estos cálculos es de una gran complejidad computacional por lo
que vamos a desarrollar un programa distribuido mediante RMI. Para ello crearemos dos
programas, un servidor y un cliente. El servidor tendrá un método remoto que sepa calcular
dicha cuota. Un cliente lo invocará pasándole los tres datos iniciales, capital, interés y plazo.
INTERFAZREMOTA.JAVA
package claver.creditos;
import java.rmi.*;
import java.io.Serializable;
/**
* Interface Remota con métodos para llamada en remoto
*/
public interface InterfaceRemota extends Remote{
} // Fin InterfaceRemota
98
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
OBJETOREMOTO.JAVA
package claver.creditos;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
// Métodos
Ahora solo queda desarrollar el Servidor, que será el que esperará que le envíen los datos para
calcular.
SERVIDOR.JAVA
package claver.creditos;
import java.rmi.*;
/**
* Servidor para el ejemplo RMI
* Exporta un método para sumar dos enteros y devuelve resultado
*/
public class Servidor{
99
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Naming.rebind("//localhost/ObjetoRemoto",
objetoRemoto);
}
catch(Exception e){
e.printStackTrace();
}
} // Fin Constructor
// Main
public static void main(String[] args){
new Servidor();
} // Fin Main
} // Fin Servidor
Y el cliente, que mandará los datos para su cálculo y los recibirá una vez finalizados.
CLIENTE.JAVA
package claver.creditos;
import java.rmi.*;
import java.text.*;
/**
* Ejemplo de cliente rmi nocivo, para aprovecharse de un servidor
* sin SecurityManager
*/
public class Cliente{
// Main
public static void main(String[] args){
new Cliente();
} // Fin Main
100
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
} // Fin Cliente
Después de haber programado todas nuestras clases, deberemos compilarlo y comenzaremos
con el montaje del RMI.
La segunda cosa es que si examináis el código, cuando hago el setProperty del System, estoy
colocando los paquetes en dos carpetas.
Lo primero será compilar todos los .java. Abrimos una terminal (Ejecutar cmd) y lo primero
es acceder a la ruta donde están nuestros archivos (sin entrar al paquete, nos quedamos en
src).
cd C:\Users\Inazio\workspace\EjercicioRMI\src
Ahora compilaremos todos los .java con el comando javac, colocando la ruta de los paquetes y
las clases a compilar.
javac claver\creditos\*.java
Ya estarán compilados, así que procederemos a generar el stub con la herramienta rmic.
Lo primero que haremos será situar el CLASSPATH de nuestra consola en la ruta src de nuestro
proyecto (es decir, en la ruta de nuestro proyecto sin contar el paquete) y posteriormente
lanzaremos la herramienta rmic con la ruta de nuestro objeto remoto. Esta ruta será como si
llamásemos a clases de java, es decir, deberemos separarla por puntos.
set CLASSPATH=C:\Users\Inazio\workspace\EjercicioRMI\src
rmic claver.creditos.ObjetoRemoto
En C:\ejercicios\primero\src_cliente
Cliente.class
InterfazRemota.class
ObjetoRemoto_Stub.class
En C:\ejercicios\primero\src_servidor
Servidor.class
ObjetoRemoto.class
101
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
InterfazRemota.class
ObjetoRemoto_Stub.class
Habrá que resetear el CLASSPATH modificado antes a uno vacio y ya lanzar el servicio.
set CLASSPATH=
rmiregistry
Se nos quedará la consola como esperando instrucciones. Bien, es lo que tiene que pasar.
Abrimos una segunda terminal y accedemos a la ruta src_servidor y ejecutamos nuestro
Servidor.
cd C:\ejercicios\primero\src_servidor
java claver.creditos.Servidor
cd C:\ejercicios\primero\src_cliente
java claver.creditos.Cliente
Ahora, el cliente enviará los datos al servidor y este procederá a hacer el cálculo y reenviarlo a
Cliente, que en este caso en concreto lo mostrará por pantalla.
102
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Programación segura
1. Informarse
Una forma de evitar fallos es estudiar y comprender los erroes que otros han cometido
a la hora de desarrollar software.
Internet es el hogar de una gran variedad de foros públicos donde se debaten con
frecuencia problemas de vulnerabilidad de software.
Leer libros y artículos sobre prácticas de codificación segura, así como análisis de los
defectos del software.
Revisar los ficheros de configuración. Es necesario validar los datos, como si se tratase
de una entrada de datos por teclado, ya que pueden ser manipulados por un atacante.
No fiarse de las URLs Web. Muchos diseñadores de aplicaciones web utilizan URLs para
insertar variables y sus valores. El usuario puede alterar la URL directamente dentro de
su navegador por variables de ajuste y/o de sus valores a cualquier configuración que
elija. Si la aplicación Web no realiza una comprobación de los datos puede ser atacada
con éxito.
Cuidado con los contenidos Web. Muchas aplicaciones web insertar variables en
campos HTML ocultos. Tales campos también pueden ser modificados por el usuario
en una sesión de navegador.
Comprobar las cookies web. Los valores pueden ser modificados por el usuario final y
no se debe confiar en ellas.
103
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Establecer valores iniciales válidos para los datos. Es un buen hábito inicializar
correctamente las variables.
3. Reutilización de código siempre que sea posible. Se refiere a la reutilización del software
que ha sido completamente revisado y probado, y ha resistido las pruebas del tiempo y de los
usuarios.
Es recomendable el desarrollo de una lista de cosas que buscar, esta tiene que ser
mantenida y actualizada con los nuevos fallos de programación encontrados.
5. Utilizar listas de control de seguridad. Estas listas pueden ser muy útiles para asegurarse de
que se han cubierto todas las fases durante la ejecución. Por ejemplo:
Este sistema de aplicación requiere una contraseña para que los usuarios puedan
acceder.
Todos los inicios de sesión de usuario son únicos.
Esta aplicación utiliza el sistema de control de acceso basado en roles.
Las contraseñas no se transmiten a través de la red en texto plano.
El cifrado se utiliza para proteger los datos que se transfieren entre servidores y
clientes.
6. Ser amable con los mantenedores. El mantenimiento del código puede ser de vital
importancia para la seguridad del software en el transcurso de su vida útil. Seguiremos estas
prácticas de mantenimiento del código:
Utilizar normas. Con respecto la documentación en línea del código fuente, los
nombres de las variables, etc; para que hagan la vida más fácil para aquellos que
104
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Criptografía
105
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Funciones de una sola vía (o funciones Hash). Practicamente cualquier protocolo las
usa para procesar claves, encadenar una secuencia de eventos, o incluso autenticar
eventos y son esenciales en la autenticación por firmas digitales. Por ejemplo MD5 y
SHA-1
Algoritmos de clave secreta o de criptografía simétrica, como DES, Tripe DES, AES.
Una firma digital está compuesta por una serie de datos asociados a un mensaje, estos datos
nos permiten:
106
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
El emisor genera un hash (resumen) del mensaje mediante una función acordada, a
este hash le llamamos H1.
Este hash es el cifrado con su clave privada. El resultado es lo que se conoce como
firma digital (FD) que se envía adjunta al mensaje.
El emisor envía el mensaje y su FD al receptor, es decir, el mensaje firmado.
107
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Certificados digitales
108
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Un certificado digital es un documento que certifica que una entidad determinada, como
puede ser un usuario, una máquina, un dispositivo de red o un proceso, tiene una clave pública
determinada.
Para certificar estos documentos se acude a las Autoridades de Certificación (AC), que son
entidades que se encargan de emitir y gestionar tales certificados y que tienen una propiedad
muy importante: que se puede confiar en ellas. La forma en la que la AC hace válido el
certificado es firmándolo digitalmente.
Versión
Número de serie. Identificador numérico único
Algoritmo de firma y parámetros, que identifican el algoritmo asimétrico y la función e
una sola vía que se usa.
Emisor del certificado: El nombre X.500 de la AC.
Fechas de inicio y final de validez que determinan el periodo de validez del certificado.
Nombre del propietario de la clave pública.
Identificador del algoritmo que se está utilizando, la clave pública del usuario y otros
parámetros si son necesarios.
La firma digital de la AC, es decir, el resultado de cifrar mediante el algoritmo
asimétrico y la clave privada de la AC, el hash obtenido del docuemento X.509.
109
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Principales aplicaciones
Como obtenerlos
Control de acceso
Componentes
110
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Autenticación. Es cualquier proceso por el cual se verifica que alguien es quien dice
ser. Esto implica generalmente un nombre de usuario y una contraseña, pero puede
incluir cualquier otro método para demostrar la identidad, como una tarjeta
inteligente, exploración de la retina, reconocimento de voz o las huellas dactilares.
Autorización. Es el proceso de determinar si el sujeto, una vez autenticado, tiene
acceso al recurso. La autorización es equivalente a la comprobación de la lista de
invitados a una fiesta.
Algo que se sabe, algo que se conoce, típicamente las contraseñas, es la más extendida
Algo que se tieen, los Access tokens (sistemas de tarjetas)
Algo que se es, medidas que utilizan la biometría (identificación por medio de la vez, la
retina, la huella dactilar, geometría de la mano, etc).
El cargador de clases
El verificador de ficheros de clases
El gestor de seguridad
El cargador de clases
Es el responsable de encontrar y cargar los bytecodes que definen las clases. Cada programa
Java tiene como mínimo tres cargadores:
El cargador de clases bootstrap que carga las clases del sistema (normalmente desde el
fichero JAR rt.jar)
El cargador de clases de extensión que carga una extensión estándar desde el
directorio jre/lib/ext
El cargador de clases de la aplicación que localiza las clases y los ficheros JAR/ZIP de la
ruta de acceso a las clases (según está establecido por la variable de entorno
CLASSPATH o por la opción –classpath de la línea de comandos)
Se encarga de validar los bytecodes. Algunas de las comprobaciones que lleva a cabo son:
El gestor de seguridad
111
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Por defecto no se instala de forma automática ningún gestor de seguridad cuando se ejecuta
una aplicación Java. En el siguiente ejemplo veremos la salida que produce el programa
ejecutándolo sin gestor de seguridad y con gestor de seguridad. El programa muestra los
valores de ciertas propiedades de sistema (usamos el método
System.getProperty(propiedad) para mostrar los valores), la siguiente tabla
describe alguna de las más importantes:
Archivo EJEMPLO1.JAVA
System.setProperty ("java.security.policy",
System.getProperty("user.dir") +
"\\src\\_01SinConGestor\\Politica1.policy");
System.setSecurityManager(new SecurityManager());
for (int i = 0; i < t.length; i++) {
System.out.print("Propiedad:" + t[i]);
try {
String s = System.getProperty(t[i]);
//valor de la propiedad
System.out.println("\t==> " + s);
} catch (Exception e) {
System.err.println("\n\tEXcepción " + e.toString()); }
}//Fin de for
}//Fin de main
}//Fin de Ejemplo1
grant {
permission java.util.PropertyPermission "java.class.path",
"read";
112
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Firmas digitales
Resúmenes de mensajes (hash)
Certificados y validación de certificados
Encriptación (cifrado de bloques, cifrado simétrico y asimétrico)
Generación y gestión de claves y generación segura de números aleatorios
JSSE (Java Secure Extension, Extensión de Sockets Seguros Java). Es un conjunto de paquetes
Java provistos para la comunicación segura en Internet. Implementa una versión Java de los
protocolos SSL y TLS. Incluye funcionalidades como:
Cifrado de datos
Autenticación del servidor
Integridad de mensajes
Autenticación del cliente
113
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
La autorización de usuarios para garantizar que quién lo ejecuta tiene los permisos
necesarios para hacerlo.
El valor por defecto es tener un solo fichero de políticas en todo el sistema y un fchero de
políticas en el directorio home (dado por la variable user.home) del usuario (este tiene un
punto delante).
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy
Entrada grant
Un fichero de políticas especifica qué permisos están disponibles para el código de varias
fuentes.
Contiene una secuencia de entradas grant, cada una de ellas tiene una o más entradas, el
formato básico es el siguiente:
A la derecha de codeBase se indica la ubicación del código base sobre el que se van a definir
los permisos. Su valor es una URL y siempre se debe utiliza la barra diagonal (/) como
separador de directorio incluso para las URL de tipo file en Windows. Por ejemplo: grant
codeBase “file:/C:/somepath/api/”.
Si se omite codeBase entonces los permisos se aplican a todas las fuentes.
114
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
En el parámetro Acción se indica una lista de acciones separadas por comas, por ejemplo read,
write, delete o execute para una clase de permiso java.io.FilePermission; accept,
listen, connect, resolve para una clase de permiso java.netSocketPermission; read,
write para una clase de permiso java.util.PropertyPermission.
Desde la URL:
http://docs.oracle.com/javase/8/docs/technotes/guides/security/p
ermissions.html
Creamos un fichero en una carpeta determinada (en Windows C:\Ficheros) e insertamos una
línea, después lee el contenido del fichero y lo muestra en pantalla. El comportamiento del
programa será diferente si usamos o no el gestor de seguridad y ficheros de políticas.
Archivo EJEMPLO2.JAVA
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
115
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Archivo POLITICA2.POLICY
que permite a los programas localizados en la carpeta indicada (incluido permiso para crear)
ficheros en la ruta C\Ficheros.
Formato grant
“/” (donde “/” es el carácter separador de ficheros) indica un directorio y todos los
ficheros contenidos en ese directorio.
“/-“ indica un directorio y (recursivamente) todos los ficheros y subdirectorios
contenidos en ese directorio.
“<<ALL FILES>>” indica cualquier fichero
También es posible usar propiedades de sistema como user.dir o user.home para dar
privilegios sobre el directorio en el que se está ejecutando el programa o sobre el
directorio por defecto del usuario. Se debe utilizar la propiedad encerrada entre llaves
y con el caracer $ delante. ${propiedad}.
116
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Archivo EJEMPLO3SERVIDOR.JAVA
import java.net.ServerSocket;
import java.net.Socket;
try {
servidor = new ServerSocket(numeroPuerto);
System.out.println("Esperando al cliente.....");
Socket clienteConectado = servidor.accept();
System.out.println("Cliente conectado.");
clienteConectado.close();
System.out.println("Cliente desconectado.");
servidor.close();
} catch (Exception e) { System.err.println("ERROR=> " +
e.toString()); }
Archivo EJEMPLO3.JAVA
import java.net.Socket;
117
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
System.setProperty ("java.security.policy",
System.getProperty("user.dir") +
"\\src\\_03FicherosPoliticasSockets\\Politica4.policy");
System.setSecurityManager(new SecurityManager());
try {
Socket Cliente = new Socket(Host, Puerto);
System.out.println("CLIENTE INICIADO.");
Cliente.close();
System.out.println("CLIENTE FINALIZADO.");
} catch (Exception e) { System.err.println("ERROR=> " +
e.toString()); }
Archivo POLITICA3.POLICY
Archivo POLITICA4.POLICY
El programa cliente se conecta al servidor en el puerto 6000, visualiza unos mensajes y luego
se desconecta.
El fichero de políticas para el cliente, Politica4.policy, representa un permiso que permite a los
programas localizados en la carpeta ${user.dir}/bin/ conectarse al puerto 6000 en localhost.
Herramienta PolicyTool
118
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
El API JCA (Java Cryptography Architecture, incluye la extensión criptográfica de Java JCE –
Java Cryptography Extension) incluída dentro del paquete JDK incluye dos componentes de
software:
El marco que define y apoya los servicios criptográficos para que los proveedores
faciliten implementaciones. Este marco incluye paquetes como
java.security
javax.crypto
javax.crypto.spec
javax.crypto.interfaces
Los proveedores reales, tales como Sun, SunRsaSign, SunJCE, que contienen las
implementaciones criptográficas reales. El proveedore es el encargado de
proporcionar la implementación de uno o varios algoritmos al programador. Los
proveedores de seguridad se definen en el fichero java.security localizo en la carpeta
java.home\lib\security.
Forman una lista de entradas con un número que indican el orden de búsqueda
cuando en los programas no se especifica un proveedor.
security.provider.1=sun.security.provider.Sun
security.provider.2=sun.security.rsa.SunRsaSign
security.provider.3=com.sun.net.ssl.internal.ssl.Provider
security.provider.4=com.sun.crypyo.provider.SunJCE
security.provider.5=sun.security.jgss.SunProvider
JCA define el concepto de proveedor mediante la clase Provider del paquete java.security. Se
trata de una clase abstracta que debe ser redefinida por clases proveedor específicas.
Tiene métodos para acceder a informaciones sobre las implementaciones de los algoritmos
para la generación, conversión y gestión de claves y la generación de firmas y resúmenes,
como el nombre del proveedor, el número de versión, etc.
119
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Resúmenes de mensajes
Un message digest o resúmen de mensajes (también se le conoce como función hash) es una
marca digital de un bloque de datos.
Ejemplo
Archivo EJEMPLO4.JAVA
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA");
String texto = "Esto es un texto plano.";
120
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}//Fin de main
return hex.toUpperCase();
}//Fin de Ejemplo4
121
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Segundo ejemplo
Archivo EJEMPLO5.JAVA
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
try {
FileOutputStream fileout = new
FileOutputStream("DATOS.DAT");
ObjectOutputStream dataOS = new
ObjectOutputStream(fileout);
MessageDigest md = MessageDigest.getInstance("SHA");
String datos = "En un lugar de la Mancha, "
+ "de cuyo nombre no quiero acordarme, no
ha mucho tiempo "
+ "que vivía un hidalgo de los de lanza
en astillero, "
+ "adarga antigua, rocín flaco y galgo
corredor.";
byte dataBytes[] = datos.getBytes();
md.update(dataBytes) ;// TEXTQ A RESUMIR
byte resumen[] = md.digest(); // SE CALCULA EL
RESUMEN
dataOS.writeObject(datos); //se escriben los datos
dataOS.writeObject(resumen);//Se escribe el resumen
dataOS.close();
fileout.close();
} catch (IOException e) { e.printStackTrace();
} catch (NoSuchAlgorithmException e) { e.printStackTrace();
}
}//Fin de main
}//Fin de Ejemplo5
Tercer ejemplo
Archivo EJEMPLO6.JAVA
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.security.MessageDigest;
122
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
try {
FileInputStream fileout = new
FileInputStream("DATOS.DAT");
ObjectInputStream dataOS = new
ObjectInputStream(fileout);
Object o = dataOS.readObject();
MessageDigest md = MessageDigest.getInstance("SHA");
//Se calcula el resumen del String leído del fichero
md.update(datos.getBytes());// TEXTO A RESUMIR
byte resumenActual[] = md.digest(); // SE CALCULA EL
RESUMEN
}//Fin de main
}//Fin de Ejemplo6
Al recuperar los datos del fichero primero necesitamos leer el String y luego el resumen, a
continuación hemos de calcular de nuevo el resumen con el String leído y comparar este
resúmen con el leído del fichero.
Se puede decir que el fichero no es correcto si el texto que se lee no produce la misma salida
que el resúmen guardado.
Pero alguien puede cambiar el texto y el resumen, y no podemos estar seguros de que el texto
sea el que debería ser.
123
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Clase KeyPairGenerator
En algunos casos, el par de claves (clave pública y clave privada) están disponibles en ficheros.
En ese caso, el programa puede importar y utilizar la clave privada para firmar.
Clase KeyPair
PrivateKey y PublicKey son interfaces que agrupan todas las interfaces de clave privada y
pública respectivamente.
Clase Signature
124
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Un objeto de esta clase se puede utilizar para generar y verificar firmas digitales.
Al especifiar el nombre del algoritmo de firma, también se debe incluir el nombre del
algoritmo de resumen de mensajes utilizado por el algoritmo de firma. SHA1withDSA es una
forma de especificar el algoritmo de firma DSA, usando el algoritmo de resumen SHA-1.
MD5withRSA significa algoritmo de resumen MD5 con algortmo de firma RSA.
Existen tres fases en el uso de un objeto Signature ya sea para firmar o verificar los datos:
inicialización (ya sea con clave pública initVerify() o clave privada initSign()), actualización
(update()) y firma (sign()) o verificación (verify()).
Ejemplo
Archivo EJEMPLO7.JAVA
import java.security.*;
try {
KeyPairGenerator keyGen =
KeyPairGenerator.getInstance("DSA");
//SE INICIALIZA EL GENERADOR DE CLAVES
SecureRandom numero =
SecureRandom.getInstance("SHA1PRNG");
keyGen.initialize (1024, numero);
125
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}//Fin de main
}//Fin de Ejemplo7
Para almacenar la clave privada en disco es necesario codificarla en formato PKCS8 usando la
clase PKCS8EncodedKeySpec.
PKCS8EncodedKeySpec pk8Spec =
new PKCS8EncodedKeySpec(clavepriv.getEncoded());
//Escribir a fichero binario la clave privada
FileOutputStream outpriv =
new FileOutputStream("Clave.privada");
outpriv.write(pk8Spec.getEncoded());
outpriv.close();
Para almacenar la clave pública en disco es necesario codificarla en formato X.509 usando la
clase X509EncodedKeySpec.
126
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
X509EncodedKeySpec pkX509 =
new X509EncodedKeySpec(clavepub.getEncoded());
//Escribir a fichero binario la clave pública
FileOutputStream outpub =
new FileOutputStream("Clave.publica");
outpub.write(pkX509.getEncoded());
outpub.close();
Clase KeyFactory. Para recuperar las claves de los ficheros que proporciona métodos para
convertir claves de formato criptográfico (PKCS8, X.509) a especificaciones de claves y
viceversa. Su constructor y alguno de sus métodos:
Archivo EJEMPLO8.JAVA
import java.io.*;
import java.security.*;
import java.security.spec.*;
try {
// LECTURA DEL FICHERO DE CLAVE PRIVADA
FileInputStream inpriv = new
FileInputStream("Clave.privada");
byte[] bufferPriv = new byte[inpriv.available()];
inpriv.read(bufferPriv);// lectura de bytes
inpriv.close();
127
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}//Fin de main
}//Fin de Ejemplo8
Genera la firma del fichero DATOS.DAT a partir de la clave privada almacenada en el fichero
Clave.privada. La firma se almacenará en el fichero DATOS.FIRMA.
Archivo EJEMPLO9.JAVA
import java.io.*;
import java.security.*;
import java.security.spec.*;
try {
//LECTURA DE LA CLAVE PUBLICA DEL FICHERO
FileInputStream inpub = new
FileInputStream("Clave.publica");
byte[] bufferPub = new byte[inpub.available()];
inpub.read(bufferPub);// lectura de bytes
inpub.close();
128
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}//Fin de main
}//Fin de Ejemplo9
129
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Nos pedirá contraseña para el almacén de claves y para la clave privada del par de claves
generado.
El certificado generado tiene una validez de 90 días a no ser que se especifique la opción -
validity en keytool.
Los certificados autofirmados son útiles para desarrollar y probar una aplicación.
La aplicación está firmada con un certificado que no es de confianza, por tanto, al ejecutarla nos
preguntará antes si queremos ejecutarla.
Se recomienda no importar en un almacén de claves un certificado en el que no se confíe
plenamente.
4. Con keytool –export exportar el certificado de clave pública para que el receptor
autentique la firma del emisor.
Clase Cipher
130
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Para crear un objeto Cipher se llama al método getInstance() pasando como argumento
el algoritmo y opcionalmente, el nombre de un proveedor.
AES/CBC/NoPadding (128)
AES/CBC/PKCS5Padding (128)
AES/ECB/NoPadding (128)
AES/ECB/PKCS5Padding (128)
DES/CBC/NoPadding (56)
DES/CBC/PKCS5Padding (56)
DES/ECB/NoPadding (56)
DES/ECB/PKCS5Padding (56)
DESede/CBC/NoPadding (168)
DESede/CBC/PKCS5Padding (168)
DESede/ECB/NoPadding (168)
DESede/ECB/PKCS5Padding (168)
RSA/ECB/PKCS1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)
ECB (Electronic Cookbook Mode). Los mensajes se dividen en bloques y cada uno de ellos es
cifrado por separado por separado utilizando la misma clave K. A bloques de texto plano o
131
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
claro idénticos les corresponden bloques idénticos de texto cifrado, de anera que se
pueden reconocer estos patrones. De ahí que no sea recomendable.
CBC (Cipher Block Chaining), a cada bloque de texto plano se le aplica la operación XOR con
el bloque cifrado anterior antes de ser cifrado. De esta forma, cada bloque de texto cifrado
depende de todo el texto en claro procesado hasta este punto. Para hacer cada men saje
único se utilza asimismo un vector de inicialización.
Clase KeyGenerator
Archivo EJEMPLO10.JAVA
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
132
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
try {
/*
//Muchos modos de algoritmo (por ejemplo CBC)
requieren un vector de inicialización que se especifica cuando se
inicializa
//el objeto Cipher en modo desencriptación. En estos
casos, se debe pasar al método init() el vector de inicialización.
//La clase IvParameterSpec se usa para hacer esto en
el cifrado DES.
KeyGenerator kg = KeyGenerator.getInstance("DES");
Cipher c =
Cipher.getInstance("DES/CBC/PKCS5Padding");
Key clave = kg.generateKey();
133
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
} catch (InvalidKeyException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
// } catch (InvalidAlgorithmParameterException e) {
}
import java.io.*;
import java.security.*;
import javax.crypto.*;
try {
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128);
//genera clave secreta
SecretKey clave = kg.generateKey();
ObjectOutputStream out = new ObjectOutputStream(new
FileOutputStream("Clave.secreta"));
out.writeObject(clave);
out.close();
/*
//Para recuperar la clave secreta del fichero
ObjectInputStream in = new ObjectInputStream(new
FileInputStream("Clave.secreta"));
Key secreta = (Key) in.readObject();
in.close();
*/
}//Fin de main
}//Fin de AlmacenaClaveSecreta
134
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Conversación encriptada
Criptografía híbrida
Archivo EJEMPLO12.JAVA
import java.security.*;
import javax.crypto.*;
try {
//SE CREA EL PAR DE CLAVES PÚBLICA Y PRIVADA
KeyPairGenerator keyGen =
KeyPairGenerator.getInstance("RSA");
keyGen.initialize (1024);
KeyPair par = keyGen.generateKeyPair();
PrivateKey clavepriv = par.getPrivate();
PublicKey clavepub = par.getPublic();
135
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init (128);
SecretKey clavesecreta = kg.generateKey();
}//Fin de main
}//Fin de Ejemplo12
Clave de sesión
Es un término medio entre el cifrado simétrico y asimétrico que permite combinar las dos
técnicas. Consiste en generar una clave de sesión K y cifrarla usando la clave pública del
receptor. El receptor descifra la clave de sesión usando su clave privada. El emisor y el receptor
136
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
comparten una clave que solo ellos conocen y pueden cifrar sus comunicaciones usando la
misma clave de sesión.
Archivo EJEMPLO13CIFRA.JAVA
import java.io.*;
import java.security.*;
import javax.crypto.*;
try {
//RECUPERAMOS CLAVE SECRETA DEL FICHERO
ObjectInputStream oin = new ObjectInputStream( new
FileInputStream("Clave.secreta"));
Key clavesecreta = (Key) oin.readObject();
oin.close();
137
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Cipher c =
Cipher.getInstance("AES/ECB/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, clavesecreta);
//FICHERO A CIFRAR
FileInputStream filein = new
FileInputStream("FICHERO.pdf");
//OBJETO CipherOutputStream donde se almacena el
fichero cifrado
CipherOutputStream out = new CipherOutputStream( new
FileOutputStream("FicheroPDF.Cifrado"), c);
int tambloque = c.getBlockSize();//tamaño de bloque
objeto Cipher
byte[] bytes = new byte[tambloque];//bloque de bytes
out.flush();
out.close();
filein.close();
System.out.println("Fichero cifrado con clave
secreta.");
}//Fin de main
}// Fin de Ejemplo13Cifra
Utiliza la clave secreta almacenada en un fichero llamado para cifrar un documento PDF de
nombre Fichero.pdf.
Archivo EJEMPLO13DESCIFRA.JAVA
import java.io.*;
import java.security.*;
import javax.crypto.*;
try {
//RECUPERAMOS CLAVE SECRETA DEL FICHERO
ObjectInputStream oin = new ObjectInputStream(new
FileInputStream("Clave.secreta"));
Key clavesecreta = (Key) oin.readObject();
oin.close();
138
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}//Fin de main
}//Fin de Ejemplo13Descifra
JSSE (Java Secure Socket Extension) es un conjunto de paquetes que permiten el desarrollo de
aplicaciones seguras en Internet. Proporciona un marco y una implementación para la versión
Java de los protocolos SSL y TSL e incluye funcionalidad de encriptación de datos,
autenticación de servidores, integridad de mensajes y autenticación de clientes.
Con JSSE, los desarrolladores pueden ofrecer intercambio seguro de datos entre un cliente y
un servidor que ejecuta un protocolo de aplicación, tales como HTTP, Telnet o FTP, a través de
TCP/IP.
Las clases de JSSE se encuentran en los paquetes javax.net y javax.net.ssl.
SSL
Las clas SSLSocket y SSLServerSocket representan sockets seguros y son derivadas de las ya
familiares Socket y ServerSocket respectivamente.
139
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Archivo SERVIDORSSL.JAVA
import java.io.*;
import javax.net.ssl.*;
//System.setProperty("javax.net.ssl.keyStore",
System.getProperty("user.dir") + "\\AlmacenSSL");
//System.setProperty("javax.net.ssl.keyStorePassword",
"1234567");
140
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Crea una conexión sobre un socket servidor seguro y que atenderá hasta cuatro conexiones de
clientes que se identificarán con un certificado válido. El servidor espera las conexiones, de
cada cliente que se conecta recibe un mensaje y a continuación le envía un saludo.
Archivo CLIENTESSL.JAVA
import java.io.*;
import javax.net.ssl.*;
//System.setProperty("javax.net.ssl.trustStore",
System.getProperty("user.dir") + "\\UsuarioAlmacenSSL");
//System.setProperty("javax.net.ssl.trustStorePassword",
"890123");
/*---------------------------------------------------------
---------------------
//Información sobre la sesión SSL
SSLSession session = ((SSLSocket) Cliente).getSession();
System.out.println("Host: "+session.getPeerHost());
System.out.println("Cifrado: " + session.getCipherSuite());
System.out.println("Protocolo: " + session.getProtocol());
System.out.println("IDentificador:" + new
BigInteger(session.getId()));
141
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
X509Certificate certificate =
(X509Certificate)session.getPeerCertificates()[0];
System.out.println("Propietario: " +
certificate.getSubjectDN());
System.out.println("Algoritmo: " +
certificate.getSigAlgName());
System.out.println("Tipo: " + certificate.getType());
System.out.println("Emisor: " + certificate.getIssuerDN());
System.out.println("Número Serie: " +
certificate.getSerialNumber());
-----------------------------------------------------------
-------------------*/
El servidor necesita disponer de un certificado que mostrar a los clientes que se conecten a él.
Usaremos la herramienta keytool para crearlo, en el ejemplo le damos el nombre de
AlmacenSSL y el valor de la clave es 1234567.
C:>java -Djavax.net.ssl.keyStore=AlmacenSSL -
Djavax.net.ssl.keyStorePassword=1234567 ServidorSSL
Una vez que tenemos el fichero exportado es necesario incorporarle al nuevo almacenamiento
para permitir realizar la validación. A continuación se crea un keystore de nombre
UsuarioAlmacenSSL con la clave 890123 y se incorpora el fichero de certificado Certificado.cer.
Esto lo hacemos donde ejecutemos el cliente.
142
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
C:>java -Djavax.net.ssl.trustStore=UsuarioAlmacenSSL -
Djavax.net.ssl.trustStorePassword=890123 ClienteSSL
En estos ejemplos para ejecutar el programa cliente y el servidor hemos establecido las
propiedades JSSE desde la línea de comandos usando la sintaxis –Dpropiedad=Valor. También
se pueden establecer desde el programa usando el método
System.setProperty(String propiedad, String valor).
En el programa servidor incluimos las siguientes líneas después de definir la variable puerto:
System.setProperty("javax.net.ssl.keyStore", "AlmacenSSL");
System.setProperty("javax.net.ssl.keyStorePassword", "1234567");
Y en el programa cliente:
System.setProperty("javax.net.ssl.trustStore", "UsuarioAlmacenSSL");
System.setProperty("javax.net.ssl.trustStorePassword", "890123");
Opcionalmente podemos registrar un proveedor SSL en los programas añadiendo esta línea:
java.security.Security.addProvider(new
com.sun.net.ssl.internal.ssl.Provider());
143
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
En el programa servidor para obtener la información del certificado tendríamos que utilizar el
método getLocalCertificates() en lugar de getPeerCertificates().
Para la autenticación de usuarios: para determinar de forma fiable y segura quién está
ejecutando nuestro código Java
Para la autorización de los usuarios: Para asegurarse de que quien lo ejecuta tiene los
permisos necesarios para realizar las acciones.
Los paquetes en los que están disponibles las clases e interfaces principales de JAAS son:
javax.security.auth.* que contiene las clases de base e interfaces para los mecanismos
de autenticación y autorización.
javax.security.auth.callback.* que contiene las clases e interfaces para definir las
credenciales de autenticación de la aplicación.
javax.security.auth.login.* que contiene las clases para entrar y salir de un dominio de
aplicación.
javax.security.auth.spi.* que contiene las interfaces para un proveedor de JAAS para
implementar módulos JAAS.
Autenticación
144
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Ejemplo
Esquema básico de las clases y ficheros que se van a utilizar en el ejemplo para probar la
autenticación básica con JAAS
EjemploLogin{
EjemploLoginModule required;
}
145
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
grant {
permission java.util.PropertyPermission "usuario", "read";
permission java.util.PropertyPermission "clave", "read";
permission javax.security.auth.AuthPermission
"createLoginContext.EjemploLogin";
};
Archivo MYCALLBACKHANDLER.JAVA
import java.io.*;
import javax.security.auth.callback.*;
this.usuario = usu;
this.clave = clave;
}
CallbackHandler tiene el método handle() que será invocado por el LoginModule al que le
pasará un array de objetos Callbacks, que contiene los campos de datos que se necesitan.
Cada Callback representa uno de los datos comunicados por el usuarios en el proceso de
autenticación (nombre, password, etc), es necesario recorrer este array para recuperar los
datos.
146
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Archivo EJEMPLOLOGINMODULE.JAVA
import java.util.Map;
import javax.security.auth.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
try {
//se invoca al método handle del CallbackHandler
//para solicitar el usuario y la contraseña
callbackHandler.handle(callbacks);
String usuario =
((NameCallback)callbacks[0]).getName();
char [] passw =
((PasswordCallback)callbacks[1]).getPassword();
String clave = new String(passw);
147
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}//Fin de login
}//Fin de EjemploLoginModule
Autorización
Archivo EJEMPLOACCION.JAVA
import java.io.*;
import java.security.PrivilegedAction;
148
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
}else {
return null;
Esta clase implementa PrivilegedAction, contiene el método run() que es el código que se
ejecutará una vez que el usuario ha sido autenticado, teniendo en cuenta las autorizaciones de
los principales definidas en el fichero de políticas.
Archivo EJEMPLOPRINCIPAL.JAVA
import java.security.Principal;
if (name == null)
throw new NullPointerException("Entrada nula");
this.name = name;
}
149
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
return false;
}//Fin de equals
}//Fin de EjemploPrincipal
Esta clase implementa la interface Principal, se usa en la clase EjemploLoginModule una vez
que el usuario ha sido autenticado en el método commit().
grant {
permission javax.security.auth.AuthPermission
"createLoginContext.EjemploLogin";
permission javax.security.auth.AuthPermission
"modifyPrincipals";
permission javax.security.auth.AuthPermission
"doAsPrivileged";
};
150
Desarrollo de Aplicaciones Multiplataforma. Inazio Claver
programandoapasitos.blogspot.com
Epílogo
Hasta aquí ha llegado lo visto en el módulo de Programación de servicios y procesos del Grado
Superior de Desarrollo de Aplicaciones Multiplataformas.
Tengo la sensación de que éste libro podría haber sido más eminentemente práctico, o haber
podido profundizar más a fondo en algunos temas, pero considero que pese a todo he creado
una buena guía para todo aquel que quiera iniciarse en la programación de servicios y
procesos desde cero.
Además, siempre podéis recurrir a Google para buscar información, o leer los siguientes libros
que recomiendo para comprender como programar procesos y servicios:
Inazio Claver
Huesca, a 25 de Marzo de 2016
151