0% encontró este documento útil (0 votos)
214 vistas54 páginas

PSP Solucionario Defweb

El documento aborda la programación multiproceso y multihilo, comparando procesadores y describiendo la destrucción de procesos en Windows y Linux. También se discuten conceptos como la programación distribuida, la diferencia entre procesos e hilos, y los sistemas de planificación de procesos. Finalmente, se presentan ejemplos de código en Java para ilustrar la ejecución de hilos y los problemas de concurrencia que pueden surgir.

Cargado por

julianiqui
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
214 vistas54 páginas

PSP Solucionario Defweb

El documento aborda la programación multiproceso y multihilo, comparando procesadores y describiendo la destrucción de procesos en Windows y Linux. También se discuten conceptos como la programación distribuida, la diferencia entre procesos e hilos, y los sistemas de planificación de procesos. Finalmente, se presentan ejemplos de código en Java para ilustrar la ejecución de hilos y los problemas de concurrencia que pueden surgir.

Cargado por

julianiqui
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

SOLUCIONARIO

Programación de servicios y procesos

UNIDAD 1.
Programación multiproceso

Actividades propuestas

1.1. Comparación de procesadores

80486DX2 i5-12600HX
Fecha de lanzamiento 1992 2022
Número de bits 32 64
Número de núcleos 1 12
Frecuencia 66MHz 4.6GHz
Precio lanzamiento aproximado 5000€ 1700€
actualizado
(ordenador completo)

1.2. Destrucción de proceso de terminal

Identificación del PID en Windows a través del administrador de tareas:

Desde una PowerShell, ejecutar el comando kill indicando como parámetro el identificador del
proceso:

© Ediciones Paraninfo 2
Programación de servicios y procesos

Una vez ejecutado, el proceso será finalizado.

En sistemas Linux, se puede obtener el PID utilizando el comando ps. El comando kill se debe
ejecutar de la misma manera que en la PowerShell de Windows.

1.3. Redacción de descripciones asociadas a códigos de finalización

Código de finalización Descripción


0 El proceso ha terminado correctamente.
1 No se puede establecer la conexión con la base de datos.
2 No se puede establecer la conexión con el servicio web del IPC.
3 El valor de referencia del IPC no es numérico.
4 Las credenciales del servicio de correo electrónico no son válidas.

Actividades finales
Actividades de comprobación

1.1. 1.2. 1.3. 1.4. 1.5. 1.6. 1.7. 1.8. 1.9. 1.10.
D D B C B A B C C C

Actividades de aplicación
1.11.

Para ejecutar un programa escrito con un lenguaje de programación interpretado se necesita


tener instalado el intérprete. Dicho intérprete leerá y traducirá las sentencias del programa
durante su ejecución para transmitírselas al sistema operativo.

En cambio, el lenguaje de programación compilado debe ser traducido por medio del proceso
denominado compilación a lenguaje máquina (binario) antes de su ejecución. Por lo tanto, se
necesita disponer de un compilador (programa específico para cada sistema operativo).

Los lenguajes interpretados suelen ser más lentos que los compilados, ya que la traducción se
realiza durante el proceso de ejecución, mientras que en los compilados se realiza en la
compilación (proceso previo a la ejecución).

© Ediciones Paraninfo 3
Programación de servicios y procesos

Los lenguajes interpretados suelen ser más versátiles en cuanto a que el mismo programa se
puede ejecutar en diferentes sistemas operativos siempre y cuando se disponga del intérprete
adecuado.

1.12.

Un programa es el resultado de la realización del proceso de programación por parte de un


programador. Puede ser el fichero fuente o el fichero compilado, ya que en ambos casos se le
denomina programa.

Un proceso es un programa en ejecución (cargado en memoria y gestionado por parte del


sistema operativo para que disponga de los recursos necesarios para dicha ejecución).

1.13.

La programación distribuida es aquella cuyo resultado (programas) se ejecuta en diferentes


ordenadores de forma coordenada con el objetivo de utilizar sus recursos de manera común.

1.14.

La diferencia fundamental se encuentra en el punto de partida. Dos procesos se inician de


manera independiente, no teniendo a priori ninguna relación entre ambos salvo el hecho de
crearse a partir del mismo programa.

Una bifurcación o fork se inicia a partir de un proceso común en ejecución. Una vez creados, cada
bifurcación será en sí misma un proceso y tendrá independencia del resto.

1.15.

Un posible esquema del sistema es el siguiente:

La base de datos intermedia hace de elemento común para la comunicación. En dicha base de
datos deberá existir una tabla en la que se almacene la información que se quiera intercambiar
compuesta por los siguientes datos:

- Emisor.
- Receptor.
- Información.
- Indicador de si ha sido leída la información.

El proceso emisor escribirá un registro en la base de datos con la información que se va a


transferir. El proceso receptor cada cierto tiempo irá a la base de datos, buscará los registros con
información destinada a él y pendientes de leer, la leerá y marcará en el indicador que ya la ha
leído.

© Ediciones Paraninfo 4
Programación de servicios y procesos

1.16.

Un posible esquema del sistema es el siguiente:

Cada proceso escribirá la información que desee transmitir al otro proceso en un fichero distinto.
Por ejemplo, Proceso 1, escribe en Fichero 1 lo que desea transmitir a Proceso 2. Proceso 2 hará
lo propio sobre Fichero 2.

Cada cierto tiempo, los procesos irán a su fichero de recepción a buscar la información
almacenada, la leerán y la eliminarán. Alternativamente, se podría guardar la información leída
en otros ficheros distintos si desea dejar copia de la información procesada.

1.17.

Un posible esquema del sistema es el siguiente:

Los procesos, desplegados y en ejecución en ordenadores independientes, necesitan disponer de


un nexo común para el intercambio de información. Este nexo puede ser una base de datos.
A dicha base de datos se podría acceder directamente, pero en esta propuesta se propone
acceder a través de un servicio web. El proceso emisor envía por HTTP la información que se va a
transferir al servicio web y este la almacena en una base de datos. El proceso receptor, cada
cierto tiempo pide al servicio web la nueva información almacenada, marcando dicho servicio la
información transferida para no volver a enviarla.

1.18.

Los hilos son más ligeros que los procesos, ya que comparten recursos como la memoria. Cuando
el sistema operativo crea un proceso le debe dotar de todos los recursos necesarios para su
ejecución. En cambio los hilos se crean en el contexto de un proceso creado previamente, por lo
que los recursos asignados son comunes.

© Ediciones Paraninfo 5
Programación de servicios y procesos

1.19.

El planificador de procesos se encarga de distribuir la capacidad de procesamiento del ordenador


entre los distintos procesos que se encuentran en ejecución, maximizando el rendimiento y la
equidad en dicho reparto.

1.20.

El sistema de planificación muy sencillo consiste en aplicar una técnica conocida como “Round-
robin”. Consiste en crear una lista de procesos y asignar el mismo tiempo a cada uno de ellos de
manera secuencial y ordenada. La lista se recorrerá desde el primer al último elemento para
volver a comenzar por el primero en cada iteración. El tiempo asignado por turno debe ser
suficientemente pequeño como para que no se aprecien parones en las ejecuciones de los
procesos que no están en turno, pero suficientemente grande como para que no se destine todo
el tiempo de CPU al cambio de contexto.

1.21.

El número de circunstancias adversas que se puede producir cuando se intenta acceder a un


ordenador remoto, a internet o a una base de datos es extenso. En la siguiente lista se muestran
algunas de las posibles contingencias, asignándoles un valor (código de retorno) y una
descripción.

- 0. Ejecución correcta.
- 1. No se dispone de acceso a la red.
- 2. No se dispone de acceso al servidor de base de datos.
- 3. Credenciales de acceso a la base de datos incorrectas.
- 4. Ordenador de destino no accesible.
- 5. Credenciales de acceso al ordenador de destino incorrecto.
- 6. No se dispone de acceso a internet.

1.22.

package [Link].actividad_1_22;

public class Ejecutador {

public static void main(String[] args) {


try {
String[] infoProceso = { "C:\\Program Files\\Mozilla
Firefox\\[Link]", "[Link] };
Process proceso = [Link]().exec(infoProceso);
int valorRetorno = [Link]();
if (valorRetorno == 0) {
[Link]("El proceso se ha completado
satisfactoriamente");
} else {
[Link]("El proceso ha fallado. Código de
error:" + valorRetorno);
}
} catch (Exception e) {
[Link]();
}
}
}

© Ediciones Paraninfo 6
Programación de servicios y procesos

Actividades de ampliación

1.23.

Se toma como referencia un equipo montado de sobremesa basado en el procesador i5-12400F.


Dicho procesador dispone de 6 núcleos. El precio en una conocida plataforma de venta online es
de 650 €. Se necesitan 7 equipos para disponer de los 40 núcleos solicitados en el enunciado, por
lo que el presupuesto asciende a 4550 €.

1.24.

Algunos lenguajes que admiten la programación de hilos de forma nativa son Java, Python, C++ o
C#.

JavaScript admite programación fuera del hilo principal de ejecución mediante el uso de Web
Workers.

1.25.

El término “Render Farm” o granja de renderización consiste en agrupar ordenadores con el fin
de reducir los tiempos de renderizado al permitir distribuir los procesos.

Existen programas que permiten aplicar este tipo de renderizado a sistemas comerciales como
Adobe After Effects, Cinema 4D o Blender.

1.26.

El primer sistema operativo multiproceso de Microsoft fue Windows NT 3.1. Creado en 1993 para
procesadores Intel Pentium de 32 bits.

1.27.

- Intel Celeron G5905. 42€. 2 núcleos.


- Intel Core i7-12700. 370€. 12 núcleos.
- AMD Ryzen 7 5800X. 299€. 8 núcleos.

1.28.

- Round Robin. Todos los procesos tienen asignado el mismo tiempo y la misma prioridad.
Difícil de implementar.
- SJF (Short Job First). Este algoritmo da prioridad a los procesos más cortos. Sólo válido en
entornos muy concretos, ya que puede provocar que procesos largos no tengan tiempo
asignado.
- FCFS (First-come, First-served). También conocido como FIFO. El primer proceso que llega
es el primero que se atiende. Es muy sencillo de implementar, pero muy poco eficiente.

© Ediciones Paraninfo 7
Programación de servicios y procesos

UNIDAD 2.
Programación multihilo

Actividades propuestas
2.1. Ejecución de run en lugar de start

Solución desarrollada con Java versión 1.8.

La ejecución del ejemplo original generar la siguiente salida:

El ratón Jerry ha comenzado a alimentarse


El ratón Mickey ha comenzado a alimentarse
El ratón Pinky ha comenzado a alimentarse
El ratón Fievel ha comenzado a alimentarse
El ratón Pinky ha terminado de alimentarse
El ratón Fievel ha terminado de alimentarse
El ratón Jerry ha terminado de alimentarse
El ratón Mickey ha terminado de alimentarse

Las cuatro primeras líneas de salida, las que indican que los ratones han comenzado a
alimentarse, se muestran inmediatamente. Las cuatro líneas finales, las que indican que los
ratones han terminado de alimentarse, van apareciendo transcurridos los segundos
correspondientes al tiempo que se les asignó para alimentarse. El tiempo total empleado es el
tiempo del ratón más lento, ya que todos comen a la vez.

Al sustituir en el código las llamadas a start() por llamadas a run(), la ejecución genera la siguiente
salida:

El ratón Fievel ha comenzado a alimentarse


El ratón Fievel ha terminado de alimentarse
El ratón Jerry ha comenzado a alimentarse
El ratón Jerry ha terminado de alimentarse
El ratón Pinky ha comenzado a alimentarse
El ratón Pinky ha terminado de alimentarse
El ratón Mickey ha comenzado a alimentarse
El ratón Mickey ha terminado de alimentarse

Cada ratón comienza a alimentarse, espera el tiempo indicado y termina de alimentarse antes de
pasar al siguiente ratón. Por lo tanto, el tiempo total empleado es la suma de los tiempos
empleados por cada ratón. Esto se debe a que la llamada a run(), pese a que no genera un error
de sintaxis, no provoca la ejecución de un hilo independiente como tal, sino una ejecución
secuencial síncrona, haciendo que cada instrucción bloquee la ejecución del único hilo existente.

2.2. Generación de problemas de concurrencia

Solución desarrollada con Java versión 1.8.

El código de partida para realizar las pruebas es el siguiente. Para las sucesivas ejecuciones se
debe modificar el valor de la constante NUMERO_HILOS declarada en el método main.

© Ediciones Paraninfo 8
Programación de servicios y procesos

package [Link];

public class RatonSimple implements Runnable {


private String nombre;
private int tiempoAlimentacion;
private int alimentoConsumido;

public RatonSimple(String nombre, int tiempoAlimentacion) {


super();
[Link] = nombre;
[Link] = tiempoAlimentacion;
}

public void comer() {


try {
[Link]("El ratón %s ha comenzado a alimentarse%n",
nombre);
[Link](tiempoAlimentacion * 1000);
alimentoConsumido++;
[Link]("El ratón %s ha terminado de
alimentarse%n", nombre);
[Link]("Alimento consumido:%d%n",
alimentoConsumido);
} catch (InterruptedException e) {
[Link]();
}
}

@Override
public void run() {
[Link]();
}

public static void main(String[] args) {


RatonSimple fievel = new RatonSimple("Fievel", 4);
final int NUMERO_HILOS = 1000;//2, 10, 100 ó 1000
for (int i = 0; i < NUMERO_HILOS; i++) {
new Thread(fievel).start();
}
}
}

Tras dos ejecuciones con cada configuración de número de hilos (2, 10, 100 y 100), el resultado
obtenido se muestra en la siguiente tabla.

Se comparan, para cada número de hilos, el alimento que deberían consumir los ratones y el que
han consumido en realidad.

Alimento Alimento consumido Alimento consumido


Número de
consumido máximo máximo
hilos
esperado (Primera ejecución) (Segunda ejecución)
2 2 2 2
10 10 9 10
100 100 100 99
1000 1000 998 997

© Ediciones Paraninfo 9
Programación de servicios y procesos

En los resultados obtenidos se pueden observar algunas características propias de los sistemas
multihilo y de los problemas de concurrencia asociados. En primer lugar, llama la atención que las
salidas son distintas en diferentes ejecuciones. Esto se debe a que el procesador tiene diferentes
cargas de trabajo en distintas ejecuciones. Se observa como las salidas esperadas y las obtenidas
no son consistentes en todas las ejecuciones.

En el contexto de ejecución en que se realizaron las pruebas, los problemas comienzan a aparecer
a partir de 10 hilos. Esto no tiene por qué ocurrir en todas las ejecuciones, ya que depende de
múltiples factores. Sucesivas pruebas arrojarán diferentes resultados, pero en general a mayor
número de hilos más frecuentes e importantes serán las diferencias entre resultados esperados y
obtenidos.

Es importante recordar que estos errores no se van a producir siempre, por lo que podrían no
detectarse en pruebas de software tradicionales. Los errores de concurrencia deben evitarse
durante la codificación, anticipando los posibles riesgos.

2.3. Análisis de los estados de los hilos

Solución desarrollada con Java versión 1.8.

El código resultante tras la eliminación de la llamada al método sleep() es el siguiente:

package [Link];

import [Link];

public class RatonSimple implements Runnable {


private String nombre;
private int tiempoAlimentacion;
private int alimentoConsumido;

public RatonSimple(String nombre, int tiempoAlimentacion) {


super();
[Link] = nombre;
[Link] = tiempoAlimentacion;
}

public void comer() {


[Link]("El ratón %s ha comenzado a alimentarse%n",
nombre);
alimentoConsumido++;
[Link]("El ratón %s ha terminado de alimentarse%n",
nombre);
[Link]("Alimento consumido:%d%n", alimentoConsumido);
}

@Override
public void run() {
[Link]();
}

public static void main(String[] args) {


RatonSimple mickey = new RatonSimple("Mickey", 6);
ArrayList<[Link]> estadosHilo = new ArrayList();
Thread h = new Thread(mickey);
[Link]([Link]());
[Link]();

© Ediciones Paraninfo 10
Programación de servicios y procesos

while ([Link]() != [Link]) {


if (![Link]([Link]())) {
[Link]([Link]());
}
}
if (![Link]([Link]())) {
[Link]([Link]());
}
for ([Link] estado : estadosHilo) {
[Link](estado);
}
}
}

La salida producida por la ejecución es la siguiente:

El ratón Mickey ha comenzado a alimentarse


El ratón Mickey ha terminado de alimentarse
Alimento consumido:1
NEW
RUNNABLE
TERMINATED

Como se puede observar, no aparece el estado TIMED_WAITING, ya que a este estado se


transmita por la invocación al método sleep().

2.4. Planificación de ejercicios de estiramiento

Solución desarrollada con Java versión 1.8.

Planificador:

package [Link].actividad_propuesta_2_4;

import [Link];
import [Link];

public class RelojActividad extends TimerTask{

@Override
public void run() {
try {
[Link]("Es momento de
andar durante 5 minutos");
} catch (Exception e) {
[Link]();
}
}

public static void main(String[] args) {


Timer temporizador = new Timer();
[Link](new RelojActividad(), 1800000, 1800000);
}
}

© Ediciones Paraninfo 11
Programación de servicios y procesos

Generador de notificaciones (Windows 10):

package [Link].actividad_propuesta_2_4;

import [Link].*;

public class W10GeneradorNotificaciones {


public static void mostrarMensaje(String mensaje) throws AWTException {
SystemTray tray = [Link]();
Image image = [Link]().createImage("[Link]");
TrayIcon trayIcon = new TrayIcon(image, "Avisador de inactividad");
[Link](trayIcon);
[Link]("Aviso de actividad", mensaje,
[Link]);
}
}

2.5. Hilos durmientes

Solución desarrollada con Java versión 1.8.

package [Link].actividad_propuesta_2_5;

public class Hilo extends Thread {


private String nombre;
public Hilo(int numero) {
[Link]=[Link]("Hilo%d", numero);
}
@Override
public void run() {
while(true) {
[Link]([Link]("Soy el bucle %s y estoy
trabajando", [Link]));
try {
long pausa = (long)([Link]()*10)+1;
[Link](pausa*1000);
} catch (Exception e) {
[Link]();
}
}
}
public static void main(String[] args) {
for (int i=1;i<=5;i++) {
new Hilo(i).start();
}
}
}

2.6. Determina la sección crítica.

Solución desarrollada con Java versión 1.8.

En el código que forma parte del enunciado existen dos segmentos que se pueden considerar
sección crítica: la línea 16 y las líneas comprendidas entre las líneas 21 y 23.

En ambos casos, se está accediendo a una variable estática directa o indirectamente a través de
operaciones no atómicas, lo que podría generar errores de concurrencia.

© Ediciones Paraninfo 12
Programación de servicios y procesos

2.7. Resolución de error de concurrencia con clases de [Link]

Solución desarrollada con Java versión 1.8.

El problema de concurrencia se resuelve sustituyendo la clase ArrayList del paquete [Link] por
CopyOnWriteArrayList del paquete [Link].

package [Link];

import [Link];
import [Link].*;

public class GestorHojas extends Thread {


private static List<String> lista = new CopyOnWriteArrayList<String>();

@Override
public void run() {
while(true) {
if ([Link]()>=10) {
[Link](0);
}
else if ([Link]()<10) {
[Link](new String("Texto"));
}
for (String string : lista) {
//Recorriendo la lista
}
}
}

public static void main(String[] args) {


for(int i=0;i<10;i++) {
[Link](new String("Texto"));
}
for (int i=0;i<100;i++) {
new GestorHojas().start();
}
}
}

2.8. Sincronización de segmento

Solución desarrollada con Java versión 1.8.

La ejecución del código de ejemplo provoca la siguiente salida:

Comienzo del método 2 del hilo 2


Comienzo del método 1 del hilo 1
Fin del método 2 del hilo 2
Fin del método 1 del hilo 1
Comienzo del método 2 del hilo 1
Comienzo del método 1 del hilo 2
Fin del método 2 del hilo 1
Fin del método 1 del hilo 2

Se ejecutan simultáneamente un método del hilo 1 y un método del hilo 2, pero nunca dos
métodos del mismo hilo.

© Ediciones Paraninfo 13
Programación de servicios y procesos

package [Link];

public class SincronizacionSegmento extends Thread {


int id;
static Object bloqueo1 = new Object();
static Object bloqueo2 = new Object();
public SincronizacionSegmento(int id) {
[Link] = id;
}
public void metodo1() {
synchronized(bloqueo1) {
[Link]("Comienzo del método 1 del hilo " + id);
try {
[Link](1000);
} catch (InterruptedException ie) {
return;
}
[Link]("Fin del método 1 del hilo " + id);
}
}

public void metodo2() {


synchronized (bloqueo1) {
[Link]("Comienzo del método 2 del hilo " + id);
try {
[Link](1000);
} catch (InterruptedException ie) {
return;
}
[Link]("Fin del método 2 del hilo " + id);
}
}
@Override
public void run() {
if (id==1) {
metodo1();
metodo2();
} else {
metodo2();
metodo1();
}
}
public static void main(String[] args) {
new SincronizacionSegmento(1).start();
new SincronizacionSegmento(2).start();
}
}

Al utilizar los dos bloques sincronizados el mismo objeto de bloqueo, se impide que dichos
métodos se ejecuten concurrentemente. Por lo tanto, la ejecución se realizará método a método,
de manera secuencial.

Comienzo del método 1 del hilo 1


Fin del método 1 del hilo 1
Comienzo del método 2 del hilo 1
Fin del método 2 del hilo 1
Comienzo del método 2 del hilo 2
Fin del método 2 del hilo 2

© Ediciones Paraninfo 14
Programación de servicios y procesos

Comienzo del método 1 del hilo 2


Fin del método 1 del hilo 2

2.9. Resolución de error de concurrencia con synchronized

Solución desarrollada con Java versión 1.8.

package [Link];

import [Link];
import [Link];

public class GestorHojas extends Thread {


private static Object objetoBloqueo = new Object();
private static List<String> lista = new ArrayList<String>();

@Override
public void run() {
while(true) {
if ([Link]()>=10) {
synchronized(objetoBloqueo) {
[Link](0);
}
}
else if ([Link]()<10) {
synchronized(objetoBloqueo) {
[Link](new String("Texto"));
}
}
synchronized(objetoBloqueo) {
for (String string : lista) {
//Recorriendo la lista
}
}
}
}

public static void main(String[] args) {


for(int i=0;i<10;i++) {
[Link](new String("Texto"));
}
for (int i=0;i<100;i++) {
new GestorHojas().start();
}
}
}

2.10. Uso de semáforos

Solución desarrollada con Java versión 1.8.

En la solución propuesta a continuación, la ejecución se desarrolla de la siguiente manera:

• Inicialmente se ejecutan concurrentemente dos versiones del método 1 de dos hilos


distintos.
• Transcurrida la espera en milisegundos programada, se ejecutan dos versiones del método 2
de dos hilos distintos y dos versiones del método 1 de otros dos hilos distintos.

© Ediciones Paraninfo 15
Programación de servicios y procesos

Esto se debe a que inicialmente se lanza el método 1 de todos los hilos y, dado que hay un
semáforo que impide que más de dos hilos ejecuten un mismo método, hasta que los primeros
hilos no terminen la ejecución del método 1 y pasen al método 2, no dejarán recursos para el
resto de los hilos.

package [Link];

import [Link];

public class Sistema extends Thread {


static Semaphore semaforo1 = new Semaphore(2);
static Semaphore semaforo2 = new Semaphore(2);
private int numeroHilo;
private static int MILISEGUNDOS_ESPERA = 2000;

public Sistema(int numeroHilo) {


[Link]=numeroHilo;
}

public void metodo1() {


try {
[Link]();
[Link]("Ejecutando método 1 del hilo " +
[Link]);
[Link](MILISEGUNDOS_ESPERA);
} catch (Exception e) {
[Link]();
} finally {
[Link]();
}
}
public void metodo2() {
try {
[Link]();
[Link]("Ejecutando método 2 del hilo " +
[Link]);
[Link](MILISEGUNDOS_ESPERA);
} catch (Exception e) {
[Link]();
} finally {
[Link]();
}
}

@Override
public void run() {
this.metodo1();
this.metodo2();
}

public static void main(String[] args) {


for (int i=1;i<=10;i++) {
new Sistema(i).start();
}
}
}

© Ediciones Paraninfo 16
Programación de servicios y procesos

Actividades finales
Actividades de comprobación

2.1. 2.2. 2.3. 2.4. 2.5. 2.6. 2.7. 2.8. 2.9. 2.10. 2.11. 2.12. 2.13. 2.14.
C A B B A C C A C C B C C C

Actividades de aplicación
2.15.

En la programación secuencial, las instrucciones de los programas se ejecutan una tras otra,
independientemente de la capacidad de proceso que tenga el ordenador en el que se están
procesando y de las características de los propios programas.
En la programación concurrente, las instrucciones de los programas se intentan ejecutar de
manera simultánea, aprovechando las capacidades de computación del ordenador en el que se
están procesando.
2.16.
• Nuevo. El hilo está creado, pero no está en ejecución.
• Ejecutable. El hilo podrá estar en ejecución o pendiente de ejecución.
• Bloqueado. Está bloqueado a la espera ser liberado.
• Esperando. Está esperando a que otro hilo realice una acción concreta.
• Esperando un tiempo. Está esperando a que otro hilo realice una acción concreta durante
un tiempo determinado.
• Finalizado. El hilo ha terminado de ejecutarse.

2.17.
Algunos de los problemas de concurrencia tienen que ver con el orden de ejecución de los hilos.
Este orden viene determinado por el sistema operativo e intervienen circunstancias de contexto
que pueden ser distintas en distintas ejecuciones. Por lo tanto, en función de estas circunstancias
cambiantes, el orden podrá ser el adecuado o no. Normalmente, a mayor número de hilos y mayor
número de cálculos, mayor es la posibilidad de que se produzcan errores.
2.18.
La interfaz Runnable es aquella que deben implementar los bloques de código que se ejecuten
como un hilo independiente. La clase Thread implementa esta interfaz.
2.19.

Java es un lenguaje que solo admite herencia simple. De esta manera, si se hereda de la clase
Thread, ya no se podrá heredar de ninguna otra clase. Por ejemplo, si se desea crear una versión
multihilo de una clase existente, no se podría heredar de esta a la vez que de la clase Thread. La
interfaz Runnable sí permitiría heredar de esa clase base e implementar la funcionalidad multihilo.
2.20.

[Link] es la variante segura frente a múltiples hilos de la clase


[Link].

© Ediciones Paraninfo 17
Programación de servicios y procesos

[Link]. CopyOnWriteArraySet es la variante segura frente a múltiples hilos de las


implementaciones de la interfaz Set.
2.21.

El método sleep() permite suspender la ejecución del hilo desde el que se invoca. Acepta como
parámetro la cantidad de tiempo en milisegundos.

Puede lanzar InterruptedException.

2.22.
En computación, una interrupción es una suspensión temporal de la ejecución de un proceso o de
un hilo de ejecución. Las interrupciones no suelen pertenecer a los programas, sino al sistema
operativo, viniendo generadas por peticiones realizadas por los dispositivos periféricos.
2.23.

Si se desea que varios hilos compartan información existen varias alternativas:

• Utilizar atributos estáticos. Los atributos estáticos son comunes a todas las instancias, por
los que independientemente de la manera de construir los hilos la información es
compartida.

• Utilizando referencias a objetos comunes accesibles desde todos los hilos.

• Utilizando atributos no estáticos de la instancia de una clase que implemente Runnable y


construyendo los hilos a partir de dicha instancia.

• Existen otras formas de compartir información, ya sea a través de ficheros, bases de datos
o servicios de red o de internet.

2.24.
Declarando una variable como volatile solo existirá una copia en el procesador.
2.25.

El método join permite indicar a un hilo que debe suspender su ejecución hasta que termina otro
hilo de referencia. Este método debe ejecutarse dentro del bloque asíncrono del código, ya que lo
contrario no tendrá ningún efecto.
2.26.

Los semáforos se suelen utilizar cuando un recurso tiene una capacidad limitada y se desea
controlar el número de consumidores de dicho recurso.

Actividades de ampliación

2.27.

Según la documentación del método stop() de la clase Thread, este método es intrínsecamente
inseguro. Detener un subproceso con [Link] hace que desbloquee todos los monitores que
ha bloqueado, lo que podría generar un comportamiento arbitrario.

2.28.

Un pool de hilos es una colección limitada de hilos que se pueden utilizar bajo demanda, evitando
un uso abusivo de los recursos del sistema.

© Ediciones Paraninfo 18
Programación de servicios y procesos

Las clases Executors, Executor y ExecutorService proporcionan los mecanismos para crear pools de
hilos en Java.

2.29.

Las interrupciones en los sistemas operativos son los mecanismos disponibles para que los
dispositivos periféricos (teclado, ratón, tarjeta de red, etc.) “interrumpan” temporalmente la
ejecución de los programas para enviar las señales relacionadas con los eventos que suceden en
dichos dispositivos.

2.30.

Python dispone de una clase Semaphore que dispone de los métodos acquire() y reléase() para
adquirir y liberar el semáforo.

2.31.

“CUDA son las siglas de Compute Unified Device Architecture (Arquitectura Unificada de
Dispositivos de Cómputo) que hace referencia a una plataforma de computación en paralelo que
incluye un compilador y un conjunto de herramientas de desarrollo creadas por Nvidia que
permiten a los programadores usar una variación del lenguaje de programación C (CUDA C) para
codificar algoritmos en GPU de Nvidia.

Por medio de wrappers se puede usar Python, Fortran, Julia y Java en vez de C/C++.”

Fuente: Wikipedia.

2.32.

“El problema de la cena de los filósofos o problema de los filósofos cenando (dining philosophers
problem) es un problema clásico de las ciencias de la computación propuesto por Edsger Dijkstra
en 1965 para representar el problema de la sincronización de procesos en un sistema operativo.

Cinco filósofos se sientan alrededor de una mesa y pasan su vida cenando y pensando. Cada
filósofo tiene un plato de fideos y un tenedor a la izquierda de su plato. Para comer los fideos son
necesarios dos tenedores y cada filósofo solo puede tomar los que están a su izquierda y derecha.
Si cualquier filósofo toma un tenedor y el otro está ocupado, se quedará esperando, con el tenedor
en la mano, hasta que pueda tomar el otro tenedor, para luego empezar a comer.

Si dos filósofos adyacentes intentan tomar el mismo tenedor a una vez, se produce una condición
de carrera: ambos compiten por tomar el mismo tenedor, y uno de ellos se queda sin comer.

Si todos los filósofos toman el tenedor que está a su derecha al mismo tiempo, entonces todos se
quedarán esperando eternamente, porque alguien debe liberar el tenedor que les falta. Nadie lo
hará porque todos se encuentran en la misma situación (esperando que alguno deje sus
tenedores). Entonces los filósofos se morirán de hambre. Este bloqueo mutuo se denomina
interbloqueo o deadlock.

El problema consiste en encontrar un algoritmo que permita que los filósofos nunca se mueran de
hambre.”

© Ediciones Paraninfo 19
Programación de servicios y procesos

Fuente: Wikipedia.

Una buena solución de este problema se encuentra en el siguiente enlace:

[Link]

© Ediciones Paraninfo 20
Programación de servicios y procesos

UNIDAD 3.
Programación de comunicaciones en red

Actividades propuestas
3.1. Lectura remota de ficheros.

Solución desarrollada con Java versión 1.8.

Se deben crear dos proyectos, uno para alojar el programa servidor y otro para alojar el
programa cliente.

Servidor:

package [Link];

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

public class SocketTCPServer {


private ServerSocket serverSocket;
private Socket socket;
private InputStream is;
private OutputStream os;

public SocketTCPServer (int puerto) throws IOException {


serverSocket = new ServerSocket(puerto);
}
public void start() throws IOException {
[Link]("(Servidor) Esperando conexiones...");
socket = [Link]();
is = [Link]();
os = [Link]();
[Link]("(Servidor) Conexión establecida.");
}
public void stop() throws IOException {
[Link]("(Servidor) Cerrando conexiones...");
[Link]();
[Link]();
[Link]();
[Link]();
[Link]("(Servidor) Conexiones cerradas.");
}

public static void main(String[] args) {


try {
SocketTCPServer servidor = new SocketTCPServer(49171);
[Link]();

© Ediciones Paraninfo 21
Programación de servicios y procesos

BufferedReader br = new BufferedReader(new


InputStreamReader([Link]));
String nombreFichero = [Link]();
FileReader fr = new FileReader(nombreFichero);
int caracter;
while((caracter=[Link]())!=-1) {
[Link](caracter);
}
[Link]();
[Link]();
} catch (IOException ioe){
[Link]();
}
}
}

Cliente:

package [Link];

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

public class SocketTCPClient {


private String serverIP;
private int serverPort;
private Socket socket;
private InputStream is;
private OutputStream os;
//Objetos específicos para el envío y recepción de cadenas de caracteres
private InputStreamReader isr;
private BufferedReader br;
private PrintWriter pw;

public SocketTCPClient(String serverIP, int serverPort) {


[Link] = serverIP;
[Link] = serverPort;
}
public void start() throws UnknownHostException, IOException {
[Link]("(Cliente) Estableciendo conexión...");
socket = new Socket(serverIP, serverPort);
os = [Link]();
is = [Link]();

[Link]("(Cliente) Conexión establecida.");


}
public void stop() throws IOException {
[Link]("(Cliente) Cerrando conexiones...");
[Link]();
[Link]();
[Link]();
[Link]("(Cliente) Conexiones cerradas.");

© Ediciones Paraninfo 22
Programación de servicios y procesos

}
public void abrirCanalesDeTexto() {
[Link]("(Cliente) Abriendo canales de texto...");
//Canales de lectura
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
//Canales de escritura
pw = new PrintWriter(os, true);
[Link]("(Cliente) Canales de texto abiertos.");
}
public void cerrarCanalesDeTexto() throws IOException {
[Link]("(Cliente) Cerrando canales de texto.");
//Canales de lectura
[Link]();
[Link]();
//Canal de escritura
[Link]();
[Link]("(Cliente) Canales de texto cerrados.");
}
public void solicitarFichero(String mensaje) {
[Link]("(Cliente) Enviando mensaje...");
[Link](mensaje);
[Link]("(Cliente) Mensaje enviado.");
}

public static void main(String[] args) {


SocketTCPClient cliente = new SocketTCPClient("localhost", 49171);
Scanner sc = new Scanner([Link]);
[Link]("Introduce la ruta y el nombre del fichero
remoto:");
String nombreFichero = [Link]();
try {
[Link]();
[Link]();
//Envio del mensaje al servidor
[Link](nombreFichero);
//Recepción del mensaje del servidor
int caracter;
[Link]("(Cliente) Mensaje recibido:");
while ((caracter=[Link]())!=-1) {
[Link]((char)caracter);
}
[Link]();
[Link]("(Cliente) Fin del mensaje");
[Link]();
[Link]();
} catch (UnknownHostException e) {
[Link]();
} catch (IOException e) {
[Link]();
}
[Link]();
}
}

© Ediciones Paraninfo 23
Programación de servicios y procesos

Al ejecutar en una terminal SocketTCPServer muestra el siguiente mensaje esperando


establecer una conexión:

(Servidor) Esperando conexiones...

Al ejecutar en una terminal SocketTCPCliente muestra el siguiente mensaje esperando que el


usuario introduzca la ruta y el nombre de un fichero alojado en el ordenador servidor:

Introduce la ruta y el nombre del fichero remoto:

Al introducir en la ejecución del programar cliente el nombre del fichero remoto, se muestra el
contenido de este por el terminal:

Introduce la ruta y el nombre del fichero remoto:c:/[Link]


(Cliente) Estableciendo conexión...
(Cliente) Conexión establecida.
(Cliente) Abriendo canales de texto...
(Cliente) Canales de texto abiertos.
(Cliente) Enviando mensaje...
(Cliente) Mensaje enviado.
(Cliente) Mensaje recibido:Contenido
del
fichero
remoto...
(Cliente) Fin del mensaje
(Cliente) Cerrando canales de texto.
(Cliente) Canales de texto cerrados.
(Cliente) Cerrando conexiones...
(Cliente) Conexiones cerradas.

3.2. Transferencia de datos por UDP

Solución desarrollada con Java versión 1.8.

Se deben crear dos proyectos, uno para alojar el programa servidor y otro para alojar el
programa cliente.

Se deberá ejecutar el servidor en un terminal (u ordenador) y el cliente en otro terminal (u


ordenador).

Servidor:

package [Link];

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

public class SocketUDPServer {


public static void main(String[] args) {
PrintWriter pw = null;
DatagramSocket socket = null;

© Ediciones Paraninfo 24
Programación de servicios y procesos

try {
socket = new DatagramSocket(49171);
byte[] bufferLectura = new byte[32];
DatagramPacket datagramaEntrada =
new DatagramPacket(bufferLectura,
[Link]);
String mensajeRecibido;
pw = new PrintWriter(new File("G:/salida_udp.txt"));
do {
[Link](datagramaEntrada);
mensajeRecibido = new String(bufferLectura);
[Link](mensajeRecibido);
if ([Link]("FIN")) {
break;
}
[Link](mensajeRecibido);
bufferLectura = new byte[32];
datagramaEntrada =
new DatagramPacket(bufferLectura,
[Link]);
} while (true);
} catch (SocketException e) {
[Link]();
} catch (IOException e) {
[Link]();
} finally {
if (socket!=null) {
[Link]();
}
if (pw!=null) {
[Link]();
}
}
}
}

Cliente:

package [Link];

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

public class SocketUDPClient {

public static void main(String[] args) {


DatagramSocket socketUDP;
try {
InetAddress hostServidor =
[Link]("localhost");
int puertoServidor = 49171;
socketUDP = new DatagramSocket();
DatagramPacket datagrama = null;
for (int i=0;i<10000;i++) {
byte[] mensaje = ("Mensaje:" + i).getBytes();

© Ediciones Paraninfo 25
Programación de servicios y procesos

datagrama = new DatagramPacket(mensaje,


[Link], hostServidor, puertoServidor);
[Link](datagrama);
[Link]("(Cliente):" + new
String(mensaje));
}
byte[] mensaje = ("FIN").getBytes();
datagrama = new DatagramPacket(mensaje, [Link],
hostServidor, puertoServidor);
[Link](datagrama);
[Link]("(Cliente):" + new String(mensaje));
[Link]();
} catch (SocketException e) {
[Link]();
} catch (UnknownHostException e) {
[Link]();
} catch (IOException e) {
[Link]();
}
}
}

Actividades finales
Actividades de comprobación

3.1. 3.2. 3.3. 3.4. 3.5. 3.6. 3.7. 3.8. 3.9. 3.10
D C B A A C B C A C

Actividades de aplicación
3.11.
1. Aplicación.

2. Transporte.

3. Internet.
4. Interfaz de red.

3.12.

Las direcciones IPv4 y las IPv6 tienen diferencias en el formato y la composición. Esto afecta al
número de direcciones diferentes que se pueden crear con una y otra versión, siendo en el caso de
las direcciones IPv6 extremadamente alto.

3.13.

En las comunicaciones en red, un paquete es cada uno de los elementos en los que se divide un
mensaje para optimizar su transferencia.

3.14.
Es un paquete de datos en la transferencia realizada mediante el protocolo UDP.

© Ediciones Paraninfo 26
Programación de servicios y procesos

3.15.

MySQL utiliza por defecto el puerto 3306.


3.16.

package [Link];
import [Link];
import [Link];

public class Actividad {


public static void main(String[] args) {
String host = "[Link]";
try {
InetAddress ip = [Link](host);
[Link](ip);
} catch (UnknownHostException e) {
[Link]();
}
}
}

3.17.

package [Link];
import [Link];
import [Link];

public class Actividad {


public static void main(String[] args) {
String ip = "[Link]";
try {
InetAddress ia = [Link](ip);
[Link]([Link]());
} catch (UnknownHostException e) {
[Link]();
}
}
}

3.18.
Los sockets TCP están orientados a conexión y garantizan la recepción íntegra de los paquetes. Es,
por lo tanto, un protocolo fiable; pero dicha fiabilidad supone ralentización en las transmisiones, ya
que los paquetes que se pierden o deterioran deben ser reenviados.

Los sockets UDP no están orientados a conexión y no garantizan la recepción íntegra de los
paquetes, pudiendo sufrir pérdidas. Esta potencial pérdida de fiabilidad permite que la velocidad
en la transmisión sea mayor.

3.19.

La web permite intercambiar recursos, ya sean estos documentos HTML, ficheros PDF o imágenes,
por citar algunos ejemplos. Dichos recursos deben poderse transmitir de manera íntegra o, de lo
contrario, el receptor sería incapaz de restaurar el recurso solicitado. UDP, al contrario, admite
pérdidas de paquetes, por lo que, en caso de producirse, la recuperación del recurso solicitado por
parte del cliente sería imposible en muchos casos.

3.20.
Los pasos para crear un servidor basado en sockets TCP son los siguientes:

© Ediciones Paraninfo 27
Programación de servicios y procesos

1. Crear un socket de tipo servidor (server socket) asociado a una dirección y a un puerto
concretos.
2. Indicar al socket de tipo servidor que quede a la espera de peticiones de establecimiento
de conexión por parte del cliente.
3. Aceptar el establecimiento de la conexión y obtención del socket.

4. Abrir los flujos de lectura y escritura de datos.

5. Intercambiar datos con el cliente.


6. Cerrar los flujos de lectura y escritura de datos.

7. Cerrar de la conexión.
3.21.

Los pasos para la creación de un cliente basado en sockets TCP son los siguientes:

1. Crear un socket de tipo cliente (socket) indicando la dirección IP y el puerto del servidor.
2. Abrir los flujos de lectura y escritura de datos.

3. Intercambiar datos con el servidor.


4. Cerrar los flujos de lectura y escritura de datos.
5. Cerrar de la conexión.
3.22.

Un servidor de un hilo simple solo podría procesar la petición de un cliente en un momento


determinado. Por lo tanto, cada cliente debería esperar a que el servidor atendiese a todos los
clientes anteriores, imposibilitando el funcionamiento de la web en entornos de múltiples clientes
tal y como la conocemos.
3.23.
Un videojuego en red en tiempo real utilizará UDP para reducir las latencias en las transferencias
de información entre los jugadores y los servidores. De utilizar TCP, los tiempos de transmisión de
datos se elevan y arruinarían la experiencia de juego.
No obstante, podría utilizarse TCP como protocolo para desarrollar un juego en red si las
características de este lo permiten por no requerir un flujo constante de información. Por ejemplo,
un juego de cartas por turnos no requiere una velocidad de transmisión elevada, admitiendo en
este caso el uso de TCP.

Actividades de ampliación

3.24.

En Wikipedia está publicado un excelente artículo sobre el modelo OSI de comunicaciones.

[Link]

Otra interesante fuente para comprender y profundizar sobre el modelo OSI es el siguiente artículo
de IONOS:

© Ediciones Paraninfo 28
Programación de servicios y procesos

[Link]
normas-y-protocolos/

3.25.

“El IPv6 es una actualización al protocolo IPv4, diseñado para resolver el problema de agotamiento
de direcciones. Su desarrollo comenzó en diciembre de 1998 cuando Steve Deering y Robert
Hinden, empleados de Cisco y Nokia publicaron una especificación formal del protocolo a través de
un RFC12 y aún continua su implementación.”

Fuente: Wikipedia.

3.26.

Se pueden programar sockets TCP en los lenguajes siguientes:

• Java. [Link]
• Python. [Link]
• PHP. [Link]
• C#. [Link]
• C++. [Link]
• Kotlin. [Link]

3.27.

La longitud mínima es de 8 bytes.


La longitud máxima es de 65535 bytes.

3.28.

“La clase de socket de datagrama de multidifusión es útil para enviar y recibir paquetes de
multidifusión IP. Un MulticastSocket es un DatagramSocket (UDP), con capacidades adicionales
para unirse a "grupos" de otros hosts de multidifusión en Internet.”

Fuente: API de Java 8.

3.29.

En el siguiente artículo de MDN Web Docs proporciona una explicación clara y concisa sobre el
concepto de WebSocket:

[Link]

© Ediciones Paraninfo 29
Programación de servicios y procesos

UNIDAD 4.
Generación de servicios en red

Actividades propuestas
4.1. Acceso a un servicio web que proporcione un fichero JSON, recuperarlo y mostrar el
contenido por la consola.

Solución desarrollada con Java versión 17.

Para la solución de este ejercicio se utiliza el API del periódico El País para consultar los premios
obtenidos en los números de lotería del premio de Navidad.

La información se encuentra en la siguiente URL:


[Link]

El formato de la petición (de tipo GET) es el siguiente:


[Link]

Siendo numero el número que se desea consultar, como se puede ver en el siguiente ejemplo:
[Link]

El resultado proporcionado por el servicio web es, por ejemplo, el siguiente:


busqueda={"numero":86148,"premio":400000,"timestamp":1640196174,"status":4,"error":0}

En la solución propuesta se analiza la respuesta como una cadena de caracteres, aunque podría
procesarse como un fichero JSON utilizando la librería adecuada.

package [Link].ap_4_1;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

public class GestorPeticionesHTTP {


public static void main(String[] args) {
try {
int numero = 86148 ;//Número del décimo
String direccion =
"[Link]
HttpClient httpClient =
[Link]().version([Link].HTTP_1_1)

.followRedirects([Link]).build();
HttpRequest request =
[Link]().GET().uri([Link](direccion))
.headers("Content-Type",
"text/plain").setHeader("User-Agent", "Mozilla/5.0").build();
HttpResponse<String> response = [Link](request,
[Link]());

© Ediciones Paraninfo 30
Programación de servicios y procesos

if ([Link]()==200) {//Petición procesada


correctamente
String respuesta = [Link]();
String cadenaInicio = "\"premio\":";
int posicionInicio =
[Link](cadenaInicio)+[Link]();
int posicionFinal =
[Link](",",posicionInicio);
[Link]("PREMIO:" +
[Link](posicionInicio, posicionFinal));
}

} catch (Exception e) {
[Link]();
}
}
}

4.2. Descarga de ficheros a través de FTP

Solución desarrollada con Java versión 17.

package [Link].ap_4_2;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

import [Link];
import [Link];
import [Link];
import [Link];

public class GestorFTP {


private FTPClient clienteFTP;
private static final String SERVIDOR = "localhost";
private static final int PUERTO = 21;
private static final String USUARIO = "fpaniagua";
private static final String PASSWORD = "psp$1971";

public GestorFTP() {
clienteFTP = new FTPClient();
}

private void conectar() throws SocketException, IOException {


[Link](SERVIDOR, PUERTO);
int respuesta = [Link]();
if (![Link](respuesta)) {
[Link]();
throw new IOException("Error al conectar con el servidor
FTP");
}
boolean credencialesOK = [Link](USUARIO, PASSWORD);
if (!credencialesOK) {
throw new IOException("Error al conectar con el servidor FTP.
Credenciales incorrectas.");

© Ediciones Paraninfo 31
Programación de servicios y procesos

}
[Link](FTP.BINARY_FILE_TYPE);
}

private void desconectar() throws IOException {


[Link]();
}

private boolean descargarFichero(String ficheroRemoto, String pathLocal)


throws IOException {
OutputStream os = new BufferedOutputStream(new
FileOutputStream(pathLocal));
boolean recibido = [Link](ficheroRemoto, os);
[Link]();
return recibido;
}

public static void main(String[] args) {


GestorFTP gestorFTP = new GestorFTP();
try {
[Link]();
[Link]("Conectado");

FTPFile[] ficheros = [Link]();


for (FTPFile ftpFile : ficheros) {
[Link]();
boolean descargado =
[Link]([Link](), "j:/"+[Link]());
if (descargado) {
[Link]("Fichero " +
[Link]() + " descargado correctamente");
} else {
[Link]("Ha ocurrido un error al
intentar descargar el fichero");
}
}
[Link]();
[Link]("Desconectado");
} catch (Exception e) {
[Link]("Ha ocurrido un error:" + [Link]());
}
}
}

4.3. Obtención de los buzones entrada de una cuenta de Gmail

Solución desarrollada con Java versión 17.

package [Link].ap_4_3;

import [Link];
import [Link];

import [Link];
import [Link];
import [Link];

import [Link];

© Ediciones Paraninfo 32
Programación de servicios y procesos

public class LectorEmail {

private Session getSesionImap() {


Properties propiedades = new Properties();
[Link]("[Link]", "imap");
[Link]("[Link]", "[Link]");
[Link]("[Link]", "993");
[Link]("[Link]", "true");
[Link]("[Link]", "[Link]");
Session sesion = [Link](propiedades);
return sesion;
}

public void leerCarpetaInbox(String email, String password) throws


Exception {
Session sesion = [Link]();
Store almacen = [Link]("imaps");
[Link]("[Link]", 993, email, password);
IMAPFolder inbox = (IMAPFolder) [Link]("INBOX");
[Link](Folder.READ_WRITE);
[Link][] folders = [Link]().list();
for (Folder folder : folders) {
[Link]([Link]());
}
}

public static void main(String[] args) {


Scanner sc = new Scanner([Link]);
[Link]("Introduce dirección de correo:");
String email = [Link]();
[Link]("Introduce contraseña:");
String password = [Link]();
[Link]();
try {
new LectorEmail().leerCarpetaInbox(email, password);
} catch (Exception e) {
[Link]();
}
}
}

4.4. Desarrollo de aplicación distribuida de obtención de definiciones

Solución desarrollada con Java versión 17.

Servidor:

package [Link].ap_4_4;

import [Link];
import [Link];

public interface IGestorDefiniciones extends Remote {


public String obtenerDefinicion(String termino) throws RemoteException;
}

© Ediciones Paraninfo 33
Programación de servicios y procesos

package [Link].ap_4_4;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

public class App {


public static void registrarGestorDefiniciones() {
try {
Registry registro = null;
registro = [Link](5555);
GestorDefiniciones gestorDefiniciones = new
GestorDefiniciones();
[Link]("GestorDefiniciones",
(IGestorDefiniciones)
[Link](gestorDefiniciones, 0));
[Link]("Servidor arrancado...");
} catch (RemoteException ex) {
[Link]();
} catch (AlreadyBoundException ex) {
[Link]();
}
}
public static void main(String[] args) {
registrarGestorDefiniciones();
}
}

package [Link].ap_4_4;

import [Link];
import [Link];

public class GestorDefiniciones implements IGestorDefiniciones {

ArrayList<Elemento> elementos = new ArrayList<Elemento>();


public GestorDefiniciones() {
[Link](new Elemento("Fuselaje","Parte central del
avión"));
[Link](new Elemento("Alerón", "Superficies de mando y
control que se encuentran en los extremos de las alas de los aviones"));
[Link](new Elemento("Timón", "Es un dispositivo
utilizado para dirigir el avión"));
}
@Override
public String obtenerDefinicion(String termino) throws RemoteException {
for (Elemento elemento : elementos) {
if ([Link]().equals(termino)) {
return [Link]();
}
}
return "Termino no en encontrado";
}

private class Elemento {


String termino;

© Ediciones Paraninfo 34
Programación de servicios y procesos

String definicion;
public Elemento(String termino, String definicion) {
[Link] = termino;
[Link] = definicion;
}
public String getTermino() {
return [Link];
}
public String getDefinicion() {
return [Link];
}
}
}

Cliente:

package [Link].ap_4_4;

import [Link];
import [Link];

public interface IGestorDefiniciones extends Remote {


public String obtenerDefinicion(String termino) throws RemoteException;
}

package [Link].ap_4_4;

import [Link];
import [Link];
import [Link];

public class Cliente {


private IGestorDefiniciones gestorDefiniciones = null;

public Cliente() {
try {
Registry registro = [Link]("localhost",
5555);
gestorDefiniciones = (IGestorDefiniciones)
[Link]("GestorDefiniciones");
} catch (Exception e) {
[Link]();
}
}

public static void main(String[] args) {


Cliente cliente = new Cliente();
String resultado;
try {
resultado =
[Link]("Alerón");
[Link](resultado);
} catch (RemoteException e) {
[Link]();
}
}
}

© Ediciones Paraninfo 35
Programación de servicios y procesos

Actividades finales
Actividades de comprobación

4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 4.11 4.12
D C A A A B C D A D C B

Actividades de aplicación
4.13.

1. 100-199. Respuesta informativa.

2. 200-299. Éxito.

3. 300-399. Redirección.
4. 400-499. Error del cliente.

5. 500-599. Error del servidor.


4.14.
HTTPS es la versión segura del protocolo HTTP, ya que cifra toda la información intercambiada
entre cliente y servidor.
4.15.
El Protocolo Simple de Transferencia de Correo (SMTP o Simple Mail Transfer Protocol) es el usado
para el envío de correos electrónicos.
4.16.

Ambos son protocolos para la recepción y lectura de correos. IMAP almacena los correos en el
servidor, por lo que se pueden leer desde varios dispositivos. POP3, en cambio, descarga los
correos al programa cliente y los elimina del servidor, por lo que una vez leídos en un dispositivo
no se pueden leer desde otros.
4.17.
Ambos protocolos sirven para conectarse de manera remota a un ordenador en modo texto, con el
objetivo habitual de adminístralo. En la actualidad se debe utilizar SSH, ya que es un protocolo
seguro que cifra la información transferida entre los ordenadores cliente y servidor, mientras que
Telnet no cifra la información, siendo, por lo tanto, un protocolo inseguro.
4.18.

Los pasos para realizar con esta clase una petición HTTP sin parámetros son los siguientes:

• Creación de URL.

• Apertura de la conexión.

• Configuración de la conexión:
– Método HTTP.

– Tipo de contenido.

© Ediciones Paraninfo 36
Programación de servicios y procesos

– Sistema de codificación.

– Agente de usuario a utilizar.

• Obtención y evaluación del código de respuesta HTTP. Si el código es 200:


– Obtención del objeto InputStream para lectura (si aplica).

 Lectura del stream.

– Obtención del objeto OutputStream para escritura (si aplica).


 Escritura en el stream.

– Desconexión.
4.19.

Para realizar una petición HTTP, se deben seguir los siguientes pasos:

• Crear el objeto HttpClient, indicando versión del protocolo, así como otros datos
opcionales como el comportamiento en caso de que existan redirecciones del servidor.

• Crear el objeto HttpRequest, indicando la URI y los parámetros de la cabecera de la


petición.

• Realizar la petición a través del método send del HttpClient y asignar la respuesta de la
petición a un objeto HttpResponse.

• Procesar la respuesta.
4.20.

1. Commons Email para el envío y recepción de correos electrónicos.


2. Apache Commons Net con implementaciones de los protocolos de internet.

4.21.
Utilizando las librerías de Apache y sin tener en cuenta el control de errores o de rechazos del
servidor, los pasos esenciales para descargar un fichero son los siguientes:

• Instanciar un objeto [Link]();

• Establecer una conexión al servidor (método connect).

• Validarse con las credenciales de la cuenta del usuario en el servidor (método login).

• Crear un objeto OutputStream para almacenar el fichero recuperado.

• A través del método retrieveFile recuperar el fichero y almacenarlo en el fichero local.

• Desconectarse del servidor (método disconnect).

4.22.
Los pasos que seguir para enviar correos electrónicos que solo contienen texto plano son los
siguientes:

• Creación de la sesión, indicando la URL del servidor de SMTP, el puerto, si utiliza SSL y si se
requiere autenticación.

© Ediciones Paraninfo 37
Programación de servicios y procesos

• Creación del mensaje (objeto Message). En este objeto se incluye la dirección de correo del
emisor, la del destinatario, el asunto y el texto del mensaje.

• Establecimiento de la conexión (creación de objeto Transport), indicando el sistema de


transporte.

• Envío del mensaje.

• Cierre de la conexión.
4.23.

La descarga de correos electrónicos desde Java se puede realizar siguiendo estos pasos:

• Creación de la sesión (Session) IMAP, indicando el protocolo, el nombre del host, el puerto,
si utiliza SSL y el servidor de confianza asociado.

• Configuración y obtención del almacén (Store).

• Obtención de la conexión a través del almacén, indicando identificador y contraseña de la


cuenta.

• Obtención de la carpeta que se desea leer.

• Apertura de la carpeta.

• Obtención de los mensajes.

• Procesado de los mensajes.


4.24.

Es el responsable de arrancar el registro de objetos remotos en un host y puerto determinado.

Actividades de ampliación
4.25.

“El servidor HTTP Apache es un servidor web HTTP de código abierto, para plataformas Unix (BSD,
GNU/Linux, etc.), Microsoft Windows, Macintosh y otras.”.

Fuente: Wikipedia.
4.26.
“Un sniffer es una herramienta de software o hardware que permite al usuario supervisar su tráfico
en Internet en tiempo real y capturar todo el tráfico de datos que entran y salen de su equipo.”.

Fuente: Avast.

El tráfico transferido mediante HTTP será legible si es interceptado por el “sniffer”. En cambio, si se
utiliza el protocolo HTTPS, el tráfico transferido podrá ser interceptado, pero no leído, ya que está
cifrado.

4.27.

• 202. Indica que la petición ha sido recibida pero que todavía no se ha actuado al respecto.

• 401. La petición (request) no ha sido ejecutada porque carece de credenciales válidas de


autenticación para el recurso solicitado.

© Ediciones Paraninfo 38
Programación de servicios y procesos

• 402. Es un código de estado de respuesta no estándar que está reservado para uso futuro.
Este código de estado se creó para habilitar el efectivo digital o los sistemas de (micro)
pago e indicaría que el contenido solicitado no está disponible hasta que el cliente realice
un pago.

• 403. Indica que el servidor ha recibido y ha entendido la petición, pero rechaza enviar una
respuesta.

• 501. El servidor no admite la funcionalidad requerida para cumplir con la solicitud.


Fuente de las descripciones: Mozilla MDN.

4.28.

Clientes para Windows:

• FileZilla

• FireFTP

• SmartFTP
4.29.

Pasos:

• Conectarse con el servidor:


o ftp nombre-servidor

• Subir el fichero:
o put rutal-local/nombre-fichero-local

4.30.

• Desde un terminal:

o ssh nombre-usuario@nombre-servidor
o Introducir la contraseña del usuario.

4.31.

“CORBA es un estándar para distribuir objetos a través de redes de modo que las operaciones en
dichos objetos puedan llamarse de forma remota. CORBA no está asociado con ningún lenguaje de
programación en particular y cualquier lenguaje que tenga un enlace CORBA puede utilizarse para
llamar e implementar objetos CORBA. Los objetos se describen en una sintaxis denominada IDL
(Interface Definition Language)”.

Fuente: IBM.
La diferencia frente a RMI es que CORBA no está asociado a ningún lenguaje de programación,
mientras que RMI es específico de Java.

© Ediciones Paraninfo 39
Programación de servicios y procesos

UNIDAD 5.
Técnicas criptográficas para la programación
de aplicaciones seguras

Actividades propuestas
5.1. Implementa un sistema criptográfico basado en el método César.

Solución desarrollada con Java versión 1.8.

La implementación propuesta trabaja sobre un alfabeto de letras mayúsculas, un desplazamiento


de 6 posiciones y la no sustitución de los símbolos punto (.) y coma (,).

package [Link].ap_5_1;

import [Link];
import [Link];

public class CifradorCesar {


private static final int DESPLAZAMIENTO = 6;
private static String alfabeto="ABCDEFGHIJKLMNÑOPQRSTUVWXYZ";
public static StringBuffer leerFichero(String nombreFichero) throws
Exception {
FileReader fr = new FileReader(nombreFichero);
StringBuffer sb = new StringBuffer();
int caracter;
while ((caracter = [Link]())!=-1) {
[Link]((char)caracter);
}
[Link]();
return sb;
}
public static StringBuffer escribirFichero(String nombreFichero,
StringBuffer texto) throws Exception {
FileWriter fw = new FileWriter(nombreFichero);
StringBuffer sb = new StringBuffer();
[Link]([Link]());
[Link]();
return sb;
}
public static StringBuffer cifrar(StringBuffer entrada) {
StringBuffer textoCifrado = new StringBuffer();
for (int i=0;i<[Link]();i++) {
char caracterClaro = [Link](i);
int caracterCifrado =

[Link](([Link](caracterClaro)+DESPLAZAMIENTO)%CifradorC
[Link]());
switch (caracterClaro) {
case ' ':
caracterCifrado = ' ';
break;
case '.':
caracterCifrado = '.';

© Ediciones Paraninfo 40
Programación de servicios y procesos

break;
case ',':
caracterCifrado = ',';
break;
}
[Link]((char)caracterCifrado);
}
return textoCifrado;
}
public static StringBuffer descrifrar(StringBuffer entrada) {
StringBuffer textoDescifrado = new StringBuffer();
for (int i=0;i<[Link]();i++) {
char caracterCifrado = [Link](i);
int posicion = ([Link](caracterCifrado)-
DESPLAZAMIENTO);
if (posicion<0)
posicion=[Link]()+posicion;
int caracterDescifrado =
[Link](posicion);
switch (caracterCifrado) {
case ' ':
caracterDescifrado = ' ';
break;
case '.':
caracterDescifrado = '.';
break;
case ',':
caracterDescifrado = ',';
break;
}
[Link]((char)caracterDescifrado);
}
return textoDescifrado;
}

public static void main(String[] args) {


try {
//CIFRADO
StringBuffer entradaFicheroEnClaro =
[Link]("G:/[Link]");
StringBuffer entradaCifrada =
[Link](entradaFicheroEnClaro);
[Link]("G:/entrada_cifrada.txt",
entradaCifrada);
//DESCIFRADO
StringBuffer entradaFicheroCifrada =
[Link]("G:/entrada_cifrada.txt");
StringBuffer entradaDescifrada =
[Link](entradaFicheroCifrada);
[Link]("G:/salida_descifrada.txt",
entradaDescifrada);
} catch (Exception e) {
[Link]();
}
}
}

© Ediciones Paraninfo 41
Programación de servicios y procesos

5.2. Comprobación de integridad

Solución desarrollada con Java versión 11.

La web de Eclipse ([Link] dispone, junto con la descarga del IDE, de los
resúmenes SHA-512 para poder comprobar la integridad.

Este ejercicio se ejecuta sobre una descarga de Eclipse y su resumen SHA-512 almacenado en un
fichero de texto.

package [Link].ap_5_2;

import [Link];
import [Link];

public class HashManager {


private static final String ALGORITMO = "SHA-512";

public static byte[] getDigest(byte[] mensaje) throws


NoSuchAlgorithmException {
byte[] resumen = null;
try {
MessageDigest algoritmo =
[Link](ALGORITMO);
[Link]();
[Link](mensaje);
resumen = [Link]();
} catch (NoSuchAlgorithmException e) {
throw e;
}
return resumen;
}
}

package [Link].ap_5_2;

import [Link];
import [Link];
import [Link];
import [Link];

public class VerificadorIntegridad {


public static void main(String[] args) {
try {
//Fichero a comprobar
Path path = [Link]("G:/[Link]");
byte[] bytes = [Link](path);
byte[] digest = [Link](bytes);
String strDigest = [Link]("%064x", new BigInteger(1,
digest));
//Digest de referencia
Path pathDigest = [Link]("G:/[Link]");
String strDigestOriginal = [Link](pathDigest);
//Comparación de los resúmenes
if ([Link](strDigestOriginal)) {
[Link]("Integridad verificada.");
} else {

© Ediciones Paraninfo 42
Programación de servicios y procesos

[Link]("Hay un error de integridad.");


}
} catch (Exception e) {
[Link]();
}
}
}

5.3. Almacenamiento seguro

Solución desarrollada con Java versión 11.

El cifrado de ejemplo se realiza de la siguiente manera:

java [Link].ap_5_3.DescifradorAESSimple
G:/[Link] mellamospiderman

El descifrado de ejemplo se realiza de la siguiente manera:

java [Link].ap_5_3.DescifradorAESSimple
G:/[Link] mellamospiderman

package [Link].ap_5_3;

import [Link];
import [Link];
import [Link];

public class AESSimpleManager {


public static Key obtenerClave(String password, int longitud) {
Key clave = new SecretKeySpec([Link](), 0, longitud,
"AES");
return clave;
}

public static byte[] cifrar(byte[] entrada, Key key) throws Exception {


Cipher cipher = [Link]("AES/ECB/PKCS5Padding");
[Link](Cipher.ENCRYPT_MODE, key);
byte[] entradaCifrada = [Link](entrada);
return entradaCifrada;
}

public static byte[] descifrar(byte[] entrada, Key key) throws Exception {


Cipher cipher = [Link]("AES/ECB/PKCS5Padding");
[Link](Cipher.DECRYPT_MODE, key);
byte[] entradaDescifrada = [Link](entrada);
return entradaDescifrada;
}
}

package [Link].ap_5_3;

import [Link];
import [Link];
import [Link];

public class CifradorAESSimple {

© Ediciones Paraninfo 43
Programación de servicios y procesos

public static void main(String[] args) {


try {
final int LONGITUD_BLOQUE = 16;// Expresado en bytes
final String NOMBRE_FICHERO = args[0];
final String PASSWORD = args[1];
byte[] entrada =
[Link]([Link](NOMBRE_FICHERO));
Key clave = [Link](PASSWORD,
LONGITUD_BLOQUE);
byte[] entradaCifrada = [Link](entrada,
clave);
[Link]([Link](NOMBRE_FICHERO + ".cifrado"),
entradaCifrada);
} catch (Exception e) {
[Link]();
}
}
}

package [Link].ap_5_3;

import [Link];
import [Link];
import [Link];

public class DescifradorAESSimple {

public static void main(String[] args) {


final int LONGITUD_BLOQUE = 16;// Expresado en bytes
final String NOMBRE_FICHERO = args[0];
final String PASSWORD = args[1];
try {
Key clave = [Link](PASSWORD,
LONGITUD_BLOQUE);
byte[] entrada =
[Link]([Link](NOMBRE_FICHERO));
byte[] entradDescifrada = [Link](entrada,
clave);
[Link]([Link](NOMBRE_FICHERO + ".descifrado"),
entradDescifrada);
} catch (Exception e) {
[Link]();
}
}
}

5.4. Confidencialidad e identidad

Solución desarrollada con Java versión 11.

Se generan dos pares de firmas con el programa [Link] y renombrándolos


manualmente:

clave_publica_emisor.key
clave_privada_emisor.key
clave_publica_receptor.key
clave_privada_receptor.key

© Ediciones Paraninfo 44
Programación de servicios y procesos

Posteriormente, se ejecuta el programa RSACifrador para generar la salida cifrada, indicando un


fichero de entrada (en este ejemplo un fichero PNG), y finalmente se ejecuta RSADescrifrador
para generar el fichero descifrado (genera un fichero con extensión PNG).

package [Link].ap_5_4;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link].PKCS8EncodedKeySpec;
import [Link].X509EncodedKeySpec;

public class ClavesManager {


public static final String FICHERO_CLAVE_PUBLICA_EMISOR =
"g:/test/clave_publica_emisor.key";
public static final String FICHERO_CLAVE_PRIVADA_EMISOR =
"g:/test/clave_privada_emisor.key";
public static final String FICHERO_CLAVE_PUBLICA_DESTINATARIO =
"g:/test/clave_publica_destinatario.key";
public static final String FICHERO_CLAVE_PRIVADA_DESTINATARIO =
"g:/test/clave_privada_destinatario.key";

public static KeyPair generarClaves() throws NoSuchAlgorithmException {


KeyPairGenerator generador = [Link]("RSA");
[Link](1024);
KeyPair claves = [Link]();
return claves;
}

public static void guardarClaves(KeyPair claves, String


nombreFicheroClavePublica, String nombreFicheroClavePrivada) throws Exception {
FileOutputStream fos = new
FileOutputStream(nombreFicheroClavePublica);
[Link]([Link]().getEncoded());
[Link]();
fos = new FileOutputStream(nombreFicheroClavePrivada);
[Link]([Link]().getEncoded());
[Link]();
}

public static PublicKey getClavePublica(String nombreFicheroClavePublica)


throws Exception {
File ficheroClavePublica = new File(nombreFicheroClavePublica);
byte[] bytesClavePublica =
[Link]([Link]());
KeyFactory keyFactory = [Link]("RSA");
EncodedKeySpec publicKeySpec = new
X509EncodedKeySpec(bytesClavePublica);
PublicKey clavePublica = [Link](publicKeySpec);
return clavePublica;
}

© Ediciones Paraninfo 45
Programación de servicios y procesos

public static PrivateKey getClavePrivada(String nombreFicheroClavePrivada)


throws Exception {
File ficheroClavePrivada = new File(nombreFicheroClavePrivada);
byte[] bytesClavePrivada =
[Link]([Link]());
KeyFactory keyFactory = [Link]("RSA");
EncodedKeySpec publicKeySpec = new
PKCS8EncodedKeySpec(bytesClavePrivada);
PrivateKey clavePrivada =
[Link](publicKeySpec);
return clavePrivada;
}

public static void main(String[] args) {


try {
KeyPair clavesEmisor = generarClaves();
guardarClaves(clavesEmisor, FICHERO_CLAVE_PUBLICA_EMISOR,
FICHERO_CLAVE_PRIVADA_EMISOR);
KeyPair clavesDestinatario = generarClaves();
guardarClaves(clavesDestinatario,
FICHERO_CLAVE_PUBLICA_DESTINATARIO, FICHERO_CLAVE_PRIVADA_DESTINATARIO);
} catch (Exception e) {
[Link]();
}
}
}

package [Link].ap_5_4;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

public class RSACifrador {

private static byte[] cifrar(byte[] entrada, Key clave) throws Exception {


Cipher encryptCipher = [Link]("RSA/ECB/PKCS1Padding");
[Link](Cipher.ENCRYPT_MODE, clave);
byte[] entradaCifrada = [Link](entrada);
return entradaCifrada;
}

public static void main(String[] args) {


PrivateKey clavePrivadaEmisor;
PublicKey clavePublicaDestinatario;
try {
clavePrivadaEmisor =
[Link](ClavesManager.FICHERO_CLAVE_PRIVADA_EMISOR);
clavePublicaDestinatario =
[Link](ClavesManager.FICHERO_CLAVE_PUBLICA_DESTINATARIO);
byte[] bloque = new byte[117];//Los 11 bytes de diferencia
con 128 corresponden al padding.
//CIFRADO CON CLAVE PRIVADA EMISOR
FileInputStream fis = new FileInputStream(new
File("G:/test/[Link]"));

© Ediciones Paraninfo 46
Programación de servicios y procesos

FileOutputStream fos = new FileOutputStream(new


File("G:/test/[Link]"));
while (([Link](bloque)) != -1) {
byte[] bloqueCifradoPrivadaEmisor = cifrar(bloque,
clavePrivadaEmisor);
[Link](bloqueCifradoPrivadaEmisor);

}
[Link]();
[Link]();
//CIFRADO CON CLAVE PUBLICA
FileInputStream fisCifrado = new FileInputStream(new
File("G:/test/[Link]"));
FileOutputStream fosFinal = new FileOutputStream(new
File("G:/test/[Link]"));
while (([Link](bloque)) != -1) {
byte[] bloqueCifradoPublicaDestinatario = cifrar(bloque,
clavePublicaDestinatario);
[Link](bloqueCifradoPublicaDestinatario);
}
[Link]();
[Link]();
} catch (Exception e) {
[Link]();
}
}
}

package [Link].ap_5_4;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

import [Link];

public class RSADescifrador {

private static byte[] descifrar(byte[] mensajeCifrado, Key clave) throws


Exception {
Cipher descifrador = [Link]("RSA/ECB/PKCS1Padding");
[Link](Cipher.DECRYPT_MODE, clave);
byte[] mensajeDescifrado = [Link](mensajeCifrado);
return mensajeDescifrado;
}

public static void main(String[] args) {


PrivateKey clavePrivadaDestinatario;
PublicKey clavePublicaEmisor;
try {
clavePrivadaDestinatario =
[Link](ClavesManager.FICHERO_CLAVE_PRIVADA_DESTINATARIO);
clavePublicaEmisor =
[Link](ClavesManager.FICHERO_CLAVE_PUBLICA_EMISOR);
byte[] bloque = new byte[128];
//DESCIFRADO CON CLAVE PRIVADA DESTINATARIO

© Ediciones Paraninfo 47
Programación de servicios y procesos

FileInputStream fis = new FileInputStream(new


File("G:/test/[Link]"));
FileOutputStream fos = new FileOutputStream(new
File("G:/test/[Link]"));
while (([Link](bloque)) != -1) {
byte[] bloqueDesdifradoPrivadaDestinatario=
descifrar(bloque, clavePrivadaDestinatario);
[Link](bloqueDesdifradoPrivadaDestinatario);
}
[Link]();
[Link]();
//DESCIFRADO CON CLAVE PUBLICA DEL EMISOR
FileInputStream fisCifrado = new FileInputStream(new
File("G:/test/[Link]"));
FileOutputStream fosFinal = new FileOutputStream(new
File("G:/test/[Link]"));
while (([Link](bloque)) != -1) {
try {
byte[] bloqueDescifradoPublicaEmisor =
descifrar(bloque, clavePublicaEmisor);
[Link](bloqueDescifradoPublicaEmisor);
} catch ([Link] e) {
//Error de padding. No es relevante para el
resultado final.
}
}
[Link]();
[Link]();
} catch (Exception e) {
[Link]();
}
}
}

5.5. Firmado digital de documentos.

Solución desarrollada con Java versión 11.

En primer lugar, se debe ejecutar la clase ClavesManager para generar las claves pública y
privada.

Posteriormente, se debe ejecutar la clase FirmaDigitalManager. Este programa crea y almacena la


firma del documento indicada utilizando la clave privada y, posteriormente, comprueba que el
fichero es íntegro y que el emisor es el correcto utilizando la clave pública de este y la firma
almacenada.

package [Link].ap_5_5;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

© Ediciones Paraninfo 48
Programación de servicios y procesos

import [Link].PKCS8EncodedKeySpec;
import [Link].X509EncodedKeySpec;

public class ClavesManager {


private static final String FICHERO_CLAVE_PUBLICA =
"G:/test/clave_publica.key";
private static final String FICHERO_CLAVE_PRIVADA =
"G:/test/clave_privada.key";

public static KeyPair generarClaves() throws NoSuchAlgorithmException {


KeyPairGenerator generador = [Link]("DSA");
[Link](512);// Admite 512, 768 ó 1024
KeyPair claves = [Link]();
return claves;
}

public static void guardarClaves(KeyPair claves) throws Exception {


FileOutputStream fos = new FileOutputStream(FICHERO_CLAVE_PUBLICA);
[Link]([Link]().getEncoded());
[Link]();
fos = new FileOutputStream(FICHERO_CLAVE_PRIVADA);
[Link]([Link]().getEncoded());
[Link]();
}

public static PublicKey getClavePublica() throws Exception {


File ficheroClavePublica = new File(FICHERO_CLAVE_PUBLICA);
byte[] bytesClavePublica =
[Link]([Link]());
KeyFactory keyFactory = [Link]("DSA");
EncodedKeySpec publicKeySpec = new
X509EncodedKeySpec(bytesClavePublica);
PublicKey clavePublica = [Link](publicKeySpec);
return clavePublica;
}

public static PrivateKey getClavePrivada() throws Exception {


File ficheroClavePrivada = new File(FICHERO_CLAVE_PRIVADA);
byte[] bytesClavePrivada =
[Link]([Link]());
KeyFactory keyFactory = [Link]("DSA");
EncodedKeySpec publicKeySpec = new
PKCS8EncodedKeySpec(bytesClavePrivada);
PrivateKey clavePrivada =
[Link](publicKeySpec);
return clavePrivada;
}

public static void main(String[] args) {


try {
KeyPair claves = generarClaves();
guardarClaves(claves);
} catch (Exception e) {
[Link]();
}
}
}

© Ediciones Paraninfo 49
Programación de servicios y procesos

package [Link].ap_5_5;

import [Link];
import [Link];
import [Link];
import [Link];

public class FirmaDigitalManager {


public static void firmar(String nombreFichero, String nombreFicheroFirma)
throws InvalidKeyException, Exception {
byte[] entrada = [Link]([Link](nombreFichero));
Signature signature = [Link]("DSA");
[Link]([Link]());
[Link](entrada);
byte[] firma = [Link]();
[Link]([Link](nombreFicheroFirma), firma);
}

public static boolean comprobarIdentidadEIntegridad(String nombreFichero,


String nombreFicheroFirma) throws InvalidKeyException, Exception {
byte[] entrada = [Link]([Link](nombreFichero));
Signature signature = [Link]("DSA");
[Link]([Link]());
[Link](entrada);
byte[] firmaAlmacenada =
[Link]([Link](nombreFicheroFirma));
return [Link](firmaAlmacenada);
}

public static void main(String[] args) {


try {
firmar("G:/test/[Link]", "G:/test/[Link]");
boolean resultado =
comprobarIdentidadEIntegridad("G:/test/[Link]", "G:/test/[Link]");
[Link](resultado ? "Integridad e identidad
confirmada" : "Integridad e identidad NO confirmada");
} catch (Exception e) {
[Link]();
}
}
}

Actividades finales
Actividades de comprobación

5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.10 5.11
B D B C B D D A B A B

Actividades de aplicación
5.12.
Los sistemas criptográficos son seguros en la medida en que el tiempo que se necesita para
descifrar por “fuerza bruta” el mensaje cifrado sea tanto que no resulte útil realizar este proceso.

© Ediciones Paraninfo 50
Programación de servicios y procesos

Por ejemplo, el cifrado CESAR es descifrable en décimas de segundo, mientras que en su origen era
prácticamente indescifrable. El algoritmo DES, mucho más moderno y complejo, es descifrable en
varios días. En todos los casos, a medida que los computares son más rápidos, el tiempo necesario
para romper un sistema criptográfico es menor, pudiéndolo convertir en poco seguro.
5.13.

Un control de acceso basado en credenciales es un mecanismo que permite restringir el acceso a


un sistema informático a los usuarios que poseen un identificador y una contraseña registrados
previamente.

5.14.
En un sistema de almacenamiento de credenciales utilizando resúmenes (HASH), las contraseñas se
almacenan cifradas con este tipo de algoritmos. De esta manera nunca se almacena la contraseña
en claro, evitando así su exposición si el sistema es atacado.

5.15.
La verificación se realiza de la siguiente manera: el usuario introduce su identificador de usuario y
contraseña; la contraseña se resume con un algoritmo HASH; se lee el resumen de la contraseña
previamente almacenado en una base de datos asociado al identificador del usuario; se comparan
los resúmenes HASH introducidos por el usuario y el almacenado: si ambos coinciden, el usuario
queda verificado.
5.16.

El principal motivo consiste en almacenar una “versión” de la contraseña que, si es obtenida de


manera ilegítima, no permita obtener la contraseña real.
5.17.
El proceso consiste en generar el resumen del mensaje y enviar ambos conjuntamente dentro de
un único mensaje cifrado. En la recepción, se descifra el mensaje completo que contiene el
mensaje original y su resumen. La verificación se realiza generando el resumen del mensaje original
y se compara con el recibido con el mensaje completo. Si son iguales, quiere decir que el mensaje
original no ha sido modificado.

5.18.
Una colisión en un algoritmo HASH surge cuando dos mensajes distintos generan el mismo
resumen. Es muy poco probable que ocurran, pero no imposible. Pese a ello, se admiten en
algunos contextos los algoritmos en los que se ha demostrado que existen colisiones, ya que las
posibilidades de que estos se produzcan (por ejemplo, dos contraseñas que generan el mismo
resumen) son tan remotas que prácticamente es imposible.
5.19.

La firma digital permite asociar la identidad del emisor a un documento, aunque este no esté
cifrado, garantizando así el origen del mensaje.

5.20.

Cualquier transferencia de datos está sujeta a la posibilidad de que sea interceptada. Si esto
ocurre, la información obtenida de manera ilegítima debe ser ilegible (estar cifrada). Los protocolos
TCP y HTTP no cifran la información, por lo que son protocolos no seguros salvo que se tomen las
medidas oportunas.

© Ediciones Paraninfo 51
Programación de servicios y procesos

5.21.

No es infrecuente que un sistema sea atacado y obtenidos los resúmenes de las contraseñas
almacenadas en él. Si esto ocurre, los atacantes tratarán obtener contraseñas en claro que generen
los resúmenes recuperados. Para ello necesitan, fundamentalmente, tiempo. Si antes de que haya
transcurrido el tiempo necesario para obtener la contraseña en claro esta ha sido cambiada, el
riesgo de un acceso ilegítimo al sistema se minimiza.

5.22.
Un sitio web que proporciona la contraseña en claro cuando sus usuarios se la solicitan no utiliza
un algoritmo HASH para su almacenamiento. Por lo tanto, la debe almacenar en claro o cifrada con
un algoritmo reversible. En ambos casos es un riesgo muy alto que se debe evitar.
5.23.

Un mensaje cifrado con la clave privada del emisor y, posteriormente, con la clave pública del
receptor, solo puede ser descifrado con la clave privada del receptor (confidencialidad) y,
posteriormente, con la clave pública del emisor (identidad).

5.24.

• Modificar la contraseña dos veces al año.

• La contraseña debe tener una longitud mínima de 20 caracteres.

• Debe contener letras minúsculas, mayúsculas, dígitos numéricos y caracteres especiales.

Actividades de ampliación

5.25.

En este artículo de Wikipedia se hace una extensa descripción del cifrado César:
[Link]

Para descifrar un texto con este sistema hay que conocer el número de desplazamientos con el que
se cifró el texto.

• Dado el siguiente alfabeto: abcdefghijklmnñopqrstuvwxyz ,.


• Dado el siguiente número de desplazamientos: 3
• Dado el siguiente texto cifrado: sdp

El texto en claro se obtiene desplazando hacia la izquierda cada carácter del texto cifrado en el
alfabeto tantas posiciones como el número de desplazamientos. Por lo tanto, el descifrado es el
siguiente:

sp
da
pn

Siendo, por lo tanto, la palabra en claro: pan

© Ediciones Paraninfo 52
Programación de servicios y procesos

5.26.

Utilizando el mismo texto de entrada, se obtienen los siguientes resultados:

URL: [Link]
MD5 HASH generado: d1921aa0ca3c1146a01520c04e6caa9e

URL: [Link]
MD5 HASH generado: d1921aa0ca3c1146a01520c04e6caa9e

URL: [Link]
MD5 HASH generado: d1921aa0ca3c1146a01520c04e6caa9e

5.27.

El cifrado de bloques cifra por bloques de datos de longitud fija.


El cifrado de flujo cifra (normalmente) byte a byte.

5.28.

Según se indica en la web [Link]:

“La Ley 39/2015, de 1 de octubre, del Procedimiento Administrativo Común de las


Administraciones Públicas, en su artículo 10, Sistemas de firma admitidos por las Administraciones
Públicas, indica que, dentro del contexto en el que nos encontramos relativo a los empleados
públicos, la firma electrónica:

• Se puede usar para la identificación y autenticación del ejercicio de la competencia de la


Administración u Órgano Administrativo al que pertenece el empleado. En este aspecto, se
indica que se considerarán válidos a efectos de firma electrónica:

o Sistemas de firma electrónica cualificada y avanzada basados en certificados


electrónicos cualificados de firma electrónica expedidos por prestadores incluidos
en la “Lista de confianza de prestadores de servicios de certificación”. Teniendo en
cuenta el contexto en el que estamos, de empleado público, el certificado usado
para la firma, debe disponer de la atribución de empleado público.
o Cualquier otro sistema que las Administraciones Públicas consideren válido en los
términos y condiciones que se establezca, siempre que cuenten con un registro
previo como usuario que permita garantizar su identidad, previa autorización por
parte de la Secretaría General de Administración Digital del Ministerio de Política
Territorial y Función Pública, que solo podrá ser denegada por motivos de
seguridad pública, previo informe vinculante de la Secretaría de Estado de
Seguridad del Ministerio del Interior. La autorización habrá de ser emitida en el
plazo máximo de tres meses. Sin perjuicio de la obligación de la Administración
General del Estado de resolver en plazo, la falta de resolución de la solicitud de
autorización se entenderá que tiene efectos desestimatorios.

Se puede validar la firma de un documento en el siguiente sitio web:

• [Link]

© Ediciones Paraninfo 53
Programación de servicios y procesos

5.29.

AES-CBC: En el modo CBC (cipher-block chaining), antes de ser cifrado, a cada bloque de texto se le
aplica una operación XOR con el bloque previo ya cifrado. De este modo, cada bloque cifrado
depende de todos los bloques de texto claros usados hasta ese punto. Además, para hacer cada
mensaje único se debe usar un vector de inicialización en el primer bloque.

AES-CFB: El modo CFB (cipher feedback), en su forma más simple, utiliza toda la salida del cifrado
de bloque. En esta variación, es muy similar a CBC, convierte un cifrado de bloque en un cifrado de
flujo de sincronización automática.

AES-OFB: El modo OFB (output feedback) emplea una clave para crear un bloque pseudoaleatorio
que es operado a través de XOR con el texto claro para generar el texto cifrado. Requiere de un
vector de inicialización que debe ser único para cada ejecución realizada.

Fuente: Wikipedia

5.30.

Este artículo publicado en la web de Google contiene una explicación bastante extensa y
comprensible sobre el modo en el que funciona este protocolo:

[Link]

5.31.

“La infraestructura de JAAS puede dividirse en dos componentes principales: un componente de


autenticación y un componente de autorización. El componente de autenticación de JAAS
proporciona capacidad para determinar con fiabilidad y seguridad quién está procesando el
código Java en ese momento, independientemente de si el código se ejecuta como una aplicación,
un applet, un bean o un servlet. El componente de autorización de JAAS complementa la
infraestructura de seguridad existente de Java 2, proporcionando los medios para evitar que se
procese código Java para realizar tareas confidenciales, en función del origen del código (como se
hace en Java 2) y en función de la persona que se autentique.”

Fuente: IBM

5.32.

“La extensión Java Secure Socket (JSSE) permite comunicaciones seguras en Internet. Proporciona
un marco y una implementación para una versión Java de los protocolos SSL, TLS y DTLS e incluye
funcionalidad para el cifrado de datos, autenticación de servidor, integridad de mensajes y
autenticación de cliente opcional.”

Fuente: Oracle

© Ediciones Paraninfo 54

También podría gustarte