Você está na página 1de 34

11-12-2018 Práctica 4

Diseño y desarrollo de interfaces y


protocolos de red (implantación
remota mediante sockets)

Pablo Villarino Viloria


EN COLABORACIÓN CON JOSÉ ANTONIO VILLALÓN MARTÍNEZ
ÍNDICE
1. Conceptos clave ...................................................................................................................2
2. Objetivo ................................................................................................................................2
3. Resumen ..............................................................................................................................3
4. Solución propuesta...............................................................................................................4
4.1. Funcionalidad de los usuarios.......................................................................................5
4.1.1. Funcionalidades del usuario cliente......................................................................5
4.1.2. Aplicación del servidor .........................................................................................5
4.1.3. Aplicación eco .......................................................................................................6
4.1.4. Funcionalidades de la aplicación FTP ....................................................................6
4.2. Definición del interfaz de comunicación ......................................................................7
4.3. Funcionalidad de las entidades ....................................................................................8
4.3.1. Funcionalidades asociadas al interfaz (comunicación entidad-usuario) ...............8
4.3.2. Funcionalidades asociadas al protocolo (comunicación entre entidades) ............9
4.4. Estructura del mensaje ...............................................................................................10
4.5. Protocolos de comunicación ......................................................................................11
4.5.1. Protocolo 1 .........................................................................................................12
4.5.2. Protocolo 2 .........................................................................................................13
5. Programación de la solución propuesta .............................................................................14
5.1. Entidad B ....................................................................................................................14
5.2. Entidad A ....................................................................................................................20
5.3. Usuario 1 (cliente) ......................................................................................................22
5.4. Usuario 2 ....................................................................................................................25
5.5. Makefile del equipo servidor ......................................................................................29
5.6. Makefile del equipo cliente ........................................................................................30
5.7. EJEMPLOS DE EJECUCIÓN ...........................................................................................31
1. Conceptos clave

- Socket de red: Se trata de un extremo interno a través del cual enviar o recibir datos
dentro de un nodo en una red de computadoras. Un proceso puede referirse a un socket
utilizando un descriptor de socket. Primero, un proceso solicita que se cree un socket, y
se devuelve un descriptor de socket tal que el proceso pueda identificarlo. Entonces, el
proceso pasa el descriptor del socket a la cola del protocolo cuando desea enviar o
recibir datos utilizando este socket. Existen varios tipos de sockets, que se detallan a
continuación, y su uso durante la práctica se discutirá en la solución que se ha
propuesto.

- Datagram sockets: También conocidos como sockets sin conexión; utilizan el protocolo
UDP.

- Stream sockets: También conocidos como sockets orientados a la conexión, utilizan


comúnmente el protocolo TCP.

- Raw sockets: Típicamente utilizados en routers u otro equipamiento de red. La capa de


transporte es ignorada y las cabeceras del paquete se hacen accesibles a la aplicación.

- Puerto de red: Es un extremo de la comunicación. Las conexiones físicas e inalámbricas


terminan en puertos de dispositivos hardware. Al nivel de software, dentro de un
sistema operativo, un puerto es un constructo lógico que identifica un proceso
específico o un tipo de servicio de red.

2. Objetivo
Aclarar conceptos sobre las características y posibilidades que tienen los protocolos de
comunicación entre procesos remotos y una arquitectura en la que el protocolo de cada nivel
permite al usuario servicios adicionales a los de su bloque funcional. Se utilizan los servicios del
nivel de transporte de TCP/IP, programando entidades funcionales basadas en sockets.
3. Resumen
Se plantea la implementación de transferencia de archivos de texto ASCII entre dos procesos
de usuario en máquinas diferentes de forma remota. Ambos procesos han de hacer uso de
funciones de interfaz con las entidades de nivel inferior, que ejecutan un protocolo que
proporciona los servicios de comunicación necesarios.

Figura 1. Esquema de la comunicación que se establece.

Respecto a lo programado en la práctica 1, se observan los siguientes cambios:

- Implementación de la conexión entre entidades a través de sockets.


- Implementación del protocolo ftp.
- Modificación de las cabeceras en la interfaz para introducir el puerto.
4. Solución propuesta
El desarrollo de esta práctica es muy similar al que se realizó anteriormente en la primera
práctica, con ciertas modificaciones. Es por esto que, a lo largo de la explicación de la solución
que proponemos, nos basaremos en la práctica anterior, y puntualizaremos las diferencias que
se produzcan.

Nuevamente, se tendrían cuatro procesos, esta vez repartidos en dos máquinas diferentes,
un proceso de usuario y uno de entidad en cada una de ellas.

Las entidades forman parte de la arquitectura de red, mientras que los usuarios son externos
a la arquitectura y podrían utilizar diferentes aplicaciones de usuario (en nuestro caso, eco y
ftp). Es por ello por lo que las entidades deberán realizar las tareas más generales, enfocadas
hacia la comunicación entre equipos, de forma que sean útiles para el funcionamiento con otras
aplicaciones y usuarios. Por esta misma razón, los usuarios deberán aportar la información a las
entidades requerida al formato con el que funciona la entidad.

La comunicación entre estos cuatro procesos será de forma que estén asociados dos a dos:
Usuario 1 y Entidad A conforman al cliente, mientras que Usuario 2 y Entidad B conformarán al
servidor. El mecanismo IPC que comunica a los usuarios y entidades entre sí, es decir el interfaz
de este nivel de la arquitectura es una cola de mensajes, mientras que entre entidades se
comunicarán a través de sockets locales, dado que ambos equipos se encuentran en la misma
subred, estableciendo la comunicación a través del protocolo UDP, de modo que será necesario
introducir el puerto en la cabecera del mensaje. La entidad del equipo servidor, B, abrirá el
datagram socket y quedará a la espera de la conexión de una máquina cliente (entidad A).

Figura 2. Mecanismos de comunicación entre los procesos cliente y servidor.

En las entidades existirá un script ejecutándose en bucle comprobando si existe algún


nuevo mensaje en la cola de mensajes enviado por parte del usuario y con destino a la entidad.
4.1. Funcionalidad de los usuarios

Es el nivel de comunicación más alto y no forma parte de la arquitectura de red


propiamente, sino que es el programa que ejecuta y con el que se comunica el usuario que utiliza
el equipo.

Como se ha establecido anteriormente, existirán dos tipos diferentes de usuarios: un


servidor y uno o más clientes.

4.1.1. Funcionalidades del usuario cliente


El cliente (usuario 1) se comunicará con la entidad A, y será el que le indique a la entidad
los parámetros de la comunicación: el PID del usuario destino y el del propio usuario 1, el modo
de transmisión de los datos, protocolo a utilizar, y la funcionalidad que se ejecutará (eco o ftp).

Es interesante tener en cuenta, que al iniciar el usuario1, se elige la aplicación con la que
va a funcionar durante la ejecución y por lo tanto la forma de proceder en la comunicación
variará. No obstante, durante la comunicación se pueden definir unos procedimientos
generales:

 Apertura de la cola de mensajes: Será el mecanismo de transmisión de datos desde la


aplicación hasta la entidad, debe haber sido creada por la entidad y el usuario accederá
a ella para aportar datos.
 Transmisión de mensajes a la entidad: Dependiendo del tipo de aplicación que este
ejecutando el usuario 1, enviará una cadena de datos a la entidad a través de la cola de
mensajes. La cadena de datos que envía a la entidad contará con diferentes partes entre
las que podemos diferenciar: los PID origen y destino, el protocolo utilizado, el tipo de
aplicación que ejecuta el usuario 1 y el mecanismo de comunicación (sockets o memoria
compartida). Durante la transmisión puede darse el caso (resultaría muy extraño que
ocurriese en el usuario 1) de que los datos que se quieren enviar sean demasiado
grandes para la cola, en cuyo caso será necesario indicar cuantos bloques deben
realizarse para enviar la cadena total.
 Recepción de mensajes: El usuario debe ser capaz de recibir los mensajes enviados por
la entidad A una vez esta ha recibido respuesta por parte del servidor. Asimismo, será
el encargado de gestionar la información que este contiene, si el mensaje es demasiado
grande para la cola y se debe recibir en diferentes bloques debe ser capaz de
reconstruirlo, y mostrar por pantalla los datos del mensaje.
 Cierre de la comunicación: Se cerrará el acceso a la cola de mensajes cuando sea
solicitado por el usuario.

4.1.2. Aplicación del servidor


El usuario servidor (2) deberá comunicarse con la entidad B. Únicamente ejecuta una
aplicación que contiene un bucle iterativo que se encuentra en un estado de espera continúa
esperando la llegada de nuevos mensajes por parte de la entidad B. Una vez recibida la
cadena de datos por parte de la entidad B deberá determinar el tipo de aplicación para la
cual es utilizado el mensaje (eco o ftp) y actuará en consecuencia de ello.
- Si la aplicación es de eco: sacará por pantalla el mensaje recibido y de manera
simultánea, editará la información de origen y destino del mensaje y lo enviará a la
entidad B para que el mensaje vuelva al usuario cliente.
- Si por la contra la aplicación seleccionada es de tipo ftp, el programa recibe los datos
por parte de la entidad B y realiza el tratamiento de los diferentes comandos y
devolviendo los resultados obtenidos.

4.1.3. Aplicación eco


Esta aplicación es conceptualmente sencilla, ya que únicamente trata de enviar una cadena
de caracteres desde el usuario 1 hasta el usuario 2, e inmediatamente devolver la cadena de
vuelta al usuario 1.

Aunque el concepto es sencillo, la comunicación entre los usuarios no es directa, sino que
debe tratarse a través de niveles inferiores en la arquitectura de red. En este caso, en primer
lugar, se pasará el mensaje del usuario origen a la entidad con la que se comunique mediante el
interfaz, luego las entidades se comunicarán utilizando el nivel de comunicación y tras ello la
entidad receptora lo pasará al usuario destino utilizando el interfaz del nivel. Tras recibirlo, si es
el usuario 2, deberá devolver el mensaje de una forma similar, pero en sentido inverso.

Este tipo de aplicaciones son de utilidad para comprobar la correcta comunicación entre
equipos ya que en ambos usuarios se obtendrá por pantalla la misma cadena de caracteres.

4.1.4. Funcionalidades de la aplicación FTP


Cuando en el usuario 1 ejecutamos la aplicación ftp, tenemos diferentes comandos y
posibilidades para obtener información, recibir ficheros, o descubrir la zona donde estamos
trabajando. Es una aplicación más compleja tanto teóricamente como a la hora de implementar,
ya que el usuario 2 deberá tratar las peticiones de diferente manera dependiendo del comando,
no como el caso de eco donde simplemente tenía que cambiar origen y destino.

A continuación, se verán los comandos que puede gestionar este modo de comunicación:

4.1.4.1. DIR
Se trata de un comando homólogo a la instrucción ‘ls’ en un sistema operativo UNIX. Cuando
se invoca se muestran todos los archivos y directorios contenidos dentro de la ruta actual. No
se contempla un control de este comando puesto que si la ruta especificada se encuentra vacía
no se devuelve ningún nombre de archivo.

Ya que en un directorio puede haber un número de archivos que únicamente se encuentra


limitado por la capacidad de almacenamiento del equipo es posible que se exceda el límite de
longitud de la cola de mensaje de modo que se procedería a su división en bloques para ser
enviado a la entidad.
4.1.4.2. CD (Change Directory)
Este comando se emplea para cambiar el directorio de trabajo, se incluye sobre el campo de
datos la ruta del directorio sobre el que el usuario se quiere situar. La aplicación ftp recibe el
mensaje y ejecuta una serie de acciones para enviar al usuario 2 la petición.

Si el directorio existe, se ejecutará el cambio de directorio devolviendo al cliente la situación


actual. Si por el contrario el directorio especificado no existe no se podrá realizar el cambio de
directorio, dando como resultado la señalización de un error al cliente.

4.1.4.3. GET
Este comando se emplea para realizar la transferencia de un fichero del servidor al cliente.
Por limitaciones de la aplicación solo se pueden transferir ficheros ASCII en el sentido servidor-
> cliente, siempre y cuando no exista ningún fichero en el directorio con ese mismo nombre. En
cuyo caso se abortará la transferencia, pues no queremos perder información propia.

Cuando se finaliza el envío se muestra un mensaje de confirmación y el tiempo empleado


en realizar la transferencia.

4.1.4.4. QUIT
Este comando se emplea para romper la comunicación y finalizar los mecanismos de
comunicación existentes del usuario 1 con los demás procesos. No se tiene ningún tipo de
confirmación de que se haya finalizado la comunicación, pero al finalizar utilizando señales del
equipo se da por hecho que el tratamiento es correcto.

4.2. Definición del interfaz de comunicación


El interfaz de comunicación entre los usuarios y las entidades es el elemento conceptual que
nos permite la transmisión de mensajes entre estos elementos. Se estructura en torno al
mecanismo IPC de las colas de mensaje utilizándolo como medio para la transmisión de la
información.

Realmente, el interfaz nos especifica la información que debe transmitirse entre usuario y
entidad, así como la estructuración de esta información y tamaño del mensaje. Atendiendo a las
diferentes etapas que se suceden podemos distinguir entre unos campos fijos en el interfaz y
unos campos variables:

- Campos fijos: como campos fijos en la comunicación nos encontramos con que en todo
mensaje que se inicie o tenga destino en un usuario debe emitir el PID del usuario
origen, PID del usuario destino, longitud de la cadena que se desea transmitir, son la
cabecera de los mensajes entre usuarios y entidades.
- Campos dependientes de la longitud del mensaje: la longitud del mensaje es crucial
para el interfaz ya que si los datos que se desean transmitir tienen una longitud
suficiente como para poder ser enviados a través de la cola junto con los parámetros
fijos de la cabecera se enviarán con ella. En caso de ser demasiado grandes para
enviarlos de este modo, se deberá dividir el envío, enviando en el primer mensaje la
cadena de datos vacía y posteriormente se envían mensajes íntegramente de datos.
- Campos dependientes de la comunicación: a esta clasificación se corresponden
aquellos parámetros que se añaden en ciertas ocasiones. Estos son:
o Mensaje de usuario 1 a entidad A: se debe añadir además de los elementos fijos,
debe indicar los parámetros de inicialización: si se ejecuta eco o ftp, ipc o
sockets, protocolo y el puerto de comunicación (novedad de comunicación con
sockets).
o Mensaje de entidad B a usuario 2: en este campo se añade, además de la parte
fija de la cabecera, la aplicación que solicita servicios (eco o ftp).

En cuanto al tamaño máximo que se puede transmitir a través de la cola de mensajes


es un parámetro fijo que en nuestro diseño e implementación es de 2KB pero podría cambiarse.
El tamaño de los mensajes del usuario por teclado no está limitado y por lo tanto se necesita un
mecanismo para transmitir los mensajes por partes: transmisión dividida de datos.

Si el tamaño de los datos que deseamos transmitir es demasiado grande para nuestro
tamaño máximo de cola menos el de las cabeceras, será necesario dividir el mensaje, el
mecanismo es el siguiente:

1. Se comprueba si la cadena de datos tiene un tamaño menor al tamaño de la cola menos


el de las cabeceras. Si lo es, se envía juntamente con la cola.
2. Si el tamaño ha resultado demasiado grande para el primer tipo de envió, se envía la
cabecera vacía y posteriormente se envían tantos mensajes de datos completos como
sea necesario para transmitir todo el mensaje, conociéndose este en el elemento
receptor al haber recibido en la cabecera la longitud del mensaje.
3. En la recepción se lleva un procedimiento paralelo, si se recibe la cabecera vacía
sabemos que a continuación recibiremos una serie de cadenas de datos hasta acumular
el tamaño total que se nos ha indicado.

4.3. Funcionalidad de las entidades


Al contrario que las aplicaciones de usuario, las entidades tendrán aplicaciones simétricas,
es decir se tratará de entidades pares o gemelas. Invocan primitivas para permitir la
comunicación con los usuarios a través de un interfaz y se comunicarán entre sí mediante
datagram sockets.

Adicionalmente serán las encargadas de regular la comunicación, realizando un control


sobre los protocolos, errores y tiempos. Son las encargadas de comprobar si se ha introducido
algún tipo de información por parte del usuario, teniendo que estar revisando de manera
continua el canal de comunicación con él. Siendo esto especialmente importante para la entidad
A, ya que el momento en el que el usuario 1 introduce algún mensaje por teclado es algo
completamente estocástico.

4.3.1. Funcionalidades asociadas al interfaz (comunicación entidad-usuario)

1. Creación de la cola de mensajes: las entidades serán las encargadas de crear el canal de
comunicación entre la propia entidad y el usuario colindante. Para una transmisión
correcta se añadirá un identificador a la cola creada ya que no será extraño contar con
más de un cliente transmitiendo información de manera simultánea.
2. Abrir la cola de mensajes: con las colas tipo POSIX que emplearemos en la
implementación no será necesario abrir las colas en las entidades, ya que al crearse se
consideran preparadas para el envío.
3. Recepción de mensajes: los mensajes enviados por parte del usuario llegan a la cola de
mensajes que funcionará como una pila FIFO, no obstante, como la entidad se
encontrará en un proceso de espera del mensaje raramente se acumularán mensajes ya
que al recibir la información empezará a procesarla.
4. Enviar mensajes: la entidad enviará hacía el usuario los mensajes recibidos por parte de
otra entidad. Tras recibir el mensaje de la entidad le da el formato correcto y envía los
datos por la cola.
5. Control de las longitudes de mensaje: tanto en envío como recepción de mensajes, la
entidad debe controlar que lo que se envía o recibe tiene un tamaño correcto o
demasiado grande para el tamaño permitido por el interfaz. En caso de querer recibir
un mensaje mayor que el tamaño máximo se debe conocer la longitud para saber en
cuantos bloques debe enviarse.
6. Cerrar comunicación: una vez se haya finalizado la comunicación y ya no se vaya a
utilizar más, se cerrará el canal de comunicación con el usuario.
7. Borrar la cola de mensajes: para evitar ocupar demasiados recursos del sistema con
colas de mensaje que no tienen utilidad, será necesario eliminarlas una vez se ha
cerrado el canal.

4.3.2. Funcionalidades asociadas al protocolo (comunicación entre entidades)

1. Creación de sockets: se asigna un descriptor de fichero y se enlazan a un espacio de


direcciones los sockets que se vayan a utilizar para disponer de ellos para la
comunicación en la entidad B. Estos sockets son de tipo ‘datagram’ y utilizan el
protocolo de comunicación UDP para la comunicación entre dos máquinas distintas
en una subred.
2. Vinculación de sockets: Tras la creación de los sockets, la entidad B queda en modo
de escucha esperando la conexión por parte de la otra entidad. Si la conexión es
exitosa puede procederse al envío y recepción de información entre entidades.
3. Gestión de la fragmentación: en caso de que el mensaje tenga un tamaño de datos
superior al límite establecido, se deberá fragmentar y enviar por partes.
4. Gestión de la unión de mensajes: en caso de haberse recibido un mensaje con el
indicador de fragmentación, la entidad receptora deberá reconstruirlo para su
correcto tratamiento. Se trata de un proceso donde adquiere especial relevancia
que la reconstrucción se realice en el orden adecuado. Por ello se van concatenando
las cadenas de datos recibidas, consiguiendo así el mensaje completo.
5. Envío de mensajes: se trata del intervalo más crítico del proceso: Partiendo de la
información recibida por parte del usuario, la entidad correspondiente debe
generar una serie de cabeceras necesarias para la comunicación entre entidades.
Una vez se haya convertido el mensaje a la estructura fija definida por el protocolo
se comparte dicha estructura entre las entidades a través de los sockets creados.
6. Recepción de mensajes: La entidad procede a leer las cabeceras para así determinar
si va dirigido hacia ella. En cuyo caso recibe el mensaje, lo almacena para enviárselo
al usuario posteriormente.
7. Comprobación del PID destino: la entidad es la encargada de vigilar que el PID
destino que va en la cabecera es el correspondiente a la aplicación de esa entidad,
de no ser así se habrá producido algún tipo de error y se mostrará un mensaje
cerrando la comunicación.
8. Comprobación de errores: durante la recepción del mensaje es necesario saber si
este ha sido transmitido correctamente o por lo contrario ha habido perdida de
información. Para ello se realiza una checksum en la entidad destino guardando el
resultado en una parte de la cabecera y posteriormente se repite el proceso en la
entidad receptora, si el resultado no es el mismo es porque se ha dado un error y se
requiere el reenvío del mensaje, en caso de repetirse el error se abortará la
transmisión de este mensaje pasando al siguiente.
9. Cerrar la comunicación: una vez se finalice el uso de los sockets, deberán cerrarse
para liberar los recursos y los puertos de comunicación del sistema.

4.4. Estructura del mensaje


Los mensajes enviados a través de los sockets, comunicando a las entidades, deben
seguir siempre una estructura y tamaño fijo, que se define según las necesidades del protocolo.
Esta estructura básica se ha definido en la solución propuesta como la siguiente:

1. Patrón: Longitud de un byte. Es la cabecera distintiva del mensaje, indicando que a


continuación se encuentra un campo de datos predeterminado, que deberá ser
decodificado a lo largo de la estructura de red para extraer el mensaje. Ya que no es un
campo con más implicaciones que la simple detección del mensaje, se ha escogido el
patrón 10101010 (170 en decimal).
2. Destino: Se trata del PID del proceso destinatario del mensaje, pudiendo ser éste el
servidor o el cliente, en función del sentido en el que se produce la comunicación. En
esta ocasión, barajamos dos opciones posibles: Una de ellas, la más simple, trataría de
emplear cuatro bytes de forma que el PID introducido por el usuario es tratado como
una variable Int (4 bytes), o, sabiendo que el número de PID máximo por defecto en
UNIX es 215, podría pasarse el argumento a binario y ocupar únicamente dos bytes.
3. Origen: Similar al caso anterior, es el PID del proceso origen y es obtenido mediante la
función getpid() en el propio proceso. El tratamiento que se puede realizar al tamaño
del campo es similar al del campo Destino.
4. Protocolo escogido: Al poseer únicamente dos opciones, el campo tendrá una longitud
de un bit, 0 para un protocolo unidireccional y 1 para un protocolo bidireccional.
5. Eco / FTP: Se introduce un bit para seleccionar el tipo de proceso cliente con el que se
trabaja, 0 en caso de una funcionalidad de eco, y 1 en caso de un protocolo de
transferencia de archivos (FTP). Si se deseara añadir más funcionalidades a la utilidad en
un futuro, podría usarse una variable tipo char para la selección.
6. IPC/Sockets: en este campo se indica el canal de comunicación entre las entidades,
puede ser mediante memoria compartida o bien mediante sockets. Se reservan 4 bits.
Si es memoria compartida se guardará ‘0100’ y en caso de ser sockets será ‘1111’
7. Tipo: Define el tipo del mensaje dependiendo de su función. Tiene una longitud de dos
bits debido a que se poseen tres posibilidades. El campo tendrá el valor 00 si se trata de
un mensaje de datos, un valor 01 si se trata de un mensaje ACK (acknowlegdement),
confirmación de recepción de un mensaje (únicamente para el protocolo 2) y un valor
10 para un mensaje de error (únicamente para el protocolo 2).
8. Dígito de control: a la hora de enviar un mensaje existe la posibilidad de que se pierda
información durante la transferencia. Por ello se introduce este campo. En la entidad
origen se realiza una checksum y se almacena en esta zona el resultado. Posteriormente
en la recepción se realiza la misma suma del contenido del mensaje y comprobará si
difiere en valor. La longitud del campo es de 32 bits.
9. Fragmentación: Campo que indica si el mensaje enviado ha sido dividido en varias
partes o se transmite íntegro de una sola vez, para ello aquí dice el número de partes en
las que se divide. El tamaño del campo es de 32 bits.
10. Longitud: Especifica el tamaño de la cadena de caracteres enviada a través del campo
Datos. Tiene una longitud de 16 bits. Realmente únicamente serían necesarios 13 bits,
se deja un margen por si en algún momento posterior se desea aumentar el tamaño de
los paquetes hasta 216 .
11. Datos: Contiene la información del mensaje enviado por el usuario. Tiene una longitud
de 2032 bytes.

En total, el mensaje completo tendría una longitud de 2048 bytes, o 2Kbytes.

PAT DEST ORG PROT E/FTP IPC/S TYPE CNTRD FRAG LEN DATA
8bit 16bit 16bit 1bit 1bit 4bit 2bit 32bit 32bit 16bit 2032B

Se considera que la solución adoptada es bastante favorable ya que contamos con un


número entero de octetos y además se ha conseguido concatenar en un octeto (8bits)
cuatro campos como son: protocolo, modo eco/ftp, mecanismo de comunicación y tipo
de mensaje. Esta determinación a la hora de programar permitirá concatenar en una
variable char estos cuatro campos, consiguiendo así permitir un campo de datos mayor.

4.5. Protocolos de comunicación


Los protocolos son los encargados de garantizar la compatibilidad entre las funciones del
mismo nivel de una arquitectura de red. Las funciones y tareas deben cumplir siempre una serie
de exigencias fijas. Por todo esto, surge la necesidad de definir un protocolo que establezca las
reglas de funcionamiento de cada entidad gemela, asegurando así la compatibilidad.

Los puntos a tener en cuenta en el diseño de un protocolo serán los siguientes:

- Tipo de información que se intercambia (Señales de error, alarmas, mensajes, …)


- Formato de cada tipo de mensaje (Cabeceras, longitudes de campo, fragmentación y
reconstrucción, errores, …)
- Gestión de errores (Reenvío de mensajes, watchdog, captura de excepciones, …)

Los protocolos se definirán por niveles, comunicando entidades en un mismo nivel, siendo
transparentes para el resto de los niveles. Asimismo, también es ajena a los protocolos la
información que contiene el mensaje que tratan.

Este método de funcionamiento permite a los usuarios tener la sensación de entablar una
comunicación directa con otros usuarios.
En esta práctica se definirán dos protocolos que se implementarán en ambas entidades:

4.5.1. Protocolo 1

El primero de los protocolos de comunicación diseñados es un protocolo unidireccional y sin


control de errores. En una aplicación real, sería orientado a la velocidad de transmisión, dándose
por hecho que el modo de comunicación es lo suficientemente compacto como para transmitir
correctamente el mensaje.

La unidireccionalidad supone que, en la transmisión de mensajes, la entidad origen del


mensaje no tiene ningún medio para conocer si el mensaje ha llegado al destino, y de no ser así,
de conocer el error que se ha producido durante la comunicación. De la misma manera, tampoco
podrá conocer si el mensaje transmitido ha llegado de forma íntegra a su destinatario, que será
el que tenga que comprobar si se ha producido algún error.

Si la comunicación es fallida y el mensaje se ha transmitido con errores o simplemente no


se ha transmitido, la entidad emisora no tiene ningún medio para conocerlo y será la receptora
la que realice la gestión del mensaje comprobando su coherencia y en caso de una transmisión
correcta se enviará al usuario correspondiente y en caso contrario se desechará el mensaje.

Todos los mensajes enviados a través de este protocolo son del tipo 0 (datos).

Figura 3: Esquema del modo de transmisión de mensajes en protocolo 1


4.5.2. Protocolo 2

Este segundo protocolo implementa una comunicación bidireccional, con conexión.

El objetivo principal de la bidireccionalidad es conseguir un control y gestión de errores


detallado para asegurar una conexión fiable.

Cada entidad transmite el mensaje de datos (tipo 0) correspondiente y queda a la espera de


un mensaje de confirmación tipo 1 (ACK) o en caso de una transmisión incorrecta, un mensaje
de tipo 2 (error). Las entidades que han recibido la notificación de que su mensaje ha sido
erróneo, reenvían el mensaje de nuevo esperando que en esta ocasión el mensaje de
confirmación sea tipo 1. En caso de repetirse la recepción de un mensaje tipo 2, puede repetirse
el proceso, pero en caso de contar más de 3 errores para un mismo mensaje, se deshecha el
mensaje considerando que existe un error en la generación de este y pasando a la siguiente
petición del usuario.

Otro caso de transmisión fallida podría ser cuando enviamos un mensaje y no recibimos
ningún tipo de respuesta al cabo de un tiempo. Esto puede darse a múltiples causas relacionadas
con errores en el canal de comunicación o bien que la entidad destino se haya desconectado.
Las entidades deben estar preparadas para tratar este tipo de fallos ya que, de no hacerlo, se
quedarán bloqueadas esperando un mensaje de confirmación que nunca llegara. Uno de los
métodos más efectivos para detectar estos errores es el empleo de un “perro guardián” o
“Watchdog”, se trata de un proceso que comprueba iterativamente que el usuario destino se
encuentra activo, en caso de no detectarlo se inicializa una cuenta atrás y en caso de no detectar
signo alguno de que el otro proceso está activo, provoca el final de la comunicación cerrando el
canal.

Figura 4: Ciclo de transmisión de mensajes correctos en protocolo 2


5. Programación de la solución propuesta
5.1. Entidad B
Se trata de la entidad de la zona del servidor. Se comunica con el usuario 2 a través de una
cola de mensajes, y con la entidad A por medio de sockets. La entidad B crea un socket y lo deja
en modo de escucha esperando a la conexión de la otra entidad.

En lo que se refiere a programación la entidad B, al igual que la A, cuenta con una


estructura de datos, que en C++ es tratada como una clase, y en la que podemos encontrar cada
uno de los campos de la estructura de mensaje que se ha definido anteriormente.
typedef struct structDatos//Estructura definida por los protocolos
{
uint8_t patron;
uint16_t pid_dest;
uint16_t pid_orig;
char p_e_t_i; // Donde voy a meter protocolo/eco o ftp/tipo/ ipc o
sockets
unsigned int ctrlDig;
int nFrag;
uint16_t longitud;
char datos[TAM_DATOS];
} structDatos;

Con la estructura podemos comprobar como el número de bytes que enviamos es de 16


bytes de cabeceras y 2032 de datos.

De especial interés en la programación es el campo donde se guarda: protocolo, modo


eco/ftp, mecanismo de comunicación y tipo de mensaje (char p_e_t_i). Como se ha mencionado
anteriormente introducimos en un ‘char’ 4 campos diferentes, por lo que resultó necesario crear
una función para construir este carácter.
char build_p_e_t_i(unsigned char protocolo, unsigned char eco_ftp,
char ipc_mod, int tipo)
{
char p_e_t_i;//Variable donde se van a guardar protocolo, modo
eco/ftp, como se comunican, y tipo de mensaje
//El metodo de guardarlo va a ser mediante mascaras y
desplazamientos
// para asegurarnos de no sobreescribir se llena de izquierda a
derecha

p_e_t_i=p_e_t_i & 0B00000000;//Nos aseguramos que todos los bits


del char esten a 0

if(protocolo)
{
p_e_t_i=p_e_t_i | 0B10000000;
}
if(eco_ftp)
{
p_e_t_i=p_e_t_i | 0B01000000;
}
if(ipc_mod=='m')
{
p_e_t_i=p_e_t_i | 0B00010000;
}
else if(ipc_mod=='s')
{
p_e_t_i=p_e_t_i | 0B00111100;
}
if(tipo==1)
{
p_e_t_i=p_e_t_i | 0B00000001;
}
else if(tipo==2)
{
p_e_t_i=p_e_t_i | 0B00000010;
}
return p_e_t_i;
}

o Recepción del mensaje de la entidad A

o Protocolo 2: Con este protocolo una vez comprobadas estas cabeceras se envía
un mensaje tipo 1 o tipo 2 a la entidad A indicando que la recepción se ha
producido, y tras ello se continua el proceso para enviar la información al
usuario 2.
Figura 5.Gestión de recepción con protocolo 2

o Protocolo 1: en caso de tratarse del protocolo unidireccional directamente se procede


a preparar el mensaje para el envío al usuario 2.

Figura 6.Gestión de recepción con protocolo 1


o Envío de mensaje al usuario 2
Una vez almacenado el mensaje y comprobadas ciertas características, se gestiona la
división del mensaje al usuario en caso de ser necesaria y se le pasa al usuario 2 la información
requerida, eliminando aquí las cabeceras que no son necesarias para la comunicación. En
concreto al usuario 2 se le manda:

 Aplicación que lo solicita (eco o ftp)


 PID origen y PID destino
 Campo de datos

Figura 7. Envío del mensaje al usuario 2

o Recepción del mensaje del usuario 2


El usuario 2 procesa el mensaje que le hemos pasado en el instante anterior y genera una
respuesta que nos devuelve empleando los mismos campos con los que le enviamos la
información. Aquí se observa uno de los momentos más críticos, y este es la posibilidad de que
el mensaje del usuario 2 sea de un tamaño superior al de la cola. Se gestiona de la siguiente
manera:

El tamaño del mensaje se envía en un primer envío junto a los campos de destino y origen.

1- En caso de ser un mensaje de un tamaño suficiente como para entrar en la cola de


mensajes junto con esos campos, se enviará con ellos.
2- En caso de ser un mensaje mucho más grande, se necesitará enviar el mensaje en
intervalos y por ello se enviará la cadena de datos vacía indicando así que a continuación
se reciben solo datos hasta completar el tamaño indicado en primera instancia.

Figura 8.Recepción del mensaje.

Figura 9.Tratamiento de la recepción según el tamaño del mensaje.

o Construcción y envío del mensaje a la entidad A


Una vez hemos recibido el mensaje de respuesta generado por el usuario A, almacenamos
la información en la estructura local, construyendo de esta manera los campos del mensaje.

Figura 10.Generación de los datos de la estructura.


 Protocolo 1: el mensaje ha sido enviado y pasamos a recibir nuevos mensajes.
if(protRecibo==0)//protocolo 1
{
if(write(sock,msg,4080)<0)
perror("Writing error");
cout<<"Se ha escrito en memoria protocolo =0
"<<endl;
}

 Protocolo 2: se espera el mensaje de confirmación de la entidad A, en caso de


ser tipo 1 (ACK) se pasa a recibir nuevos mensajes. En caso de ser tipo 2 (NACK)
se reenvía el mensaje, en caso de producirse más de 3 reenvíos erróneos, se
aborta la respuesta ya que es errónea.

Figura 11.Envío de mensaje con protocolo 2


5.2. Entidad A
Como se ha dicho a lo largo de la práctica, las entidades A y B son entidades pares, luego los
diferentes procesos que hemos definido para la entidad B son válidos para esta entidad, por ello
realizaremos una breve explicación comentando las pequeñas diferencias, obviando en esta
ocasión mostrar fragmentos de código, pues el tratamiento es similar al anteriormente visto.

En primer lugar, debemos decir que esta entidad es la encargada de generar la cola de
mensajes que la comunica con el usuario 1 y del mismo modo debe crear un socket que se
conectará a la entidad B.

Al igual que la otra entidad, se encuentra programada con un bucle infinito. En este caso
lo primero que se comprueba es la recepción de mensajes por parte del usuario 1, esperando
hasta que llegue un mensaje y comenzar la comunicación con la otra entidad. Las etapas podrían
ser las siguientes:

 Recepción de mensaje del usuario 1 y construcción del mensaje para transmitir


a la entidad B
Se recibe el mensaje del usuario donde encontramos PID origen y destino, protocolo
utilizado, aplicación eco o ftp, y un campo de datos. Con ello se accede va rellenando la
estructura para el envío por memoria compartida. Se introduce el patrón, se realiza la checksum
y se rellenan los demás campos.

En esta ocasión presentaremos aquí el caso de la reconstrucción de los mensajes del


usuario en caso de que su longitud fuese superior a la de la cola.

Figura 12.Reconstrucción de los mensajes.


 Envío del mensaje a la entidad B

Una vez escrito el mensaje, dependiendo del protocolo que se esté utilizando se espera un
mensaje de confirmación (protocolo 2) o bien se espera directamente la respuesta (protocolo
1). Un aspecto que no se ha mostrado aún es el del cálculo del número de fragmentos del
mensaje. Para ello se muestra a continuación el procedimiento a seguir una vez se ha recibido
de la cola el campo de datos.

Figura 13. Cálculo de los fragmentos del mensaje.

Posteriormente el proceso de envío mostrado en la explicación de la entidad B se


introduce dentro del bucle for, de manera que se envíen tantos mensajes como sean necesarios
para completar la recepción completa de los datos. Esta posibilidad de fragmentar rara vez se
va a dar con la aplicación eco, ya que el tamaño de datos es 2032bytes, será más útil en la
transmisión de ficheros.

 Recepción del mensaje de la entidad B y envío de la información al usuario 1

Una vez la entidad B procesa la petición realizada nos emite una respuesta que
almacenamos. Una vez realizadas las comprobaciones oportunas y emitido el mensaje de
confirmación en caso de ser protocolo 2. Se procede a enviar la información al usuario 1.

De manera resumida este es el método de proceder de la entidad A, realmente su


funcionamiento es similar al de la entidad B donde se puede encontrar una descripción más
pormenorizada.
En la recepción de un mensaje fragmentado, se recibe el número de fragmentos del mensaje
y se entra en un bucle for donde se van almacenando cada uno de los mensajes, se muestra a
continuación como ejemplo la recepción de un mensaje fragmentado utilizando el protocolo 1.

Figura 14: Ejemplo de recepción de mensaje fragmentado

5.3. Usuario 1 (cliente)


El usuario cliente está programado de manera que al iniciarse se deben meter los
argumentos que indiquen: PID destino, método de comunicación (IPC o sockets), puerto,
protocolo empleado y aplicación que se quiere usar, por defecto eco.

Una vez contemplados los argumentos de entrada, se pasa a explicar la programación del
usuario. Se han programado las dos aplicaciones (eco y ftp) aunque la segunda de ellas es menos
compacta debido a su mayor complicación.

La aplicación eco tiene una programación bastante sencilla:

En primer lugar, hay que indicar que se han reservado dos cadenas de caracteres para cerrar
la aplicación ECO o cerrar todos procesos. De esta manera encontramos una forma de salir de
la aplicación, ya que de no tener estos mecanismos no se podría salir de una forma directa de la
aplicación.

Posteriormente el funcionamiento de la aplicación eco se basa en recibir una cadena de


caracteres por pantalla y almacenarlos en el vector de datos que se enviará a la entidad A a
través de la cola de mensajes.

Posteriormente se espera su recepción y se muestra la cadena de datos que se recibe. A


continuación, se muestra un fragmento de su código.
Envío:

Figura 14. Envío de un mensaje al usuario

Recepción:

Figura 15.Recepción del usuario

La aplicación eco, cuenta con dos cadenas clave que se utilizan para cerrar su propia
ejecución (-cerrarECO-), y otra para finalizar todas las aplicaciones, eliminando también los
mecanismos IPC (-salir-). Para esta última funcionalidad se emplean señales definidas por el
usuario, las envía usuario1 y son tratadas en el resto de los programas en ejecución.
La aplicación ftp por su parte cuenta con 4 comandos diferentes, luego lo primero que
hace al iniciarse la aplicación es preguntar el comando deseado, en caso de no ser uno de los
comandos contemplados solicita de nuevo el comando diciendo que el solicitado no existe.

Una vez introducido el comando y con él, la acción que se pretende conseguir, se delega
la construcción de la cadena de datos en un switch donde dependiendo del comando deseado
se crea la cadena de datos que se enviará a la entidad A. En la recepción se muestra el resultado
obtenido.

Figura 16. Selección de comando FTP.

Figura 17. Gestión del comando ‘quit’

Figura 18.Gestión del comando ‘cd’

Figura 19. Gestión del comando 'dir'


Figura 20. Gestión del comando 'get'

5.4. Usuario 2
El usuario 2 tiene una única aplicación como servidor, inicializándose sin ningún tipo de
comando adicional. El usuario 2 parte de la espera de un mensaje por parte de la cola de
mensajes de la entidad B. Como anteriormente se explicó se le pasan varios campos, pero el más
relevante y el que primero comprueba la aplicación es si el servicio solicitado es eco o ftp.
- Procesamiento de petición eco: en caso de haberse solicitado una petición eco, lo único
que realiza el usuario 2 es mostrar el mensaje en pantalla, cambiar el origen y el destino
y reenviarlo.

Figura 21. Procesamiento de la petición de eco

- Procesamiento petición ftp: este caso es bastante más crítico y la complejidad del código
es muy superior al caso anterior. Hay que detectar el comando solicitado y según el
realizar una serie de acciones para conseguir y devolver la información solicitada,
gestionando cada comando por separado. A continuación se muestra como se procede:

Figura 22. Procesamiento del comando solicitado.

Figura 23. Gestión del comando 'quit'


Figura 24. Gestión del comando ‘cd’

Figura 25. Gestión del comando ‘dir’.


Figura 26. Gestión del comando ‘get’.
5.5. Makefile del equipo servidor
Se muestra el archivo makefile para el servidor, que en nuestro se ejecutará en Carpanta.
Con este programa se compilan y crean los ejecutables de las entidades y los usuarios, además
de borrar las colas de mensajes en caso de que no se haya cerrado correctamente durante la
ejecución.

Figura 27. Makefile del equipo servidor.


5.6. Makefile del equipo cliente
Por último, se muestra el makefile del equipo cliente, que se ejecutará en Saturno. En este
equipo únicamente se compila para hacer posible la comunicación mediante sockets ya que este
era el objetivo principal de la práctica y programarlo de otra forma complicaba en gran medida
el código.

Figura 28. Makefile del equipo cliente.


5.7. EJEMPLOS DE EJECUCIÓN

A continuación, se muestran ejemplos de ejecución de la aplicación ftp, aunque se muestra la


ejecución con el protocolo 1, el protocolo 2 es igualmente funcional y valido. Como se ha
comentado a lo largo de la práctica, en Carpanta hemos implementado el servidor y en Saturno
el cliente. La elección ha venido determinada por simple funcionalidad ya que inicialmente
Saturno nos daba error al conectar como servidor y decidimos cambiarlo. El código del servidor
esta preparado para recibir mensajes desde el puerto 7546 (el asignado a mi cuenta).

Entidad A Entidad B

Usuario 2
Usuario 1

Figura 29: Conexión

Figura 30: Ejemplo de ejecución de Dir


En la figura 30 se puede observar el resultado de la ejecución de dir, tras gestionarse la petición
y extraerse el nombre de los ficheros, en el usuario 1 se listan los ficheros que se encuentran en
la carpeta del usuario 2.

Figura 31: Ejecución de cd ./dire

Partiendo de los resultados de la figura 30, observamos la existencia del directorio dire,
mediante la ejecución del comando cd ./dire , en la figura 31 y remarcado podemos observar
como al usuario 1 acaba llegando un mensaje de confirmación de que se ha cambiado el
directorio correctamente

Figura 32: Ejemplo de ejecución get prueba.txt


En la figura 32 nos encontramos dentro del directorio dire alojado en carpanta (servidor),
dentro de este directorio conocemos la existencia de un fichero de texto llamado “prueba.txt”,
con el comando get prueba.txt se llega observa que se guarda el fichero con su contenido en
Saturno, tal y como se puede ver en la figura 33.

Figura 33: Resultado de realizar el comando get prueba.txt

Figura 34: Ejemplo del comando quit

Por último, se muestra el resultado del comando quit, con él se consigue cerrar las aplicaciones
de usuario de servidor y cliente, tal y como se muestra en la figura 34.

Você também pode gostar