158
 Diego Rodríguez-Losada González Pablo San Segundo Carrillo Programación Avanzada, Concurrente y Distribuida 

47508930-ProgramacionAvanzada

  • Upload
    vanvan

  • View
    8

  • Download
    0

Embed Size (px)

Citation preview

  • 5/21/2018 47508930-ProgramacionAvanzada

    1/157

    Diego Rodrguez-Losada GonzlezPablo San Segundo Carrillo

    Programacin Avanzada,

    Concurrente y Distribuida

  • 5/21/2018 47508930-ProgramacionAvanzada

    2/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 2

    Universidad Politcnica de Madrid -UPM

  • 5/21/2018 47508930-ProgramacionAvanzada

    3/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 3

    Universidad Politcnica de Madrid -UPM

    PRLOGO 7

    PARTE I. Desarrollo de una aplicacin distribuida yconcurrente en LINUX

    11.. EDICIN, COMPILACIN, Y DEPURACIN DE UNA APLICACIN C/C++ BAJO LINUX 11

    1.1. INTRODUCCIN 111.2. LOGIN EN MODO TEXTO 121.3. MANEJO DE ARCHIVOS Y DIRECTORIOS EN MODO TEXTO. 121.4. EL EDITOR DE TEXTO 151.5. DESARROLLO C/C++EN LINUX EN MODO TEXTO 151.6. EL PROCESO DE CREACIN DE UN EJECUTABLE 161.7. LAS HERRAMIENTAS DE DESARROLLO 171.8. EL COMPILADOR GCC 171.9. MAKEFILE Y LA HERRAMIENTA MAKE 19

    1.10. TIPOS DE ERROR 201.11. DEPURACIN DE LA APLICACIN. 211.12. CREACIN DE UN SCRIPT 221.13. DESARROLLO EN UN ENTORNO GRAFICO 231.14. EJERCICIO PRCTICO 241.15. EJERCICIO PROPUESTO 25

    22.. INTRODUCCIN A LOS SISTEMAS DISTRIBUIDOS. COMUNICACIN POR SOCKETS 27

    2.1. OBJETIVOS 27

    2.2. SISTEMA DISTRIBUIDO 282.3. SERVICIOS DE SOCKETS EN POSIX 292.3.1 PROGRAMA CLIENTE 302.3.2 SERVIDOR 322.4. ENCAPSULACIN DE UN SOCKET EN UNA CLASEC++ 352.4.1 ENVO DE MLTIPLES MENSAJES 362.4.2 CONEXIONES MLTIPLES. 382.5. ESTRUCTURA DE FICHEROS 422.6. TRANSMITIENDO EL PARTIDO DE TENIS 442.6.1 CONEXIN 442.6.2 ENVO DE DATOS 45

    2.7. EJERCICIOS PROPUESTOS 45

  • 5/21/2018 47508930-ProgramacionAvanzada

    4/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 4

    Universidad Politcnica de Madrid -UPM

    33.. COMUNICACIONES Y CONCURRENCIA 47

    3.1. INTRODUCCIN 473.2. REQUISITOS 493.3. FUNCIONAMIENTO DE GLUT 49

    3.3.1 LANZANDO UN HILO 503.4. ESTRUCTURA DEL SERVIDOR 513.5. MLTIPLES CONEXIONES SIMULTANEAS 523.6. MOSTRAR LOS CLIENTES CONECTADOS 533.7. RECEPCIN COMANDOS MOVIMIENTO 553.8. GESTIN DESCONEXIONES 563.9. FINALIZACIN DEL PROGRAMA 563.10. EJERCICIO PROPUESTO 57

    44.. COMUNICACIN Y SINCRONIZACIN INTERPROCESO 59

    4.1. INTRODUCCIN 594.2. EL PROBLEMA DE LA SINCRONIZACION 604.3. COMUNICACIN INTERPROCESO 614.4. TUBERAS CON NOMBRE 624.5. MEMORIA COMPARTIDA 644.6. EJERCICIOS PROPUESTOS 68

    PARTE II. Programacin avanzada

    55.. PROGRAMACIN DE CDIGO EFICIENTE 73

    5.1. INTRODUCCIN 735.2. MODOS DE DESARROLLO 775.3. TIPOS DE OPTIMIZACIONES 775.4. VELOCIDAD DE EJECUCIN 785.5. ALGUNAS TCNICAS 795.5.1 CASOS FRECUENTES 795.5.2 BUCLES 805.5.3 GESTIN DE MEMORIA 835.5.4 TIPOS DE DATOS 855.5.5 TCNICAS EN C++ 865.6. CASOS PRCTICOS 875.6.1 ALGORTMICA VS.MATEMTICAS 875.6.2 GENERACIN DE NMEROS PRIMOS 885.6.3 PRE-COMPUTACIN DE DATOS 905.7. OBTENIENDO PERFILES (PROFILING)DEL CDIGO 935.8. CONCLUSIONES 95

  • 5/21/2018 47508930-ProgramacionAvanzada

    5/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 5

    Universidad Politcnica de Madrid -UPM

    66.. SERIALIZACIN DE DATOS 97

    6.1. INTRODUCCIN 976.2. REPRESENTACIN OBJETOS EN MEMORIA 1026.3. SERIALIZACIN EN C 103

    6.3.1 CON FORMATO (TEXTO) 1046.3.2 SIN FORMATO (BINARIA) 1046.4. SERIALIZACIN EN C++ 1076.4.1 CON FORMATO (TEXTO) 1086.4.2 SIN FORMATO (BINARIA) 1116.5. CONCLUSIONES 112

    77.. BSQUEDAS EN UN ESPACIO DE ESTADOS MEDIANTE RECURSIVIDAD 113

    7.1. INTRODUCCIN 113

    7.2. BSQUEDA PRIMERO EN PROFUNDIDAD 1157.2.1 TERMINOLOGA 1167.2.2 ESTRUCTURAS DE DATOS 1167.2.3 ANLISIS 1177.3. BSQUEDA PRIMERO EN ANCHURA 1197.4. METODOLOGA GENERAL DE RESOLUCIN DE UN PROBLEMA DE BSQUEDA MEDIANTE COMPUTACIN 1207.5. IMPLEMENTACIN DE UNA BSQUEDA DFSMEDIANTE RECURRENCIA 1217.5.1 LA PILA DE LLAMADAS 1227.5.2 BSQUEDA DFSCOMO RECURSIN 124

    88.. EJECUCIN DISTRIBUIDA DE TAREAS 133

    8.1. INTRODUCCIN 1338.2. EL PROBLEMA DE LAS N-REINAS 1348.2.1 HISTORIA 1348.2.2 CARACTERSTICAS 1358.2.3 2ESTRUCTURAS DE DATOS 1368.3. IMPLEMENTACIN CENTRALIZADA 1388.3.1 DESCRIPCIN 1398.3.2 ESTRUCTURAS DE DATOS 1408.3.3 CONTROL DE LA BSQUEDA 141

    8.3.4 ALGORITMO DE BSQUEDA 1458.4. IMPLEMENTACIN DISTRIBUIDA 1478.4.1 ARQUITECTURA CLIENTE-SERVIDOR 1478.4.2 PROTOCOLO DE COMUNICACIN 1488.4.3 IMPLEMENTACIN DEL CLIENTE 1488.5. IMPLEMENTACIN DEL SERVIDOR 1538.5.1 COMUNICACIN CON EL CLIENTE 153

  • 5/21/2018 47508930-ProgramacionAvanzada

    6/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 6

    Universidad Politcnica de Madrid -UPM

  • 5/21/2018 47508930-ProgramacionAvanzada

    7/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 7

    Universidad Politcnica de Madrid -UPM

    PRLOGOGeneralmente la formacin en informtica de un ingeniero (industrial,

    automtica, telecomunicaciones o similar) comienza por la programacin estructurada,en lenguajes como C o Matlab, y luego se complementa con Programacin Orientada aObjetos (POO) e Ingeniera del Software, con Anlisis y Diseo Orientados a Objetos,UML, etc.

    Sin embargo, existen una serie de tcnicas y tecnologas software que escapan

    del alcance de los anteriores cursos. La programacin de tareas concurrentes, lossistemas distribuidos, la programacin de cdigo eficiente o algortmica avanzada sontemas que quedan a menudo relegados, y sin embargo son muy necesarios en tareasde ingeniera industrial, comunicaciones y similares.

    Este libro trata de cubrir dichos aspectos, de una manera prctica y aplicada. Laprimera parte desarrolla una aplicacin grfica distribuida: un tpico juego decomputador en red. En esta aplicacin se requiere el uso de comunicaciones por red(con sockets), as como la utilizacin de tcnicas de programacin concurrente conmulti-proceso y multi-hilo, de una manera que esperamos que sea atractiva y

    motivadora para el lector. El desarrollo se realiza en Linux (Posix), presentando unaintroduccin al manejo bsico, desarrollo y depuracin con herramientas GNU comog++, make y gdb. El cdigo de soporte para estos captulos se encuentra enwww.elai.upm.es

    La segunda parte cubre algunos tpicos genricos avanzados como laprogramacin de cdigo eficiente, la serializacin de datos, la recurrencia o lacomputacin distribuida, tpicos que muchas veces estn ntimamente relacionadoscon los anteriores.

    http://www.elai.upm.es/http://www.elai.upm.es/http://www.elai.upm.es/
  • 5/21/2018 47508930-ProgramacionAvanzada

    8/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 8

    Universidad Politcnica de Madrid -UPM

  • 5/21/2018 47508930-ProgramacionAvanzada

    9/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 9

    Universidad Politcnica de Madrid -UPM

    Parte I. Desarrollo deuna aplicacindistribuida y

    concurrente en LINUX

  • 5/21/2018 47508930-ProgramacionAvanzada

    10/157

  • 5/21/2018 47508930-ProgramacionAvanzada

    11/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 11

    Universidad Politcnica de Madrid -UPM

    11.. EDICIN,COMPILACIN,YDEPURACIN DE UNA APLICACIN C/C++

    BAJO LINUX

    1.1.INTRODUCCINEn este primer tema realizamos una aproximacin al SO operativo linux, y

    fundamentalmente al desarrollo de aplicaciones en C/C++, desarrolladas, depuradas yejecutadas en un computador con Linux. Aunque el objetivo de este curso es elaprendizaje de programacin concurrente y sistemas distribuidos, en este primer temanos ceiremos al trabajo de desarrollo convencional en linux, para aprender tanto eldesarrollo sin interfaz grafica de ventanas, como algunas de las herramientas graficas.Tambin se manejaran algunos comandos o mandatos bsicos de linux para crear,editar y manejar archivos, y se introducir el uso de las herramientas de desarrollobsico como son gcc, g++, make y gdb.

    Este tema comienza por la descripcin de los comandos bsicos para trabajaren modo texto, para despus desarrollar y depurar una pequea aplicacin ejemplo enmodo texto. Por ultimo, se trabajara en modo grafico, completando un cdigo yaavanzado para terminar con el juego del tenis que funcione en modo local, para dos

    jugadores, esto es, los dos jugadores utilizan el mismo teclado y la misma pantalla.

  • 5/21/2018 47508930-ProgramacionAvanzada

    12/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 12

    Universidad Politcnica de Madrid -UPM

    Figura 1-1. Objetivo del captulo: Desarrollo del juego del Tenis en modo local

    1.2.LOGIN EN MODO TEXTOAunque el computador arranque en modo grafico, la primera parte de esta

    prctica se va a desarrollar en modo texto. Para ello cmbiese del terminal grafico alprimer terminal de texto, mediante la correspondiente combinacin de teclas(Ctrl+Alt+F1)

    Entrar en la cuenta de usuario correspondiente. Consejo: Aunque dispongas de

    la contrasea de administrador es absolutamente recomendable no utilizarla paratrabajar normalmente. En caso de que seas el administrador del sistema, crea unacuenta de usuario normal para realizar la prctica.

    Probar a realizar el login en los distintos terminales virtuales (saliendo luegocon el comando exit de los que no se vayan a utilizar)

    1.3.MANEJO DE ARCHIVOS Y DIRECTORIOS EN MODO TEXTO.Para familiarizarse con el manejo de archivos y directorios en linux se va a crear

    la siguiente estructura de archivos, en la que los archivos de texto contienen el textoHola que tal:

    /home/usuario/

    |------------->carpeta1

    | |------->subcarpeta11

    | | |------->archivo11.txt

    | |------->archivo1.txt

    |------------->carpeta2|------->archivo2.txt

  • 5/21/2018 47508930-ProgramacionAvanzada

    13/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 13

    Universidad Politcnica de Madrid -UPM

    Utilizar y explorar los comandos y opciones siguientes:

    Tabla 1-1. Comandos bsicos consola linux

    Comando Accin Opciones

    pwd muestra el directorio actual

    ls muestra el contenido del

    directorio actual

    -a (muestra todos los

    archivos, incluidos ocultos)

    l, muestra detalles de los

    archivos

    mkdir [directorio] crea el directorio con el

    nombre dado

    cd [ruta] cambia al directorio que

    indica la ruta

    correspondientecat [fichero] concatena el fichero a salida

    estndar

    - significa entrada

    estndar. Para crear un

    archivo se puede

    redireccionarla de la

    siguiente forma cat -

    >nombre_fichero.txt

    chmod usuario+permiso

    [fichero]

    cambia los permisos

    (r=read, w=write,

    x=execute) a usuario (a=all,

    o=others, u=user, g=group)

    rm [archivo] Borra el archivo -r = borra recursivamente el

    directorio seleccionado

    (OJO, usar con mucha

    precaucin)

    cp [origen] [destino] Copia el archivo o archivos

    origen al destino

    seleccionado

    mv [origen] [destino] Mueve el archivo o archivos

    origen al destino

    seleccionado

    Tambin sirve para

    renombrar un archivo

    rmdir [directorio] Borra el directorio, que

    previamente debe estar

    vaco

    exit o logout Termina la sesin (salir)

  • 5/21/2018 47508930-ProgramacionAvanzada

    14/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 14

    Universidad Politcnica de Madrid -UPM

    Tabla 1-2. Caracteres comodin (wildcars)

    * Una cadena de caracteres

    cualesquiera

    ? Un carcter cualquiera

    Tabla 1-3. Directorios importantes

    . Directorio actual Opcion

    .. Directorio superior

    cd Vuelve al directorio inicial

    raiz del usuario

    \home\usuario

    Tabla 1-4. Ayuda

    Comando funcin Opcionman [comando] Muestra las paginas man

    del comando seleccionado

    comando Muestra una ayuda breve

    del comando al que se

    aplica

    --help -h

    info [comando] Muestra las paginas info

    del comando al que se

    aplica

    whatis [comando] Busca en una base de datos

    descripciones cortas del

    comando

    Tabla 1-5. Ayudas del shell bash

    Teclas funcin Opcion

    Tab Autocompletar, rellena el

    nombre del comando o

    archivo segn las posibles

    opciones que conozcaTab+Tab Muestra todas las opciones

    que tiene autocompletar

    Arrow Up Sube en la historia de

    comandos

    Arrow Down Baja en la historia de

    comandos

    Una vez creada la estructura, quitar el permiso de escritura al archivo11.txt eintentar concatenarle la cadena Muy bien gracias. Volver a reinstaurar el permiso y

    repetir la operacin.

    Borrar primero el archivo2.txt y luego la carpeta2. Borrar a continuacin todo elrbol de la carpeta1.

  • 5/21/2018 47508930-ProgramacionAvanzada

    15/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 15

    Universidad Politcnica de Madrid -UPM

    1.4.EL EDITOR DE TEXTOSe va a utilizar el editor vi o vim para crear y modificar los archivos de

    cdigo fuente necesarios, por ser el editor incluido por defecto en linux, y del queconviene tener al menos unas nociones bsicas que nos permitan sacarnos de unapuro en caso de necesidad.

    Para crear un archivo nuevo en la carpeta actual teclear:

    vi [fichero]

    Si el archivo no existe lo crea y si existe lo abre para editar.

    vi tiene dos modos de funcionamiento:

    Modo comando: cada tecla realiza una funcin especfica (borrar,mover) Este es el modo por defecto al arrancar el editor.

    Modo insercin: cada tecla inserta el carcter correspondiente en eltexto. Para entrar en este modo se debe pulsar la tecla i y para salir del se debe pulsar Esc.

    Operaciones bsicas

    :wgraba el archivo al disco

    :qsalir de editor

    :q!salir del editor sin grabar los cambios (forzar la salida)

    :wqgrabar y salir

    1.5.DESARROLLO C/C++EN LINUX EN MODO TEXTOVamos a construir una aplicacin con dos ficheros fuente, que muestre por

    pantalla una tabla de senos de varios ngulos. Para ello seguiremos los siguientespasos:

    1. Verificar mediante pwd que se encuentra en el directorio de usua rioadecuado

    2. Crear una carpeta pract1 que va a contener los archivos de la prctica,y cambiar el directorio actual a la misma

    3. Crear los archivos fuente siguientes:

  • 5/21/2018 47508930-ProgramacionAvanzada

    16/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 16

    Universidad Politcnica de Madrid -UPM

    /** archivo: principal.c

    */

    #include #include misfunc.h

    int main(void){int i;for(i=0;i

  • 5/21/2018 47508930-ProgramacionAvanzada

    17/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 17

    Universidad Politcnica de Madrid -UPM

    Figura 1-2. Proceso de creacin de un ejecutable

    1.7.LAS HERRAMIENTAS DE DESARROLLOSe van a utilizar a partir de ahora los compiladores y distintas herramientas.

    Puede ser que en su sistema linux no vengan instaladas por defecto. Si ese es el caso,debe de instalarlas. El gestor de aplicaciones o paquetes de su distribucin le ayudara ahacerlo. En cualquier caso es importante remarcar que las herramientas de desarrollo

    utilizadas son GNU, con licencia GPL, es decir son gratuitas y su instalacin estotalmente legal. Si utiliza un sistema basado en Debian, la forma ms sencilla deinstalar estas herramientas seria:

    sudo apt-get install build-essential

    1.8.EL COMPILADOR GCCEl compilador utilizado en linux se llama gcc. La sintaxis adecuada para la

    compilacin y linkado del anterior programa seria:

    gcco prueba principal.c misfunc.c lm

    Fichero fuente A.c, .cpp

    Fichero fuente B.c, .cpp

    COMPILADOR

    LINKADO

    Bibliotecaesttica A.a

    Bibliotecaesttica B.a

    EJECUCION

    Ejecutable

    Modulo objeto A.o

    Modulo objeto B.o

    Libreras dinmicas.so

    Proceso enejecucin

  • 5/21/2018 47508930-ProgramacionAvanzada

    18/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 18

    Universidad Politcnica de Madrid -UPM

    Ejecutar el programa mediante:

    ./prueba

    Figura 1-3. Salida por pantalla de nuestra aplicacin

    Comprobar con lsal los permisos de ejecucin del archivo

    ls -al

    La sintaxis es la siguiente:

    gcco [nombre_ejecutable] [ficheros_fuente] l[librera]

    Realmente este comando ha realizado la compilacin y el linkado todo seguido,de forma transparente para el usuario. Si se desea desacoplar las dos fases se realizade la siguiente manera:

    Compilacin fichero a fichero :

    gccc principal.c

    gccc misfunc.c

    (Ntese que aqu no es necesario especificar que se va a linkar con la libreramatemtica, ya que solo se esta compilando en un modulo objeto .o)

    Compilacin de varios ficheros en la misma lnea

    gccc principal.c misfunc.c

    Enlazado

    gcco prueba principal.o misfunc.o lm

    Ntese que la opcin lm hace referencia a linkar l con la librera m o denombre completo libm.a o libm.so que es la librera estndar matemtica en susversiones estticas o dinmicas. Buscar con find / -name libm.*

    Eliminar archivos objeto

    rm *.o

  • 5/21/2018 47508930-ProgramacionAvanzada

    19/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 19

    Universidad Politcnica de Madrid -UPM

    1.9.MAKEFILE Y LA HERRAMIENTA MAKEHemos visto un ejemplo sencillo, en el que teclear el comando para compilar y

    crear el ejecutable es muy sencillo. Sin embargo este procedimiento puede ser largo ytedioso en el caso de grandes proyectos con muchos ficheros fuente y mltiples

    opciones de compilacin.

    Por ello existe una herramienta, el make, que haciendo uso de la

    configuracin de un fichero denominado Makefile (sin extensin, tpicamentesituado en la carpeta en la que tenemos el proyecto), se encarga de todo este trabajo.Entre otras cosas, se encarga de realizar la comprobacin de que ficheros han sidomodificados, para solo compilar dichos archivos, ahorrando mucho tiempo al usuario.

    La sintaxis del Makefile es muy potente y compleja, por lo que aqu se realiza

    solamente la descripcin de una configuracin bsica para el proyecto de esta practica.Para ello crear y editar con el vi el archivo siguiente:

    #Makefile del proyecto

    CC=gccCFLAGS= -gLIBS= -lmOBJS=misfunc.o principal.o

    prueba: $(OBJS)$(CC) $(OBJS) $(LIBS) o prueba

    principal.o: principal.c misfunc.h$(CC) c principal.c

    misfunc.o: misfunc.c misfunc.h

    $(CC) c principal.c

    clean:rm f *.o prueba

    Los comentarios en un Makefile se preceden de #

    #Makefile del proyecto

    El Makefile permite la definicin de variables, mediante una simple asignacin.En la primera parte del Makefile establecemos algunas variables de conveniencia. Sedefine la cadena CC que nos definir el compilador que se va a usar

    CC=gcc

    Se define la cadena CFLAGS que nos definir las opciones de compilacin, eneste caso habilita la informacin que posibilita la depuracin del ejecutable

    CFLAGS= -g

    La cadena LIBS almacena las libreras con las que hay que linkar para generar elejecutable

    LIBS= -lm

    La cadena OBJS define los mdulos objeto que componen el ejecutable. Aqu se

    deben listar todos los archivos objeto necesarios, si nos olvidamos alguno, el enlazadorencontrara un error.

  • 5/21/2018 47508930-ProgramacionAvanzada

    20/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 20

    Universidad Politcnica de Madrid -UPM

    OBJS=misfunc.o principal.o

    A partir de aqu comienzan las reglas, cada regla tiene la siguiente estructura:

    objetivo (target): prerequisitos o dependenciascomando

    Cada regla mira si los prerrequisitos o dependencias han sido modificados, ycaso de que lo hayan sido, construye el objetivo utilizando el comando. La siguientecadena establece la construccin del ejecutable a partir de los objetos, y linkando conlas libreras LIBS y generando el ejecutable prueba

    prueba: $(OBJS)$(CC) $(OBJS) $(LIBS) o prueba

    Que es totalmente equivalente a:

    prueba: misfunc.o principal.ogcc misfunc.o principal.o -lm o prueba

    Que significa: Si alguno o ambos de los ficheros objeto han cambiado, se tieneque volver a linkar el ejecutable prueba, a partir de los ficheros objeto y enlazando

    con la librera matemticalm.

    A su vez especificamos la compilacin de cada uno de los mdulos objeto:

    principal.o: principal.c misfunc.h$(CC) c principal.c

    misfunc.o: misfunc.c misfunc.h$(CC) c principal.c

    Las dos primeras lneas, analizan si han sido modificados principal.c omisfunc.h, y en su caso, significa que hay que volver a compilar el modulo objeto a

    partir del cdigo fuente.El Makefile analiza las dependencias recursivas, esto es, si el fichero

    misfunc.h ha sido modificado, primero compilara con las dos ultimas reglas los

    ficheros objeto principal.o y misfunc.o. Como estos ficheros han sido modificados,

    invocara a su vez a la regla superior, linkando y obteniendo el ejecutable prueba.

    La regla clean (make clean) elimina los objetos y el ejecutable

    clean:rm f *.o prueba

    Lo que significa que si tecleamos en la lnea de comandos:

    make clean

    en vez de construir el ejecutable, se borran los archivos binarios temporales y elejecutable

    1.10. TIPOS DE ERRORExisten dos tipos de errores en un programa, errores en tiempo de ejecucin y

    errores en tiempo de compilacin. Vamos a ver la diferencia entre ambos:

    Errores en tiempo de compilacin. Son errores, principalmente desintaxis. El compilador los detecta y nos informa de ello, no produciendo

  • 5/21/2018 47508930-ProgramacionAvanzada

    21/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 21

    Universidad Politcnica de Madrid -UPM

    un ejecutable. Vamos a provocar un error de este estilo. Realizamos elcambio:

    printf("Seno de %d es %f \n",i,seno(float(i));

    Quitamos el punto y coma del final:

    printf("Seno de %d es %f \n",i,seno(float(i))

    Y compilamos de nuevo. Nos saldr un mensaje informndonos del errorsintctico y en que lnea se produce.

    Errores en tiempo de ejecucin. Tambin llamados errores lgicos orun-time error. Es un error que no es capaz de detectar el compiladorporque no es un fallo en la sintaxis, pero que produce un error alejecutar el programa por un fallo lgico. Por ejemplo, la divisin porcero, sintcticamente no es un error en el programa, pero al realizar ladivisin, se produce un error en tiempo de ejecucin. En todo caso, si el

    compilador detecta la divisin por cero (por ejemplo al hacer int a=3/0;)puede emitir un warning.

    int a=0;int b=3;int c=b/a;

    Compilamos este programa y lo ejecutamos. El programa fallara y nos saldr unmensaje informndonos de ello. Tambin cabe la posibilidad de que un fallo en elcdigo del programa produzca un comportamiento no deseado, pero que este noresulte en un fallo fatal y el programa finalice bruscamente.

    1.11. DEPURACIN DE LA APLICACIN.Para depurar un programa se debe ejecutar el depurador seguido del nombre

    del ejecutable (que debe haber sido creado con la opcing)

    gdb prueba

    El depurador arranca y muestra un nuevo prompt (gdb) que espera a recibirlos comandos adecuados para ejecutar el programa paso a paso o como se le indique.Los comandos que puede recibir este prompt se dividen en distintos grupos,

    mostrados por el comando

    (gdb) help

    Si se desea ver los comandos que pertenecen a cada grupo se debe escribir(p.ej. para ver los comandos que permiten gestionar la ejecucin del programa)

    (gdb) help [nombre grupo] (ejemplo: running )

    Y para ver la ayuda de un comando en particular:

    (gdb) help [comando]

    Caben destacar por su utilidad los siguientes comandos pertenecientes a losgrupos:

  • 5/21/2018 47508930-ProgramacionAvanzada

    22/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 22

    Universidad Politcnica de Madrid -UPM

    Tabla 1-6. Comandos bsicos de gdb

    Grupo Comando Accin

    running run comienza la depuracin del

    programa

    step ejecuta un paso, entrando en

    funciones

    next ejecuta un paso, sin entrar en

    funciones

    finish termina la ejecucin del programa

    continue continua la ejecucin del programa,

    hasta el siguiente breakpoint

    data display [exp] muestra el contenido de la variable

    exp cada vez que el programa se

    para

    undisplay [exp] quita el comportamiento anterior

    print [exp] Muestra el contenido de expbreakpoint break [num_linea] inserta un punto de parada o

    Breakpoint en la lnea

    correspondiente

    clear [num_linea] Eliminan el breakpoint de la lnea

    correspondiente

    delete break Pregunta si se desea eliminar todos

    los breakpoints

    status info [opcion] Muestra informacin acerca de la

    opcin elegida, por ejemplo info

    break muestra los breakpoints.

    ninguno quit sale del debugger

    Realizar la depuracin del programa anterior, viendo el valor de las posiblesvariables, ejecutando paso a paso.

    1.12. CREACIN DE UN SCRIPTSe puede crear un archivo de texto que sirva para ejecutar una serie de

    comandos consecutivos en el shell, en lo que se llama un script. Para ver un ejemplo se

    va a crear un script que muestre el nombre de la carpeta actual y a continuacinmuestre el contenido de dicha carpeta, para termina ejecutando el programa prueba.

    Para ello creamos un archivo:

    vi miscript

    echo La carpeta actual es pwdecho Y contiene lo siguiente ls

    Si intentamos ejecutar el script, nos dir que no tiene permisos de ejecucin.

    Para eso realizamos el cambio:chmod a+x miscript

  • 5/21/2018 47508930-ProgramacionAvanzada

    23/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 23

    Universidad Politcnica de Madrid -UPM

    Debe de quedar claro que con un script no se tiene cdigo mquina, ni secompila, ni se inicia un proceso. Simplemente se la pasan al shell unos comandos enlotes.

    1.13. DESARROLLO EN UN ENTORNO GRAFICOExisten distintas herramientas para el desarrollo C/C++ en linux, entre las que

    se podran destacar el Kdevelop, Anjuta, o Eclipse. Para el desarrollo de nuestraaplicacin hemos optado por Geany, que realmente es ms un editor de texto que unentorno de desarrollo, pero sin embargo tiene las caractersticas necesarias paranuestra aplicacin. Geany dispone de resaltado en colores del cdigo, y de gestion dela compilacin mediante Makefile, que permite mediante la pulsacin de F9 lainvocacin automtica de Makefile (aunque el fichero Makefile lo debemos proveernosotros), as como la gestin de los posibles errores de compilacin, con la posibilidadde saltar a la lnea del error simplemente haciendo doble click en el mensaje de error.

    Figura 1-4. El editor Geany

    Si se desea instalar el editor, as como las libreras necesarias de Glut, esnecesario:

    sudo apt-get install geany glutg3-dev

  • 5/21/2018 47508930-ProgramacionAvanzada

    24/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 24

    Universidad Politcnica de Madrid -UPM

    1.14. EJERCICIO PRCTICOSe suministra en una carpeta un conjunto de ficheros de cdigo fuente, con

    algunas clases de C++, necesarias para el desarrollo del juego del Tenis,fundamentalmente las clases Mundo, Esfera, Plano, Raqueta, y la clase auxiliar

    Vector2D. Todas las clases estn completas, exceptuando la clase Mundo.#include "Vector2D.h"class Esfera{public:

    Esfera();virtual ~Esfera();

    Vector2D centro;Vector2D velocidad;float radio;

    void Mueve(float t);void Dibuja();

    };

    #include "Esfera.h"#include "Vector2D.h"class Plano{public:

    bool Rebota(Esfera& e);bool Rebota(Plano& p);void Dibuja();

    Plano();virtual ~Plano();

    float x1,y1;float x2,y2;float r,g,b;

    protected:float Distancia(Vector2D punto, Vector2D *direccion);

    };

    #include "Plano.h"#include "Vector2D.h"class Raqueta : public Plano{public:

    void Mueve(float t);Raqueta();virtual ~Raqueta();

    Vector2D velocidad;};

    class CMundo

    {public:void Init();

  • 5/21/2018 47508930-ProgramacionAvanzada

    25/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 25

    Universidad Politcnica de Madrid -UPM

    CMundo();virtual ~CMundo();

    void InitGL();void OnKeyboardDown(unsigned char key, int x, int y);void OnTimer(int value);

    void OnDraw();};

    Se solicita al alumno que complete la clase Mundo para obtener el juego deltenis funcional. Se debe escribir un Makefile para la construccin del ejecutable.

    1.15. EJERCICIO PROPUESTOEl alumno debe de completar el juego con alguna funcionalidad extra, como por

    ejemplo, que cada una de las raquetas sea capaz de disparar un disparo, que cuando

    impacta al oponente lo inmoviliza, o disminuye el tamao de su raqueta.

    Tambin se propone el desarrollo de cualquier otro juego de complejidadsimilar.

  • 5/21/2018 47508930-ProgramacionAvanzada

    26/157

  • 5/21/2018 47508930-ProgramacionAvanzada

    27/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 27

    Universidad Politcnica de Madrid -UPM

    22.. INTRODUCCIN A LOS SISTEMASDISTRIBUIDOS.COMUNICACIN POR

    SOCKETS

    2.1.OBJETIVOSEn el captulo anterior se ha desarrollado el juego bsico del tenis en el que dos

    jugadores, compartiendo el mismo teclado y el mismo monitor, cada uno con distintasteclas puede controlar su raqueta arriba y abajo para jugar la partida. El objetivo finales la consecucin del juego totalmente distribuido, es decir, cada jugador podr jugaren su propio ordenador, con su teclado y su monitor, y los dos ordenadores estarnconectados por la red.

    En este captulo se presenta una introduccin a los sistemas distribuidos, los

    servicios proporcionados en POSIX para el manejo de Sockets, que son los conectoresnecesarios (el recurso software) para la comunicacin por la red, y su uso en nuestraaplicacin. No pretende ser una gua exhaustiva de dichos servicios sino unadescripcin prctica del uso ms sencillo de los mismos, y como integrarlos en nuestraaplicacin para conseguir nuestros objetivos. De hecho, en el curso del captulo sedesarrolla una clase C++ que encapsula los servicios de Sockets, permitiendo al usuarioun uso muy sencillo de los mismos que puede valer para numerosas aplicaciones,aunque obviamente no para todo.

    Como primera aproximacin al objetivo final se va a realizar en este captulo laretransmisin del partido de tenis por la red. Esto es, los dos jugadores van a seguir

    jugando en la misma mquina con el mismo teclado, pero sin embargo otro usuariodesde otra mquina podr conectarse remotamente a travs de la red a la mquina y a

  • 5/21/2018 47508930-ProgramacionAvanzada

    28/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 28

    Universidad Politcnica de Madrid -UPM

    la aplicacin en la que juegan los jugadores (el servidor), y esta le enviaraconstantemente los datos necesarios para que la mquina remota (el cliente) puedasimplemente dibujar el estado actual de la partida. De esta forma lo que se permite esque los clientes sean meros espectadores de la partida. Inicialmente se plantea lasolucin para un nico espectador, y finalmente se aborda la solucin para mltiples

    espectadores. No obstante esta ltima requerir para su correcto funcionamiento eluso de programacin concurrente (hilos) que se abordara en sucesivos captulos.

    Figura 2-1. Objetivo del captulo: Retransmisin de la partida de tenis a ordenadores

    remotos conectados a travs de la red al servidorEn sucesivos captulos se completar el desarrollo del juego distribuido

    haciendo que los jugadores puedan realmente jugar en dos mquinas distintas, quetransmitirn los comandos de los jugadores por la misma red al servidor, para que estelos ejecute sin necesidad de tener a dichos jugadores utilizando el mismo teclado fsicode la mquina en la que corre el servidor.

    2.2.SISTEMA DISTRIBUIDOLlamaremos sistema distribuido a una solucin software cuya funcionalidad es

    repartida entre distintas mquinas, teniendo cada mquina su propio procesador (opropios procesadores), su propia memoria, y corriendo su propio sistema operativo.Adems, no es necesario que las mquinas sean iguales, ni ejecuten el mismo SO ni elmismo software. Las mquinas estarn interconectadas por una red que sirve para elintercambio de mensajes entre dichas mquinas.

    RED

    Retransmisin

    partido

    N posiblesclientes

    que seconectanal servidorpara ver elpartido

    Servidor, en el quejuegan los dosjugadores con elmismo teclado

  • 5/21/2018 47508930-ProgramacionAvanzada

    29/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 29

    Universidad Politcnica de Madrid -UPM

    2.3.SERVICIOS DE SOCKETS EN POSIXA continuacin se presenta el cdigo de un programa cliente y de un programa

    servidor, para describir breve y generalmente los servicios de sockets implicados. Estecdigo es prcticamente el ms bsico posible, sin comprobacin de errores. El

    funcionamiento ser como sigue: Primero se arranca el programa servidor, queinicializa el socketservidor y se queda a la espera de una conexin. A continuacin sedebe lanzar el programa cliente que se conectar al servidor. Una vez que ambos estnconectados, el servidor enviara al cliente unos datos (una frase) que el clientemostrar por pantalla, y a finalmente terminarn ambos programas. El funcionamientoen lneas generales queda representado en la siguiente figura:

    Figura 2-2. Conexin sockets

    ServidorCliente

    Se crea el socketde conexin

    Se le asigna unadireccin y unpuerto y se ponea la escucha

    El socket deconexin sequeda bloqueadoa la esperaAceptando una

    conexin

    socket()

    bind()

    listen()

    accept()

    Se crea el socketde conexin ycomunicacin (esel mismo)

    Se conecta a ladireccin delservidor

    socket()

    connect()

    Comunicacin Comunicacinsend()

    recv()

    send()

    recv()

    TCP/IP

    Cierre

    shutdown()

    close()Cierre shutdown()

    close()

    Cuando el cliente se conecta alsocket de conexin que estaAceptando, este devuelve unsocket de conexin que es con elque se realiza la comunicacin

  • 5/21/2018 47508930-ProgramacionAvanzada

    30/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 30

    Universidad Politcnica de Madrid -UPM

    2.3.1Programa cliente

    El cdigo del programa cliente bsico es el siguiente:

    //includes necesarios para los sockets#include

    #include #include #include #include #include

    #define INVALID_SOCKET -1

    int main(){//declaracion de variables

    int socket_conn;//the socket used for the send-receivestruct sockaddr_in server_address;

    char address[]="127.0.0.1";int port=12000;

    // Configuracion de la direccion IP de connexion al servidorserver_address.sin_family = AF_INET;server_address.sin_addr.s_addr = inet_addr(address);server_address.sin_port = htons(port);

    //creacion del socketsocket_conn=socket(AF_INET, SOCK_STREAM,0);

    //conexionint len= sizeof(server_address);

    connect(socket_conn,(struct sockaddr *) &server_address,len);

    //comunicacionchar cad[100];int length=100; //read a maximum of 100 bytes

    int r=recv(socket_conn,cad,length,0);std::cout

  • 5/21/2018 47508930-ProgramacionAvanzada

    31/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 31

    Universidad Politcnica de Madrid -UPM

    La primera lnea declara el descriptor del socket (de tipo entero) que se utilizatanto para la conexin como para la comunicacin. La segunda declaracin declara unaestructura de datos que sirve para almacenar la direccin IP y el nmero de puerto delservidor y la familia de protocolos que se utilizaran en la comunicacin. La asignacinde esta estructura a partir de la IP definida como una cadena de texto y el puerto

    definido como un entero se hace como sigue:

    char address[]="127.0.0.1";int port=12000;server_address.sin_family = AF_INET;server_address.sin_addr.s_addr = inet_addr(address);server_address.sin_port = htons(port);

    Ntese que la IP que utilizaremos ser la 127.0.0.1. Esta IP es una IP especialque significa la mquina actual (direccin local). Realmente ejecutaremos nuestras 2aplicaciones (cliente y servidor) en la misma mquina, utilizando la direccin local de lamquina. No obstante esto se puede cambiar. Para ejecutar el servidor en una

    mquina que tiene la IP 192.168.1.13 por ejemplo, basta poner dicha direccin enambos programas, ejecutar el servidor en esa mquina, y el cliente en cualquier otra(que sea capaz de enrutar mensajes hacia esa IP).

    A continuacin se crea el socket, especificando la familia de protocolos (en estecaso protocolo de Internet AF_INET) y el tipo de comunicacin que se quiere

    emplear (fiable=SOCK_STREAM, no fiable=SOCK_DGRAM). En nuestro casoutilizaremos siempre comunicacin fiable.

    //creacion del socketsocket_conn=socket(AF_INET, SOCK_STREAM,0);

    Esta funcin generalmente no produce errores, aunque en algn caso podrahacerlo. Como regla general conviene comprobar su valor, que ser igual a -1(INVALID_SOCKET) si la funcin ha fallado. A continuacin se intenta la conexincon el socket especificado en la direccin del servidor.

    //conexionint len= sizeof(server_address);connect(socket_conn,(struct sockaddr *) &server_address,len);

    Esta funcin connect() fallar si no esta el servidor preparado por algnmotivo (lo que sucede muy a menudo). Por lo tanto es ms que convenientecomprobar el valor de retorno de connect()para actuar en consecuencia. Se podra

    hacer algo como:if(connect(socket_conn,(struct sockaddr *) &server_address,len)!=0){

    std::cout

  • 5/21/2018 47508930-ProgramacionAvanzada

    32/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 32

    Universidad Politcnica de Madrid -UPM

    Dicha informacin puede ser menor que el tamao mximo suministrado. El valor deretorno de la funcin recv()es el numero de bytes recibidos.

    //comunicacionchar cad[100];int length=100; //read a maximum of 100 bytes

    int r=recv(socket_conn,cad,length,0);std::cout

  • 5/21/2018 47508930-ProgramacionAvanzada

    33/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 33

    Universidad Politcnica de Madrid -UPM

    //escuchabind(socket_server,(struct sockaddr *) &server_address,len);// Damos como maximo 5 puertos de conexion.listen(socket_server,5);

    //aceptacion de cliente (bloquea hasta la conexion)unsigned int leng = sizeof(client_address);socket_conn = accept(socket_server,

    (struct sockaddr *)&client_address, &leng);

    //notese que el envio se hace por el socket de communicacionchar cad[]="Hola Mundo";int length=sizeof(cad);send(socket_conn, cad, length,0);

    //cierre de los dos sockets, el servidor y el de comunicacion

    shutdown(socket_conn, SHUT_RDWR);

    close(socket_conn);socket_conn=INVALID_SOCKET;

    shutdown(socket_server, SHUT_RDWR);close(socket_server);socket_server=INVALID_SOCKET;

    return 1;}

    Hasta la creacin del socket del servidor, el programa es similar al cliente,quitando la excepcin de que se declaran los 2 sockets, el de conexin y el decomunicacin. La primera diferencia son las lneas:

    //configuracion del socket para reusar direccionesint on=1;setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));

    Estas lneas se utilizan para que el servidor sea capaz de re-usar la direccin y elpuerto que han quedado abiertos sin ser cerrados correctamente en una ejecucinanterior. Cuando esto sucede, el sistema operativo deja la direccin del socketreservada y por tanto un intento de utilizarla para un servidor acaba en fallo. Con estaslneas podemos configurar y habilitar que se re-usen las direcciones previas.

    La segunda diferencia es que en vez de intentar la conexin con connect(),el servidor debe establecer primero en que direccin va a estar escuchando su socketde conexin, lo que se establece con las lneas:

    int len = sizeof(server_address);bind(socket_server,(struct sockaddr *) &server_address,len);// Damos como maximo una cola de 5 conexiones.listen(socket_server,5);

    La funcin bind() enlaza el socket de conexin con la IP y el puertoestablecidos anteriormente. Esta funcin tambin es susceptible de fallo. El fallo mscomn es cuando se intenta enlazar el socket con una direccin y puerto que ya estnocupados por otro socket. En este caso la funcin devolver -1, indicando el error. Aveces es posible que si no se cierra correctamente un socket (por ejemplo, si el

    programa finaliza bruscamente), el SO piense que dicho puerto esta ocupado, y al

  • 5/21/2018 47508930-ProgramacionAvanzada

    34/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 34

    Universidad Politcnica de Madrid -UPM

    volver a ejecutar el programa, el bind() falle, no teniendo sentido continuar laejecucin. La gestin bsica de este error podra ser:

    if(0!=bind(socket_server,(struct sockaddr *) &server_address,len)){

    std::cout

  • 5/21/2018 47508930-ProgramacionAvanzada

    35/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 35

    Universidad Politcnica de Madrid -UPM

    El cierre de los sockets se realiza de la misma manera que en el cliente,exceptuando que se deben cerrar correctamente los 2 sockets, el de conexin y el decomunicacin. La salida por pantalla al ejecutar las aplicaciones (primero arrancar elservidor y luego el cliente) debera ser (en el lado del cliente):

    Rec: 11 contenido: Hola MundoNtese que los bytes recibidos son 11 porque incluyen el carcter nulo \0 de

    final de la cadena

    2.4.ENCAPSULACIN DE UN SOCKET EN UNA CLASE C++La API vista en el apartado anterior es C, y aparte de las funciones descritas,

    tiene otras funcionalidades que no se vern en este curso. Es una prctica habitualcuando se puede desarrollar en C++ encapsular la funcionalidad de la API en una claseo conjunto de clases que oculten parcialmente los detalles ms complejos, facilitandola tarea al usuario. As, por ejemplo, las Microsoft Fundation Classes(MFC) tienen susclases CSocket y CAsyncSocket para estas tareas. Tambin se puedenencontrar en Internet numerosos envoltorios (wrappers) de C++ para los sockets enlinux.

    Vamos a desarrollar una clase C++ que encapsule la funcionalidad vista en losprogramas anteriores. Es comn encontrar, bajo una perspectiva estricta deProgramacin Orientada a Objetos (POO) que el cliente y servidor se implementan enclases separadas. No obstante, se adopta ahora un enfoque ms sencillo con una sola

    clase, que utiliza diferentes mtodos en caso del cliente y del servidor.EJERCICIO: Desarrollar la clase Socket, de acuerdo con la cabecera siguiente, paraque encapsule los detalles de implementacin anteriores.

    //includes necesariosclass Socket{public:

    Socket();virtual ~Socket();

    // 0 en caso de exito y -1 en caso de errorint Connect(char ip[],int port); //para el clienteint InitServer(char ip[],int port);//para el servidor

    //devuelve un socket, el empleado realmente para la comunicacion//el socket devuelto podria ser invalido si el accept fallaSocket Accept();//para el servidorvoid Close();//para ambos

    //-1 en caso de error,// numero de bytes enviados o recibidos en caso de exitoint Send(char cad[],int length);int Receive(char cad[],int length);

    private:int sock;

    };

  • 5/21/2018 47508930-ProgramacionAvanzada

    36/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 36

    Universidad Politcnica de Madrid -UPM

    El cdigo del servidor se ver simplificado a:

    #include #include "Socket.h"

    int main(){

    Socket servidor;servidor.InitServer("127.0.0.1",12000);

    Socket conn=servidor.Accept();

    char cad[]="Mensaje";int length=sizeof(cad);

    conn.Send(cad,length);

    conn.Close();servidor.Close();

    return 1;}

    Y el cdigo del cliente:

    #include "Socket.h"#include

    int main(){

    Socket client;client.Connect("127.0.0.1",12000);

    char cad[1000];int length=1000;

    int r=client.Receive(cad,length);std::cout

  • 5/21/2018 47508930-ProgramacionAvanzada

    37/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 37

    Universidad Politcnica de Madrid -UPM

    for(int i=0;i

  • 5/21/2018 47508930-ProgramacionAvanzada

    38/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 38

    Universidad Politcnica de Madrid -UPM

    2. Existe un convenio entre el cliente y el servidor que especifica como sonlos mensajes, para que el cliente sepa que es lo que va a recibir y comolo tiene que interpretar. Este convenio puede consistir en especificaruna longitud fija para los mensajes, o en establecer un carcterterminador de mensaje. En el caso anterior podramos haber recorrido

    los mensajes buscando los caracteres nulos \0 que nos separaran cadamensaje. Si consideramos los mensajes de longitud fija el cdigo delservidor podra ser:

    //definimos los mensajes de 100 bytes siemprechar cad[100]="Hola Mundo";int length=sizeof(cad); //length=100

    for(int i=0;i

  • 5/21/2018 47508930-ProgramacionAvanzada

    39/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 39

    Universidad Politcnica de Madrid -UPM

    Un cliente solo puede comunicarse con un servidor.

    2.4.2.1. Conexiones secuenciales

    Una primera opcin es que el servidor atienda secuencialmente las conexiones

    de los distintos clientes, esto es, se conecta un cliente, se comunica con el y vuelve aesperar aceptando en el accept()a un nuevo cliente.

    Figura 2-3. Servidor que permite mltiples conexiones secuenciales de clientes

    El cliente permanecera inalterado, y el cdigo del servidor quedara comosigue:

    Servidor

    Cliente

    Se crea el socketde conexin

    Se le asigna unadireccin y unpuerto y se ponea la escucha

    El socket deconexin sequeda bloqueadoa la esperaAceptando una

    conexin

    Comunicacin

    Cierre del socket

    Cierre del socket

    Seguiraceptandoclientes?

    SI

    NO

    Comunicacin

    Conexin

    Se crea el socketde conexin ycomunicacin

    Cierre

    Socket deconexion

  • 5/21/2018 47508930-ProgramacionAvanzada

    40/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 40

    Universidad Politcnica de Madrid -UPM

    #include #include "Socket.h"

    int main(){

    Socket servidor;

    servidor.InitServer("127.0.0.1",12000);

    while(1){

    Socket conn=servidor.Accept();

    //comunicacion, en este caso envio de 1 unico mensajechar cad[]="Hola mundo";int length=sizeof(cad);

    conn.Send(cad,length);

    conn.Close();

    }servidor.Close();

    return 1;}

    La funcin listen()toma sentido en este contexto, ya que permite poner ala cola peticiones de conexiones de varios clientes que intentan la conexin mientras elservidor esta comunicando con el cliente actual. Cuando el servidor vuelve alaccept()se atienden dichas peticiones de conexin.

    2.4.2.2. Conexiones simultneas.

    Es posible que el servidor acepte la conexin de varios clientes y enve datos atodos ellos, manteniendo la conexin activa con todos simultneamente.

    Para ello y dado que aun no estamos utilizando programacin concurrente,primero se realiza el accept() de tantos clientes como se vayan a conectar (elservidor debe conocer dicho numero). Hay que recordar que el accept() bloqueahasta que se conecta un cliente, por lo tanto hasta que no se conecten tantos clientescomo accept() se intenten, el programa no podr continuar. Como cada conexindevuelve un socket diferente a travs del accept(), estos sockets se puedenalmacenar en un vector, y manejar todas las conexiones en el servidor a travs de

    dicho vector.

  • 5/21/2018 47508930-ProgramacionAvanzada

    41/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 41

    Universidad Politcnica de Madrid -UPM

    Figura 2-4. Comunicacin simultanea con varios clientes

    El cdigo resultante en el servidor podra ser:

    #include #include "Socket.h"int main(){

    Socket servidor;servidor.InitServer("127.0.0.1",12000);

    Socket conexiones[5];for(i=0;i

  • 5/21/2018 47508930-ProgramacionAvanzada

    42/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 42

    Universidad Politcnica de Madrid -UPM

    2.5.ESTRUCTURA DE FICHEROSAhora que se ha visto como realizar el envo de informacin por la red, y se

    dispone de una clase que encapsula la funcionalidad de los sockets se va a proceder acomenzar el desarrollo de la aplicacin distribuida del juego del tenis. Debe quedar

    claro que solo hay que desarrollar dos aplicaciones, la aplicacin servidor y laaplicacin cliente. La aplicacin servidor se ejecutar una vez, pero la aplicacin cliente(el mismo binario) puede ser ejecutado mltiples veces y en distintas mquinas. Separte de la aplicacin desarrollada en el tema anterior, que constituye el juego deltenis (los dos jugadores en la misma mquina), cuyos ficheros se encuentran todos enla misma carpeta y los cuales son:

    Esfera.h y Esfera.cpp (la clase Esfera)

    Plano.h y Plano.cpp (la clase Plano)

    Raqueta.h y Raqueta.cpp (la clase Raqueta)

    Vector2D.h y Vector2D.cpp (la clase Vector2D)

    Mundo.h y Mundo.cpp (la clase Mundo)

    Tenis.cpp (el fichero principal con el main() )

    Makefile

    La primera intencin podra ser duplicar esta carpeta para realizar lasmodificaciones necesarias en cada una de ellas y transformarlas en el servidor y elcliente. No obstante, esto implicara que habra mucho cdigo idntico duplicado endos sitios. Por ejemplo, la clase

    Plano ser exactamente igual en el cliente y en el

    servidor, su parametrizacin es igual, se dibuja igual. Por tanto no es necesario (dehecho es contraproducente) que el cdigo este repetido. Se pueden desarrollar ambosprogramas, el cliente y el servidor compartiendo uno o varios archivos de cdigofuente.

    Si se analiza la funcionalidad del servidor y del cliente se llega a la conclusinque ambas aplicaciones son iguales, exceptuando:

    El servidor atiende el teclado, cambiando la velocidad de las raquetas,pero los clientes no, son solo espectadores. Esto se hace en la funcinCMundo::OnKeyboardDown()

    El servidor cambia las posiciones de los objetos (anima), realiza losclculos de las colisiones. El cliente no tiene que mover los objetos(podra moverlos de forma diferente al servidor), solo tiene que recibirla informacin del servidor de donde estn los objetos en cada instantede tiempo. El cambio de posicin de los objetos se hace en la funcinCMundo::OnTimer().

    Como se ve, la nica clase que va a tener diferencias entre el servidor y elcliente es la clase CMundo. Por tanto, se propone nicamente duplicar este archivocon dos nombres diferentes (aunque el nombre de la clase se puede mantener.)

    Tambin es necesario duplicar el archivo en el que se encuentra el main(), ya que es

  • 5/21/2018 47508930-ProgramacionAvanzada

    43/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 43

    Universidad Politcnica de Madrid -UPM

    el que instancia la clase CMundo, y en funcin de si es el servidor o el cliente,

    necesitara hacer un #includea MundoServidor.h o a MundoCliente.h

    Esfera.h y Esfera.cpp (la clase Esfera)

    Plano.h y Plano.cpp (la clase Plano)

    Raqueta.h y Raqueta.cpp (la clase Raqueta)

    Vector2D.h y Vector2D.cpp (la clase Vector2D)

    MundoServidor.h y MundoServidor.cpp (la clase Mundo para elservidor)

    MundoCliente.h y MundoCliente.cpp (la clase Mundo para el cliente)

    servidor.cpp (el fichero principal con el main(), para el servidor )

    cliente.cpp (el fichero principal con el main(), para el cliente )

    Makefile

    En el Makefile se especifican como se construyen las dos aplicacionesdiferentes:

    CC=g++CFLAGS= -gLIBS= -lm -lglutOBJS=Esfera.o Plano.o Raqueta.o Vector2D.o Socket.oHEADERS=Esfera.h MundoCliente.h MundoServidor.h Plano.h Raqueta.hVector2D.h

    all: servidor cliente

    servidor: $(OBJS) MundoServidor.o servidor.o$(CC) $(CFLAGS) -o servidor servidor.o MundoServidor.o $(OBJS)

    $(LIBS)

    cliente: $(OBJS) MundoCliente.o cliente.o$(CC) $(CFLAGS) -o cliente cliente.o MundoCliente.o $(OBJS)

    $(LIBS)

    Socket.o: Socket.cpp $(HEADERS)$(CC) $(CFLAGS) -c Socket.cpp

    MundoCliente.o: MundoCliente.cpp $(HEADERS)$(CC) $(CFLAGS) -c MundoCliente.cpp

    MundoServidor.o: MundoServidor.cpp $(HEADERS)$(CC) $(CFLAGS) -c MundoServidor.cpp

    Esfera.o: Esfera.cpp $(HEADERS)$(CC) $(CFLAGS) -c Esfera.cpp

    Plano.o: Plano.cpp $(HEADERS)$(CC) $(CFLAGS) -c Plano.cpp

    Raqueta.o: Raqueta.cpp $(HEADERS)$(CC) $(CFLAGS) -c Raqueta.cpp

    Vector2D.o: Vector2D.cpp $(HEADERS)$(CC) $(CFLAGS) -c Vector2D.cpp

    servidor.o: servidor.cpp $(HEADERS)$(CC) $(CFLAGS) -c servidor.cpp

    cliente.o: cliente.cpp $(HEADERS)

    $(CC) $(CFLAGS) -c cliente.cppclean:

    rm -f *.o cliente servidor

  • 5/21/2018 47508930-ProgramacionAvanzada

    44/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 44

    Universidad Politcnica de Madrid -UPM

    Con este Makefilela simple invocacin

    make

    construye tanto el servidor como el cliente

    2.6.TRANSMITIENDO EL PARTIDO DE TENISInicialmente vamos a realizar el envo de los datos necesarios del servidor a un

    nico cliente. Para ello se deben de seguir los siguientes pasos:

    2.6.1Conexin

    Aadir el Socket de conexin y el de comunicacin en la clase Mundo del

    servidor:

    Socket server;Socket conn;

    Aadir el Socket en la clase Mundodel cliente

    Socket client;

    En la funcin de inicializacin del juego en el servidor se establece la direccinIP y el puerto del servidor y se espera la aceptacin de un cliente:

    //en el fichero MundoServidorvoid CMundo::Init()

    {//inicializacion de la pantalla, coordenadas, etc

    server.InitServer("127.0.0.1",12000);conn=server1.Accept();

    }

    Ntese en este punto que si se compila y ejecuta el servidor no se muestranada por pantalla. Sencillamente el programa esta bloqueado a la espera de laconexin y ni siquiera ha creado aun la ventana grafica. No obstante el accept sepodra realizar ms tarde, despus de haber creado la ventana.

    El cliente tambin realiza en su funcin init()la conexin del socket:

    //del fichero MundoClientevoid CMundo::Init(){

    //inicializacion de la pantalla, coordenadas, etc

    client.Connect("127.0.0.1",12000);}

  • 5/21/2018 47508930-ProgramacionAvanzada

    45/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 45

    Universidad Politcnica de Madrid -UPM

    2.6.2Envo de datos

    Lo primero es necesario establecer cuales son los datos que es necesario queenve el servidor al cliente. Dado que la pantalla es en su mayora esttica, las variablesque es necesario transmitir podran ser:

    Coordenadas (x, y) de la pelota

    Coordenadas (x1, y1, x2, y2) de la raqueta del jugador 1.

    Coordenadas (x1, y1, x2, y2) de la raqueta del jugador 2.

    Estos datos deben de ser enviados por el servidor cada vez que se produce uncambio en los mismos, es decir, en cada temporizacin del timer. Cmo se envandatos numricos? Aunque una solucin ms evolucionada se presentara en un temaposterior, una primera solucin sencilla consiste en escribir (sprintf()) estosvalores numricos en una cadena de texto y enviar dicha cadena de texto.

    EJERCICIO:

    1. Realizar el envo de los datos por el socket de comunicacin en el servidor(MundoServidor), en la funcin CMundo::OnTimer(), al final de la misma,manteniendo el cdigo existente encargado de realizar la animacin y lgica del

    juego.

    2. Eliminar el cdigo de la funcin CMundo::OnTimer() de MundoCliente y sustituirlopor la recepcin del mensaje del servidor y la extraccin de los valores numricos.

    2.7.EJERCICIOS PROPUESTOS

    Realizar la retransmisin del juego a un numero fijo de clientes, porejemplo 3

    Implementar los conceptos desarrollados en este tema en un juego decomplejidad similar.

  • 5/21/2018 47508930-ProgramacionAvanzada

    46/157

  • 5/21/2018 47508930-ProgramacionAvanzada

    47/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 47

    Universidad Politcnica de Madrid -UPM

    33.. COMUNICACIONES Y CONCURRENCIA

    3.1.INTRODUCCINEn el captulo anterior hemos concluido con dos programas, un servidor y un

    cliente, en el que el servidor enviaba los datos de la partida de tenis de forma continuaal cliente. De hecho, tambin podamos permitir que se conectaran varios clientes y

    despus (una vez conectados todos los clientes, con lo que se tenia que conocer sunumero) enviar los datos a todos los clientes. Pero aun no podemos permitir que losclientes espectadores se conecten y desconecten cuando quieran, o que los

    jugadores puedan efectivamente jugar de forma remota.

    Tal como esta planteado el programa, esto no es posible hacerlo conprogramacin convencional (secuencial). Analizaremos en este captulo el porque yveremos la solucin a dichos problemas. Comenzamos analizando un sencillo ejemplo.Supngase que se esta diseando un controlador de una mquina, que se plasmafinalmente en un regulador que podra tener el siguiente aspecto (en pseudocdigo):

    void main()

    {float referencia=3.0f;float K=1.2f;while(1){

    float medida=GetValorSensor();float error=referencia-medida;float comando=K*error;//regulador proporcionalEnviaComando(comando);

    }}

    Donde las funciones GetValorSensor() y EnviaComando()realizaran

    la interfaz correspondiente con el hardware de la mquina. Obviamente el programase tiene que ejecutar de forma continua, recalculando en cada pasada el nuevo error y

  • 5/21/2018 47508930-ProgramacionAvanzada

    48/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 48

    Universidad Politcnica de Madrid -UPM

    enviando un comando nuevo. El programa anterior utiliza una referencia (el punto alque se quiere llevar el sistema) fija. Supngase que ahora se desea que el usuario seacapaz de introducir por teclado dicha referencia tantas veces como quiera (para llevarla mquina a distintos puntos) y que se programa de la siguiente forma:

    void main(){

    float referencia=3.0f;float K=1.2f;while(1){

    printf("Introduzca referencia: ");scanf("%f",&referencia);float medida=GetValorSensor();float error=referencia-medida;float comando=K*error;EnviaComando(comando);

    }}

    El efecto conseguido es que el programa se queda parado en el scanf()esperando a la entrada del usuario. Cuando el usuario teclea un valor, se calcula yenva un comando a la mquina y el programa se vuelve a quedar parado en elscanf(). Si el usuario no teclea una nueva referencia, la mquina sigue funcionandocon el comando anterior de forma indefinida.

    Obviamente, la solucin anterior no es valida. Tenemos dos tareas diferentes:la ejecucin de forma continua del control y la interfaz con el usuario. Dichas tareastienen que ejecutarse de forma paralela a la vez. No podemos dejar de ejecutar elcontrol por el hecho de que el usuario este tecleando una referencia, ni podemos

    inhabilitar al usuario de teclear una referencia por el hecho de que se este ejecutandoel control de forma continua.

    La solucin es utilizar programacin concurrente. En el ejemplo anterior sepodra lanzar un hilo dedicado a la gestin de la entrada del usuario mientras que elhilo principal ejecuta el control. El programa en pseudo cdigo podra quedar as:

    float referencia=0.0f;//variable globalvoid hilo_usuario(){

    while(1){

    printf("Introduzca referencia: ");scanf("%f",&referencia);

    }}void main(){

    float K=1.2f;crear_hilo ( hilo_usuario );while(1){

    float medida=GetValorSensor();float error=referencia-medida;float comando=K*error;EnviaComando(comando);

    }}

  • 5/21/2018 47508930-ProgramacionAvanzada

    49/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 49

    Universidad Politcnica de Madrid -UPM

    Ntese como se ha puesto la variable referencia como global, para queambos hilos tengan acceso a la misma. Los hilos comunican informacin entre ellos atravs de memoria global de la aplicacin.

    3.2.REQUISITOSVamos a resumir las funcionalidades que nos quedan por implementar en

    nuestro sistema distribuido:

    Queremos permitir que los clientes se puedan conectar en el instanteque quieran. El servidor no debe quedar bloqueado por esperar a quelos clientes se conecten.

    Queremos permitir cualquier nmero de clientes espectadores. Dedichos espectadores, nicamente los dos primeros podrnefectivamente controlar las raquetas.

    Los dos primeros clientes que se conecten podrn controlar lasraquetas, el primero de ellos con las teclas w y s y el segundo con las

    teclas l y o.

    El servidor debe de gestionar adecuadamente las desconexiones de losclientes.

    3.3.FUNCIONAMIENTO DE GLUTEl funcionamiento bsico de la librera GLUT se plasma en la funcin

    glutMainLoop(),que es invocada desde el main():

    //los callbacksvoid OnDraw(void);void OnTimer(int value);void OnKeyboardDown(unsigned char key, int x, int y);

    int main(int argc,char* argv[]){

    //Inicializar el gestor de ventanas GLUT

    //y crear la ventanaglutInit(&argc, argv);glutInitWindowSize(800,600);glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);glutCreateWindow("ClienteTenis");

    //Registrar los callbacksglutDisplayFunc(OnDraw);glutTimerFunc(25,OnTimer,0);glutKeyboardFunc(OnKeyboardDown);//pasarle el control a GLUT,que llamara a los callbacksglutMainLoop();

    return 0;}

  • 5/21/2018 47508930-ProgramacionAvanzada

    50/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 50

    Universidad Politcnica de Madrid -UPM

    Dicha funcin contiene en su interior un bucle continuo (en caso contrarioterminara la funcin main() y terminara el programa). Dicho bucle continuo sepodra representar a nivel conceptual como:

    void glutMainLoop(){

    while(1){

    if(pulsacion_teclado)OnKeyBoardDown(tecla); //la funcion del usuario

    if(hay_que_dibujar)OnDraw(); //la funcion del usuario

    if(tiempo_temporizador)OnTimer();//la funcion del usuario

    }}

    Por lo tanto, si se introduce alguna funcin que bloquee la secuencia continuade ejecucin, la aplicacin se vera bloqueada por completo. Por ejemplo, supngase

    que se ubica un scanf() en la funcin CMundo::OnTimer() para cambiar elradio de la pelota:

    void CMundo::OnTimer(int value){

    printf("Introduzca el radio: ");scanf("%f",&esfera.radio);

    jugador1.Mueve(0.025f);jugador2.Mueve(0.025f);esfera.Mueve(0.025f);

    El resultado final es la aplicacin bloqueada.

    3.3.1Lanzando un hilo

    Podramos conseguir el anterior objetivo, mediante el uso de un hilo, de lasiguiente forma:

    void* hilo_usuario(void* d){

    CMundo* p=(CMundo*) d;while(1)

    {printf("Introduzca el radio: ");scanf("%f",&p->esfera.radio);

    }}void CMundo::Init(){

    //inicializaciones varias

    pthread_t thid;pthread_create(&thid,NULL,hilo_usuario,this);

    }

    En este caso, la esfera esta contenida dentro de la clase CMundo, sin embargo,el hilo es una funcin global, no es una funcin de la clase CMundo. Para conseguir el

  • 5/21/2018 47508930-ProgramacionAvanzada

    51/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 51

    Universidad Politcnica de Madrid -UPM

    acceso del hilo al objeto mundo, lo que se puede hacer es pasarle un puntero al

    mismo aprovechando el cuarto parmetro de la funcin pthread_create(). El hilose encargara a su vez de hacer el cast correspondiente para poder acceder a losmiembros de la clase CMundo.

    3.4.ESTRUCTURA DEL SERVIDORSe ha visto en los requisitos que es necesario realizar distintas tareas, de forma

    simultanea:

    El hilo principal del servidor se encargara de realizar la animacin de laescena (a travs de la funcin OnTimer), del dibujo y de enviar los datospor los sockets a los clientes. Como el envo no es bloqueante, no esnecesario crear un hilo para esta tarea.

    La aceptacin de nuevos clientes si que es bloqueante. Siempre se tieneque estar ejecutando el accept() si queremos que los clientespuedan conectarse y desconectarse cuando quieran. Por lo tanto esnecesario un hilo para esta tarea.

    Para que los clientes remotos puedan efectivamente jugar de formadistribuida, es necesario que enven informacin al servidor. Cada vezque se pulse una tecla, enviaran dicha tecla al servidor. El servidor debede estar esperando a dicho mensaje. El problema es que la recepcin demensajes, en principio tambin es bloqueante, por lo que el programa

    queda bloqueado hasta que se recibe dicho mensaje. La solucin esimplementar un hilo para cada uno de los dos jugadores que este a laespera de dichos mensajes.

  • 5/21/2018 47508930-ProgramacionAvanzada

    52/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 52

    Universidad Politcnica de Madrid -UPM

    Figura 3-1. Estructura del servidor

    Ntese adems que las frecuencias a las que funcionan los distintos hilos sonmuy variables. El hilo principal ejecuta cada 25 milisegundos, aproximadamente. Sinembargo el hilo de aceptacin de nuevos clientes ejecuta una iteracin del bucle cadavez que se conecta un nuevo cliente, lo que puede tardar de forma variable desdepocos milisegundos a infinito tiempo. Los hilos de recepcin de los comandos de los

    jugadores funcionan a una frecuencia variable que coincide con las pulsaciones deteclado de los jugadores.

    3.5.MLTIPLES CONEXIONES SIMULTANEASPara permitir la conexin simultanea de mltiples clientes, es necesario

    mantener un socket por cada uno de dichos clientes. Para tal efecto declaramos en laclase CMundo(del fichero MundoServidor.h) un vector de la STL de objetos de la claseSocket. Usamos un vector STL porque nos permite de forma cmoda aadir nuevosobjetos, quitar elementos y recorrerlo de forma sencilla. Tambin aadimos unmtodo a CMundo denominado GestionaConexiones(), que se encargara derealizar dicha gestin.

    class CMundo

    {public:

    Programa servidor

    //hilo principal

    OnTimer(){//tareas//animacion

    //envio//datos

    }

    //hilo de//aceptacion de

    //nuevos clientes

    while(1){

    //accept()}

    //hilo de

    //recepcion de//comandos del//jugador1

    while(1){

    //recv()}

    //hilo de

    //recepcion de//comandos del//jugador2

    while(1){

    //recv()}

  • 5/21/2018 47508930-ProgramacionAvanzada

    53/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 53

    Universidad Politcnica de Madrid -UPM

    Socket servidor;std::vector conexiones;void GestionaConexiones();

    };

    A continuacin lanzamos un hilo denominado hilo_conexiones(), y deforma similar a como hacamos anteriormente, pasamos un puntero al objeto actual(this) a dicho hilo. Como es interesante manejarnos dentro de la clase mundo, lanica tarea que tiene que hacer la funcin hilo_conexiones()es invocar al

    mtodo GestionaConexiones(). Dicho mtodo entrara en un bucle infinito enel que se repite un accept(). Cada vez que se conecte un cliente, se le aade alvector de clientes conectados.

    void* hilo_conexiones(void* d){

    CMundo* p=(CMundo*) d;

    p->GestionaConexiones();}void CMundo::GestionaConexiones(){

    while(1){

    Socket s=servidor.Accept();conexiones.push_back(s);

    }}void CMundo::Init(){

    //inicializacion datos

    servidor.InitServer("127.0.0.1",12000);pthread_t thid_hilo_conexiones;pthread_create(&thid_hilo_conexiones,NULL,hilo_conexiones,this);

    }

    3.6.MOSTRAR LOS CLIENTES CONECTADOSUna ampliacin interesante al apartado anterior seria mostrar en la ventana los

    clientes conectados y sus nombres, aparte de los puntos de los dos jugadores. Para elloaadimos un nuevo vector a la clase CMundodel servidor. Tambin transformamos lasvariables de los puntos de los jugadores en un vector:

    class CMundo{public:

    Socket servidor;std::vector conexiones;std::vector nombres;void GestionaConexiones();

    int puntos[2];};

  • 5/21/2018 47508930-ProgramacionAvanzada

    54/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 54

    Universidad Politcnica de Madrid -UPM

    Cada vez que se conecte un cliente nuevo nos deber enviar su nombre, paraaadirlo a nuestro vector. Por lo tanto segn se conecta un cliente, esperamos con unReceive()dicho mensaje con el nombre.

    void CMundo::GestionaConexiones(){

    while(1){

    Socket s=servidor.Accept();char cad[100];s.Receive(cad,100);nombres.push_back(cad);conexiones.push_back(s);

    }}

    Los nombres de los clientes pueden ser mostrados por pantalla:

    void CMundo::OnDraw(){

    char cad[100];sprintf(cad,"Servidor");print(cad,300,10,1,0,1);int i;for(i=0;i

  • 5/21/2018 47508930-ProgramacionAvanzada

    55/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 55

    Universidad Politcnica de Madrid -UPM

    3.7.RECEPCIN COMANDOS MOVIMIENTOCuando el programa cliente detecte una pulsacin de teclado, enviara dicha

    pulsacin al servidor, para que el servidor la interprete como juzgue necesario. El envodel cliente se realiza fcilmente en la funcin OnKeyboardDown():

    void CMundo::OnKeyboardDown(unsigned char key, int x, int y){

    char cad[100];sprintf(cad,"%c",key);cliente.Send(cad,strlen(cad)+1);

    }

    Ntese como este envo se realiza nicamente si el usuario pulsa una tecla. Elhilo implementado en el servidor tendr una forma similar al hilo anterior:

    void* hilo_comandos1(void* d){

    CMundo* p=(CMundo*) d;

    p->RecibeComandosJugador1();}void CMundo::RecibeComandosJugador1(){

    while(1){

    usleep(10);if(conexiones.size()>=1){

    char cad[100];conexiones[0].Receive(cad,100);unsigned char key;sscanf(cad,%c,&key);

    if(key=='s')jugador1.velocidad.y=-4;if(key=='w')jugador1.velocidad.y=4;

    }}std::cout

  • 5/21/2018 47508930-ProgramacionAvanzada

    56/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 56

    Universidad Politcnica de Madrid -UPM

    3.8.GESTIN DESCONEXIONESEn cualquier instante los clientes espectadores pueden desconectar. Qu pasa

    entonces con el vector de sockets mantenido por el servidor? Las desconexionesdeben de ser analizadas y gestionadas adecuadamente.

    La forma ms sencilla de detectar las desconexiones es en el envo realizadodentro de la funcin CMundo::OnTimer()en el lado del servidor. El envo hay quehacerlo a todos los clientes conectados. Podramos utilizar el retorno de Send()pararealizar la eliminacin del cliente del vector. No obstante hay que tener en cuenta losefectos del borrado sobre el vector que se est recorriendo.

    void CMundo::OnTimer(int value){

    for(i=0;i=conexiones[i].Send(cad,200)){

    conexiones.erase(conexiones.begin()+i);nombres.erase(nombres.begin()+i);if(i

  • 5/21/2018 47508930-ProgramacionAvanzada

    57/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 57

    Universidad Politcnica de Madrid -UPM

    Aadir una variable denominada acabar, que inicialmente vale 0 a laclase CMundo.

    Poner dicha variable a 1 en el destructor de la clase CMundo.

    Utilizar la variable como condicin de repeticin en los bucles while()

    de los hilos:

    while(!acabar){

    }

    Poner los identificadores de todos los hilos como variables de la claseCMundo, para que puedan ser utilizados en el pthread_join

    Ejecutar el pthread_join()tantas veces como sea necesario en el

    destructor de la clase CMundo, para esperar a que terminen los hilos.

    En este punto se analiza el resultado cuando se cierra el programa servidor.Realmente se est esperando a la finalizacin de los hilos? La respuesta es no. Loshilos estn bloqueados en el accept() y en el recv() por lo que aunquemodifiquemos la bandera acabar esta no es tenida en cuenta hasta la siguiente

    iteracin del bucle. Hay que conseguir que se desbloqueen el accept()y el recv()de los hilos, lo que se puede hacer de forma sencilla cerrando el socket del servidor,antesde los pthread_join()

    3.10. EJERCICIO PROPUESTO

    Realizar la misma tarea con otro juego de complejidad similar.

    Analizar los posibles problemas de sincronizacin que pueden apareceren caso de conexiones y desconexiones de clientes.

    Aumentar la informacin que se retransmite, para que los clientestengan tambin la informacin de quien esta conectado y quien esta

    jugando, as como los puntos actuales de la partida.

  • 5/21/2018 47508930-ProgramacionAvanzada

    58/157

  • 5/21/2018 47508930-ProgramacionAvanzada

    59/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 59

    Universidad Politcnica de Madrid -UPM

    44.. COMUNICACIN Y SINCRONIZACININTERPROCESO

    4.1.INTRODUCCIN

    Existen otros mecanismos para comunicar datos entre distintos procesosdiferentes a los sockets, cuando los procesos se ejecutan en una mquina con unamemoria principal comn y gestionada por un nico sistema operativo(monocomputador). A diferencia de la comunicacin por sockets, que se sueledenominar programacin distribuida, estos mecanismos entran dentro de ladenominada comunicacin interproceso (Inter Process ComunicationIPC). Entre estosmecanismos destacan:

    Las tuberas sin nombre (pipes) y con nombre (FIFOS)

    La memoria compartida

    El hecho de tener varios procesos (o hilos) accediendo a unos datos comunesde forma concurrente puede originar problemas de sincronizacin en esos datos. Paraprevenir estos problemas hay tambin otros mecanismos como:

    Los mutex y las variables condicionales

    Las tuberas (usadas para sincronizar)

    Los semforos

  • 5/21/2018 47508930-ProgramacionAvanzada

    60/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 60

    Universidad Politcnica de Madrid -UPM

    4.2.EL PROBLEMA DE LA SINCRONIZACIONCuando existen varios hilos accediendo de forma concurrente a unos datos, se

    pueden presentar problemas de concurrencia. En nuestra aplicacin, tenemos varioshilos accediendo de forma concurrente al vector de conexiones. En concreto el hilo

    principal, a travs del timer:void CMundo::OnTimer(int value){

    for(i=conexiones.size()-1;i>=0;i--){

    char cad[1000];sprintf(cad,"%f %f %f %f %f %f %f %f %f %f",

    esfera.centro.x,esfera.centro.y,jugador1.x1,jugador1.y1,jugador1.x2,jugador1.y2,jugador2.x1,jugador2.y1,

    jugador2.x2,jugador2.y2);if(0>=conexiones[i].Send(cad,strlen(cad)+1)){

    conexiones.erase(conexiones.begin()+i);nombres.erase(conectados.begin()+i);puntos[0]=puntos[1]=0;

    }}

    }

    El hilo de gestin de las conexiones:

    void CMundo::GestionaConexiones(){

    while(!acabar){Socket s=server.Accept();char cad[100];s.Receive(cad,100);nombres.push_back(cad);conexiones.push_back(s);

    }

    }

    Y los hilos de recepcin de mensajes de los jugadores:

    void CMundo::RecibeComandosJugador1(){

    Socket s;while(!acabar){

    usleep(10);if(conexiones.size()>0){

    char cad[100];conexiones[0].Receive(cad,100); //peligrosoprintf("Llego la tecla %c\n",cad[0]);unsigned char key=cad[0];if(key=='s')jugador1.velocidad.y=-4;if(key=='w')jugador1.velocidad.y=4;

    }

    }}

  • 5/21/2018 47508930-ProgramacionAvanzada

    61/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 61

    Universidad Politcnica de Madrid -UPM

    Ms concretamente: Es posible que mientras el hilo que recibe los mensajes deljugador decide que hay un jugador conectado (conexiones.size()>0), el hiloprincipal que enva los datos por el socket, se de cuenta que dicho cliente ha sidodesconectado y decida borrarlo del vector. Si el vector queda vacio, un acceso aconexiones[0] genera un error fatal segmentation fault, y nuestro servidor

    abortara de manera inesperada.

    No obstante, en la prctica es bastante improbable que suceda esto, yseguramente serian necesarias cientos de conexiones y desconexiones para que esteefecto fuera visible. Por lo tanto, no abordaremos de momento el problema de lasincronizacin, pero hay que tener en cuenta que en una aplicacin real seratotalmente obligatorio realizar esta sincronizacin, sino nuestro programa podra fallaren un momento inesperado.

    Sin embargo si hay un motivo por el que el servidor puede cerrarinesperadamente. Es la recepcin de la seal SIGPIPE cuando se intenta enviar algo por

    un socket que ha sido cerrado. Si no se gestiona esta seal, el comportamiento pordefecto termina el programa. La forma ms sencilla de obviar esta seal, es indicar a lafuncin send()en sus banderas, que no enve esta seal en caso de error, lo que sehace de la siguiente forma:

    int err=send(sock, cad, length,MSG_NOSIGNAL);

    4.3.COMUNICACIN INTERPROCESOEn este tema se propone el siguiente esquema como ejemplo del uso de

    distintos mecanismos de comunicacin interproceso:

    Figura 4-1. Ejemplo de comunicacin interproceso con tuberas y memoriacompartida

    RED

    TCP/IPLoggerFIFO Bot

    Memoriacompartida

    Servidor Cliente

  • 5/21/2018 47508930-ProgramacionAvanzada

    62/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 62

    Universidad Politcnica de Madrid -UPM

    En el computador que corre el servidor, se desarrollara un programa que sirvapara mostrar eventos de una forma ordenada por pantalla, aunque tambin podradecidir guardarlos a disco, a una base de datos, etc. Los eventos sern los puntosmarcados, y quien (el nombre del jugador) que ha marcado un tanto, y sern enviadosmediante cadenas de texto por una tubera con nombre o FIFO, al programa que

    llamaremos logger.

    En el lado del cliente se desarrollara un programa sencillo que pueda controlarlos movimientos de la raqueta correspondiente automticamente. A este programa lellamaremos bot. El cliente y la aplicacin bot intercambiaran datos en una zona dememoria compartida.

    Ambas aplicaciones nuevas sern aplicaciones de tipo consola. El makefile delas cuatro aplicaciones quedara como sigue:

    CC=g++CPPFLAGS=-g

    LIBS= -lm -lglut -lpthreadOBJS=Esfera.o Plano.o Raqueta.o Vector2D.o Socket.o

    all: servidor cliente bot logger

    logger: logger.o$(CC) $(CPPFLAGS) -o logger logger.o $(LIBS)

    bot: bot.o$(CC) $(CPPFLAGS) -o bot bot.o $(LIBS)

    servidor: $(OBJS) MundoServidor.o servidor.o$(CC) $(CPPFLAGS) -o servidor MundoServidor.o servidor.o $(OBJS)

    $(LIBS)cliente: $(OBJS) MundoCliente.o cliente.o

    $(CC) $(CPPFLAGS) -o cliente MundoCliente.o cliente.o $(OBJS)$(LIBS)depend:

    makedepend *.cpp -Yclean:

    rm -f *.o servidor cliente bot logger

    #DEPENDENCIAS

    4.4.TUBERAS CON NOMBRELas tuberas son un mecanismo tanto de comunicacin como de sincronizacin.

    Las tuberas sin nombre o pipes se utilizan en procesos que han sido creados mediantefork() y tienen relaciones padre-hijo, de tal forma que heredan dicha tubera.Cuando se trata de procesos totalmente separados, la tubera tiene que ser connombre para que ambos procesos sean capaces de acceder a ella.

    Las tuberas con nombre se direccionan como un archivo (un archivo especial)en la estructura de directorios. En las tuberas con nombre tiene que existir un procesoque se encargue de crear dicho pseudoarchivo, que adems tiene que ser el primer

    proceso que comience a ejecutar. Dicho proceso podra tener un cdigo como elsiguiente, para enviar por el FIFO una frase a otro proceso que se conecte al mismo:

  • 5/21/2018 47508930-ProgramacionAvanzada

    63/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 63

    Universidad Politcnica de Madrid -UPM

    #include #include #include #include #include #include

    int main(int argc,char* argv[]){

    mkfifo("/ruta/MiFifo1",0777);

    int pipe=open("/ruta/MiFifo1",O_WRONLY);

    char cad[150]=Hola que tal;int ret=write(pipe,cad,strlen(cad)+1);

    close(pipe);unlink("/ruta/MiFifo1");

    return 0;}

    Donde:

    mkfifo("/ruta/MiFifo1",0777);

    crea un archivo con un icono especial en forma de tubera en la ruta indicada, ycon los permisos correspondientes (0777= permisos de lectura, escritura y ejecucinpara todo el mundo).

    int pipe=open("/ruta/MiFifo1",O_WRONLY);

    La funcin open() abre dicha tubera con el acceso especificado (O_WRONLY,O_RDONLY, O_RDWR) y devuelve un descriptor de archivo (pipe) que es el utilizadopara enviar y recibir datos. Ntese que esta funcin bloquea hasta que se conectaalguien en el otro extremo de la tubera.

    A continuacin se hace un envo de datos:

    int ret=write(pipe,cad,strlen(cad)+1);

    Y finalmente se cierra la tubera y se elimina el pseudoarchivo

    close(pipe);unlink("/ruta/MiFifo1");

    El otro proceso nicamente debe de abrir la tubera, usarla y cerrarla, pero nocrear el archivo ni borrarlo. Lgicamente, este segundo proceso debe de ser arrancadodespus del anterior, para que la tubera sea creada primero antes de intentar abrirla.

    int main(void){

    int pipe=open("/ruta/MiFifo1",O_RDONLY);

    char cad[150];read(pipe,cad,sizeof(cad));

    printf("Cadena=%s\n",cad);

    close(pipe);return 1;

    }

  • 5/21/2018 47508930-ProgramacionAvanzada

    64/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 64

    Universidad Politcnica de Madrid -UPM

    Hay que recordar que la tubera es un mecanismo totalmente unidireccional, nopermite que el receptor enve datos por la misma tubera. Si se desea implementarcomunicacin bidireccional es necesario el uso de 2 tuberas.

    Ejercicio: Implementar el programa Logger y los cambios necesarios en el Servidor,

    para que este enve al Logger el nombre y nmero de puntos que lleva un jugadorsolo en el momento de marcar un tanto. Seguir los siguientes pasos:

    1. El programa Logger se ejecuta antes que el servidor, por lo tanto ser elencargado de crear y destruir el archivo.

    2. El logger entra en un bucle infinito de recepcin de datos.

    3. Aadir el identificador del FIFO como atributo de la clase CMundoen elservidor.

    4. Abrir la tubera (antes de lanzar los hilos)

    5.

    Enviar los datos cuando se produzcan puntos.6. Cerrar la tubera adecuadamente

    4.5.MEMORIA COMPARTIDALa memoria compartida es un mecanismo exclusivamente de comunicacin que

    permite tener en comn una zona de memoria, accesible desde varios procesos.Dichos procesos, una vez inicializada y accedida, vern la zona de memoria compartidacomo memoria propia del proceso. Esta forma de trabajar resulta muy interesante

    especialmente si la cantidad de datos a compartir entre los distintos procesos es muyelevada.

    Hay distintas interfaces a la memoria compartida, como las funciones de BSD yla memoria compartida POSIX. En este captulo se utiliza la memoria compartidaPOSIX.

    As un proceso que quisiera tener en comn una zona de memoria de 10 datosde tipo entero, compartida con otros procesos, podra hacer algo de la forma:

    #include #include #include #include #include #include

    int main(void){

    int datos[10]={0};

    //memoria compartidakey_t mi_key=ftok("/bin/ls",12);int shmid=shmget(mi_key,sizeof(datos),0x1ff|IPC_CREAT);char* punt=(char*)shmat(shmid,0,0x1ff);

  • 5/21/2018 47508930-ProgramacionAvanzada

    65/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 65

    Universidad Politcnica de Madrid -UPM

    while(1){

    int i,num;printf("Numero de dato: ");scanf("%d",&i);printf("Dato: ");

    scanf("%d",&num);datos[i]=num;memcpy(punt,datos,sizeof(datos));

    }

    shmdt(punt);shmctl(shmid,IPC_RMID,NULL);

    return 1;}

    Donde

    key_t mi_key=ftok("/bin/ls",12);

    obtiene una llave nica que sirve para identificar la zona de memoriacompartida. Los parmetros suministrados a esta funcin tienen que ser los mismos enlos diferentes procesos que utilicen la zona de memoria, y son un nombre de archivo(uno cualquiera existente en el sistema de archivos) y un numero entero.

    A continuacin se obtiene el descriptor mediante la funcin shmget(), a laque se le indica el tamao en nmero de bytes de la misma, los permisos (0x1ffsignifica acceso total a todos). En el caso que el proceso realmente quiera crear la zonaporque todava no existe, debe especificar la bandera IPC_CREAT.

    int shmid=shmget(mi_key,sizeof(datos),0x1ff|IPC_CREAT);

    La obtencin de un puntero, cuyo tipo se puede adaptar sencillamente con uncast, se obtiene con la funcin shmat(), a la que se especifican otra vez lospermisos particulares de este acceso.

    char* punt=(char*)shmat(shmid,0,0x1ff);

    El acceso posterior a la zona de memoria se puede hacer con algn tipo de cast,de indireccin por ndices de un vector o directamente copiando datos a esa zona dememoria. Una vez terminada de utilizar, es necesario soltar el puntero asignado yliberar la zona de memoria:

    shmdt(punt);

    shmctl(shmid,IPC_RMID,NULL);

    Como en el caso anterior, el proceso que efectivamente crea la zona dememoria debe de ser arrancado antes que los procesos que accedan a ella. Uno deestos procesos, podra tener el aspecto siguiente:

    #include #include #include #include #include #include

    int main(void){

    int datos[10];

  • 5/21/2018 47508930-ProgramacionAvanzada

    66/157

    Rodrguez-Losada & San Segundo, 2009.Programacin Avanzada, Concurrente y Distribuida 66

    Universidad Politcnica de Madrid -UPM

    int i;key_t mi_key=ftok("/bin/ls",12);int shmid=shmget(mi_key,sizeof(datos),0x1ff);char* punt=(char*)shmat(shmid,0,0x1ff);

    while(1)

    { memcpy(datos,punt,sizeof(datos));for(i=0;i=0;i--){

    char cad[1000];sprintf(cad,"%d %f %f %f %f %f %f %f %f %f %f",

    i,esfera.centro.x,esfera.centro.y,jugador1.x1,jugador1.y1,jugador1.x2,jugador1.y2,jugador2.x1,jugador2.y1,jugador2.x2,jugador2.y2);

    }

    El cliente, tambin aadir una variable denominada num_clientea la claseCMundo, y la extraer convenientemente de la cadena recibida.

    Es necesario aadir las variables siguientes a la clase CMundodel cliente, paraque acceda adecuadamente a la zona de memoria compartida:

    #include "DatosMemCompartida.h"

  • 5/21/2018 47508930-ProgramacionAvanzada

    67/157

    Rodrgue