sábado, 12 de noviembre de 2016

Entendiendo la programación de Sockets UDP con GTK# y .NET

UDP (User Datagram Protocol) es un protocolo de datagramas, no orientado a la conexión mucho más sencillo, poco fiable y de alto rendimiento, que a diferencia de TCP no incluye todas características para garantizar la entrega de paquetes, tales como recuperación de fallas, chequeo de errores o control de flujo. Debido a estas características UDP debe utilizarse en casos específicos, donde se asuma que el rendimiento es mejor que la fiabilidad:

  1. Cuando se transmite una pequeña cantidad de datos en periodos muy cortos de tiempo.
  2. Aplicaciones de “consulta y respuesta”, que trasmiten una consulta y esperan una respuesta inmediata.
  3. En sistemas de compresión para la transmisión de audio y video que pueden aceptar cierta cantidad de corrupción o perdida de paquetes.
  4. Algunas aplicaciones tienen su propio mecanismo de entrega de transporte, así que no necesitan de una capa de transporte. Estas aplicaciones usaran UDP mas eficientemente que TCP.

Por su rendimiento, UDP es recomendable en aplicaciones en donde es aceptable que algunos paquetes se pierdan durante la comunicación, como video streaming de video o algún protocolo multicasting como DNS.

La cabecera UDP tiene solo 4 campos: puerto origen, puerto destino, longitud y checksum. Los campos origen y destino tiene la misma funcionalidad que se tienen en la cabecera TCP. La longitud del campo especifica la longitud de la cabecera UDP y el campo checksum permite la revisar integridad del paquete.

Fig 1 Los campos del paquete UDP.

La clase UDPClient

La clase UdpClient es responsable de manejar mensajes punto a punto (point-to-point) y multicasting con el protocolo UDP; la clase UdpClient utiliza endpoints, esta clase puede ser construida inicialmente con el parámetro de un host o bien este parámetro puede especificarse durante el método Send().

Tambien proporciona un método Receive() que bloquea el hilo de ejecución hasta que un datagrama es recibido.

La clase UdpClient es una pequeña interfaz comparado a la clase TcpClient. Esto refleja la simple naturaleza de este protocolo en comparación con TCP. Mientras que ambas clases TcpClient y UdpClient utilizan un Socket internamente, la clase UdpClient no contiene un método para regresar un flujo de red (stream) para escribir y leer, en lugar de eso tiene un método Send() que acepta un arreglo de bytes como parámetro, mientras el método Receive() recibe un arreglo de bytes.

Fig 2 Miembros esenciales de la clase UDP Client.

Metodo o propiedad Descripción
Receive Recibe un datagrama UDP que fue enviado por una máquina remota.
Send Envia un datagrama UDP a una máquina remota.
Client Obtiene el socket subyacente que utiliza la clase.

Ejemplo de una comunicación entre un clientes y un servidor UDP

Es importante entender que en un modelo de comunicación sin conexión como UDP la comunicación se limita estrictamente a un modelo de petición/respuesta, que se lleva de la siguiente manera:

Fig 3 Modelo de comunicación petición/respuesta

Cada petición es un arreglo de bytes se salida procedente del cliente. Este arreglo de bytes se envía al servidor mediante TCP/IP, donde se convierte en la entrada del servidor. La respuesta entonces es el arreglo de bytes que salen del servidor para actuar como la entrada del cliente. Debido a la simpleza de este modelo, presenta las siguientes limitaciones:

  • Falta de conexión continua
  • Falta de fiabilidad
  • Seguridad insuficiente

Para mostrar como trabaja la comunicación entre un cliente y un servidor UDP utilizando la clase UdpClient escribí dos programas: un servidor y un cliente UDP. Ambos programas utilizan una interfaz de usuario (GUI) en GTK# para comunicarse entre ellos.

Fig 4 Ejemplo de un time server UDP con una GUI GTK#

Fig 5 Ejemplo de un cliente UDP con una GUI GTK#

Pasos para la construcción de un servidor UDP GTK#

El servidor UDP envía la fecha y la hora actual de la máquina a los clientes que se conecten en su número de puerto, durante los minutos que el servidor se encuentre activo, ambos el número de puerto y los minutos son especificados por el usuario en la pantalla.

El proyecto del servidor se compone de dos clases:

  1. La clase MainWindowServer.cs es la clase que construye la GUI del programa, obtiene los parámetros: número de puerto y minutos de ejecución del subproceso servidor, informa acerca de su estado o las posibles excepciones.
  2. La clase Program.cs es la clase principal que donde se ejecuta el servidor.

Para construir un servidor UDP se requieren de los siguientes pasos:

1) Crear una objeto UdpClient que al construirse sin argumentos se le asigna un número de puerto de la IP local.

    UdpClient udpClient = new UdpClient();

2) Construir un objeto IPEndpoint asociando una IP para este ejemplo utilice la dirección local y el número de puerto donde se conectara para enviar la respuesta (response) del servidor.

    IPEndPoint IPremoteEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"),serverPort);

3) Utilizar la clase Encoding para convertir el texto de la respuesta en un arreglo de bytes UTF8, y enviarla por la red con el método Send().

    byte[] data = Encoding.UTF8.GetBytes(response.ToString());
    udpClient.Send(data,data.Length,IPremoteEP);

Pasos para la construcción de un cliente UDP GTK#

El cliente UDP solicita un petición al número de puerto del servidor, recibiendo la respuesta como una matriz de bytes, la convierte a una cadena y la muestra en la pantalla.

El proyecto del cliente UDP GTK# se compone de 2 clientes:

  1. 1. La clase MainWindow.cs es la clase que construye la GUI del cliente, solicita los parámetros: numero de puerto y dirección IP para configurar la comunicación, maneja los eventos para recibir la respuesta del servidor y mostrar el resultado en pantalla o las posibles excepciones.
  2. 2. La clase Program.cs es la clase principal donde se ejecuta el cliente

Para construir un cliente UDP se requieren de los siguientes pasos:

1) Se crea un objeto UdpClient y se le especifica como parámetro el número de puerto al cual se enlazará.

    UdpClient client = new UdpClient(portToReceive);

2) Se crea el IPEndPoint para recibir la matriz de bytes con los parámetros IPAddress.Any (Indica que el servidor debe escuchar por clientes en todas las interfaces de red) y el número de puerto cero (para solicitar números de puertos dinámicos).

    IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);

3) Se obtiene el arreglo de bytes del IPEndPoint

    byte[] data = client.Receive(ref anyIP);

4) Se convierte el arreglo de bytes a una cadena

    text = Encoding.UTF8.GetString(data);

5) Una vez obtenido el mensaje se debe cerrar el Socket

    client.Close();

A continuación unas imágenes del cliente y del servidor comunicándose entre si.

Fig 6 Configurando el servidor de fecha y hora

Fig 7 Ejecutando el servidor de fecha y hora

Fig 8 Ejecutando el cliente GTK# Udp

Fig 9 Obteniendo la respuesta de servidor UDP

viernes, 9 de septiembre de 2016

Entendiendo la programación de Sockets con GTK# y .NET

¿Qué son los sockets?

Los Sockets o mejor dicho los Berkeley Sockets (BSD IPC) son la combinación exclusiva de una dirección IP y un numero de puerto TCP que habilita a los servicios (procesos en ejecución) de una computadora el poder intercambiar datos a través de la red. Los servicios de red utilizan los sockets para comunicarse entre los equipos remotos. Por ejemplo el siguiente comando:

        $ telnet 192.168.1.14 80
      

Este comando solicita una conexión desde un puerto aleatorio en el cliente (por ejemplo: 5643) al puerto 80 en el servidor (que es el puerto asignado para el servicio HTTP). Con la siguiente figura (fig 1) se ilustra a detalle este esquema denominado cliente-servidor.

Fig 1 Una comunicación utilizando números de puerto para transmitir datos.

Históricamente los sockets son una API (Application Programming Interface) de programación estándar utilizada para construir aplicaciones de red que vienen desde el sistema UNIX BSD. Esta interface de programación para la capa 4 del modelo OSI (OSI Model Layer 4) permite a un programador tratar una conexión de red como un flujo de bytes que puede escribirse o leerse sin demasiada complejidad. Con un socket se pueden realizar siete operaciones básicas:

  1. Conectarse a una máquina remota.
  2. Enviar datos.
  3. Recibir datos.
  4. Cerrar una conexión.
  5. Escuchar para los datos entrantes.
  6. Aceptar conexiones desde maquinas remotas en el puerto enlazado.
  7. Enlazarse a un puerto.

Los Sockets pueden ser orientados a la conexión (Stream Socket) o no (Message-based Socket).

Los Stream Sockets son ideales para transmitir grandes volúmenes de información de manera confiable. Una conexión Stream Socket se establece mediante el mecanismo three-hand shake de TCP, los datos se transmiten y cada paquete se revisa para asegurarse de la exactitud en la transmisión.

Los Datagram Sockets son apropiados para transferencias de datos cortas, rápidas y sin necesidad de un chequeo de errores. Los desarrolladores de aplicaciones los prefieren por ser rápidos y muy fáciles de programar.

Fig 2 Tipos de Socket

El Framework .NET posee las clases de alto y bajo nivel que encapsulan la funcionalidad de un Socket (tanto TCP como UDP) para construir aplicaciones de red con relativa facilidad y sin preocuparse por todo el intricado mecanismo de comunicación que necesitaría muchas líneas de código. La siguiente lista describe las clases principales:

  • NetworkStream: Una clase derivada de la clase Stream representa el flujo de datos de entrada o de salida desde la red.
  • TcpClient: Crea conexiones TCP de red para conectarse a un socket de servidor.
  • TcpListener: Se utiliza para escuchar peticiones de red TCP.
  • UdpClient: Crea conexiones UDP de red con posibilidad de multicasting.
  • Socket: Es una clase de bajo nivel que envuelve a la implementación winsock, las clases TcpClient, TcpListener y UDPClient utilizan esta clase para sus operaciones, se puede afirmar que la clase Socket tiene las operaciones de estas clases más otras funcionalidades mucho más avanzadas y de más bajo nivel.

Pasos para la construcción de un Servidor TCP GTK#

Un servidor TCP siempre está ejecutándose de forma continua hasta que recibe una solicitud de conexión por parte de un cliente, cuando se recibe esta solicitud, el servidor establece una conexión con el cliente y utiliza dicha conexión para el intercambio de datos. Si los programas se comunican a través de TCP los datos que se procesan se envían y se reciben como flujo de bytes.

Para demostrar estos conceptos escribí dos programas: el de un servidor y el de un cliente TCP. Ambos utilizan una interfaz de usuario (GUI) en GTK# para comunicarse entre ellos mediante mensajes de texto.

Fig 3 Ejemplo de un servidor TCP con una GUI GTK#.

Fig 4 Ejemplo de un cliente TCP con una GUI GTK#.

Pasos para la construcción de un Servidor TCP GTK#

El proyecto del servidor TCP GTK# se compone de 2 clases:

  1. La clase MainWindowServer.cs es la clase que construye la GUI del programa, maneja los eventos para enviar los mensajes al cliente y las excepciones o mensajes que el programa notifique.
  2. La clase Program.cs es la clase principal que donde se ejecuta el servidor.
Para construir el servidor TCP se requieren de los siguientes pasos:

1) Crear un objeto IPEndpoint que asocia una dirección IP y un número de puerto.

IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any,6000);

2) Crear un objeto TcpListener que reciba como argumento un objeto IPEndpoint. (Aquí el objeto TcpListener oculta la intricada programación de un Server Socket para una facilidad en la programación)

listener = new System.Net.Sockets.TcpListener(ipEndPoint); 

3) Iniciar el objeto TcpListener para que escuche las peticiones.

listener.Start();

4) Se utilizar un ciclo para que el Server Socket escuche o espere indefinidamente hasta recibir una petición, cuando el servidor recibe la petición crea una conexión hacia el cliente y regresa un objeto Socket del ensamblado System.Net.Sockets.Socket.

connection = listener.AcceptSocket();

5) Se obtiene el flujo de comunicación del Socket.

System.Net.Sockets.NetworkStream socketStream = 
new System.Net.Sockets.NetworkStream(connection);

6) Finalmente se asocia el flujo de comunicación del Server Socket con un escritor y un lector binario para transferir y recibir datos a través del flujo de comunicación.

using(writer = new BinaryWriter(socketStream))
{
using(BinaryReader reader = new BinaryReader(socketStream))
{
//the stream goes here 
}
}

Es muy importante que una vez que se finaliza la comunicación con el cliente cerrar el flujo y la conexión mediante con el método Close de cada uno de los objetos.

socketStream.Close();
connection.Close(); 

Pasos para la construcción de un cliente TCP GTK#

El proyecto del cliente TCP GTK# se compone de 2 clases:

  1. La clase MainWindow.cs es la clase que construye la GUI del cliente, maneja los eventos para recibir y enviar los mensajes al servidor, y mostrar las excepciones o mensajes que ocurran.
  2. La clase Program.cs es la clase principal del cliente

Para construir el cliente TCP se requieren de los siguientes pasos, algunos son idénticos a los que se escribieron para el servidor:

1) Crear un objeto IPEndpoint que asocia la dirección IP y el número de puerto del servidor, generalmente estos datos son fijos ya que los servidores se configuran para tenerlos de manera estática. (en este ejemplo utilice una sola máquina como servidor y como cliente)

  IPEndPoint localEndPoint = 
  new IPEndPoint(IPAddress.Loopback,6000);
  

2) Se crea un Socket de cliente y se conecta al puerto del servidor.

  client.Connect(localEndPoint);
  

3) Se obtiene el flujo de comunicación del Socket

  output = client.GetStream();
  

4) Se crean los objetos lector y escritor para trabajar con el flujo de comunicación.

   using(writer = new BinaryWriter(output))
   {
    using(reader = new BinaryReader(output))
    {
//the stream goes here 
    }
  }
  

Finalmente cuando se termina la comunicación con el servidor, se cierra el flujo de datos y la conexión con el método Close de cada objeto.

    output.Close();
    client.Close();
  

La clase TcpFlags

Ambos proyectos utilizan la clase TcpFlags, la cual pretende ilustrar básicamente como son las banderas TCP (aquí más detalles de las TCP Flags) que utiliza la capa de transporte (layer 4) para manejar la comunicación entre dos máquinas.

A continuación unas imágenes del cliente y servidor comunicándose entre si.
Fig 5 Enviando un mensaje desde el cliente al servidor.

Fig 6 Recibiendo el mensaje del cliente.

Fig 7 Enviándole un mensaje al cliente desde el servidor.

Fig 8 Desconectándose del servidor.

lunes, 8 de agosto de 2016

La navaja de Ockham o principio KISS en el desarrollo de software

La navaja de Ockham o el principio de parsimonia es el principio metodológico ideado por William of Ockham (1285-1347) filósofo nominalista que se utiliza para “cortar” o "rasurar" todo aquel conocimiento que no provenga de los datos de los sentidos, es decir invalidar los conceptos que no sean comprobables por la experiencia misma.

La formulación correcta de Ockham es:

No hay que multiplicar los entes sin necesidad (entia non sunt multiplicanda praeter necessitaem).

La navaja funciona de la siguiente manera: cuando se nos ofrecen dos o más hipótesis en igualdad de circunstancias para explicar un determinado fenómeno, es razonable aceptar la hipótesis más simple o sea la que incluye menos supuestos no probados.

En otros campos del conocimiento la navaja de Ockham se utiliza para eliminar objetos o tipos de objetos que no aportan o cambian en nada las particularidades efectivas de las cosas. En informática a la navaja de Ockham se le conoce como el principio KISS, siglas que significan:

Keep It Simple, Stupid! (Mantenlo simple, estúpido)

Aplicando el KISS al desarrollo de software tenemos que pequeño y sencillo es mejor que grande y complejo. En pocas palabras, entre más pequeño sea el número de componentes o de código involucrado en un proyecto menor será la cantidad de errores o problemas asociados al mantenimiento. Lo que incrementa la probabilidad de una entrega exitosa a tiempo.

Hay un principio de la filosofía UNIX que ejemplifica la aplicación de un principio KISS:

*“En lugar de tener pocos programas grandes, cada uno tratando de hacer muchas cosas, el sistema UNIX proporciona muchas herramientas simples que pueden combinarse para realizar un amplio rango de cosas.”
*Rosen Kenneth, Rosinki Richard, UNIX System V release 4. An introduction. Second Edition, Mc Graw Hill

Algunos ejemplos del KISS en el desarrollo de software son:

  • No intentes complicar el modelo de base de datos agregando entidades que nada aportan al negocio funcional. Solo agrega las entidades que requieren una persistencia.
  • No toda la persistencia necesita estar en una base de datos, hay ocasiones en que los archivos de texto plano son la mejor opción para el rendimiento.
  • No requieres un RDBMS para guardar una agenda de contactos.
  • No intentes optimizarlo si ni siquiera funciona o no esta terminado.
  • La complejidad de los algoritmos o el número de capas que se agreguen a una arquitectura son inútiles si estos no resuelven los requerimientos funcionales, y al serán ignorados por los usuarios finales.

Hay una frase que también es un claro ejemplo de este principio:

Premature optimization is the root of all evil. (la optimización prematura es la raíz de todos los males)

--Sir Charles Anthony Richard Hoare, inventor del algoritmo Quicksort.

Hay que aclarar que la navaja de Ockham no sostiene que la hipótesis más simple sea la correcta, sino sencillamente que es la que tiene más probabilidades de ser la correcta.

martes, 2 de agosto de 2016

Entendiendo TCP Control Flags (los bits de control TCP)

El protocolo TCP (Transport Control Protocol) proporciona la comunicación orientada a la conexión entre dos sistemas utilizando características tales como: Three-way handshake, reconocimientos, y números de secuencia. Adicionalmente a esas características TCP tiene 6 banderas de control (control flags) o bits de bandera que se usan para dominar la funcionalidad de las conexiones, los 6 bits que son utilizados durante el ciclo de la conexión son: Urgent (URG), Acknowledgment (ACK), Push (PSH), Reset (RST), Synchronize (SYN) y Finish (FIN).

La posición de cada uno de los bits es una bandera para indicar si una funcionalidad TCP está habilitada o deshabilitada, poniendo el valor del bit en 1 es ON, si es 0 significa OFF.

A continuación la descripción de cada uno de los bits por su correspondiente posición:

Urgent (URG): Se utiliza para notificarle a un sistema que el paquete tiene datos urgentes dentro del campo TCP que reconocen una actividad urgente o con prioridad, por ejemplo cuando un usuario presiona la combinación CTRL-BREAK, el resultado de este bit le indica a TCP que estos datos deben tomar precedencia sobre la transmisión de datos y enviarse inmediatamente.

Acknowledgment (ACK): El bit de reconocimiento se utiliza por uno de los sistemas para indicar el reconocimiento de un paquete TCP de otro sistema. Esto indica que hay un numero de reconocimiento en el campo de reconocimiento que se envía como respuesta a un petición SYNC.

Push (PSH): Este informa a TCP de entregar los datos inmediatamente hacia la capa superior sin almacenarlos en el buffer.

Reset (RST): Indica que una conexión TCP debe reiniciar la conexión, quizás debido a un error.

Synchronize (SYN): El bit de sincronización se utiliza para solicitar la conexión TCP con otro sistema. Una solicitud de conexión tiene SYN = 1 y ACK = 0.

Finish (FIN): Este bit se utiliza para terminar la conexión en un sistema, indica que el emisor no tiene más datos que enviar. La desconexión es simétrica.

Fig 1 TCP Header (la cabecera TCP).

Flags (UAPRSF)
  • U (1 = Urgent pointer valid)
  • A (1 = Acknowledgement field value valid)
  • P (1 = Push data)
  • R (1 = Reset connection)
  • S (1 = Synchronize sequence numbers)
  • F (1 = no more data; Finish connection)

sábado, 30 de julio de 2016

Entendiendo TCP Three-way Handshake

TCP (Transport Control Protocol) es un protocolo orientado a la conexión, lo que significa que establece la conexión, maneja el intercambio de datos y termina la conexión. Este protocolo establece una serie de pasos para establecer la conexión.

Para entender la programación con sockets TCP, es esencial conocer los pasos de la comunicación entre dos sistemas o mejor dicho entre un cliente y un servidor. A esta forma de establecer comunicación se le conoce como three-way handshake.

Fig 1 TCP Three-way handshake.

El proceso three-way handshake/call setup/virtual circuit setup es una única secuencia de tres paquetes de datos se intercambian al comienzo de una conexión TCP entre dos sistemas, la secuencia es la siguiente:

1-. En el primer paso del three-way handshake el cliente reserva un puerto para establecer la comunicación y envía un paquete SYN a la máquina objetivo que especifica la dirección IP, el puerto y un número de reconocimiento. El paquete SYNC tiene un número de secuencia (SEQ) asociado en este caso x. El número de secuencia es utilizado para rastrear los paquetes de datos que se transfieren entre el cliente y el servidor. La longitud del paquete enviado por el cliente tiene una longitud 0 (LENGTH = 0), esto indica que el paquete no tiene datos.

Fig 2 Primer paso: el paquete SYNC del cliente.

2-. En el segundo paso, el servidor responde con un paquete SYNC ACK, este paquete ACK es el reconocimiento que el servidor hace del paquete que recibió de parte del cliente. El número ACK adjuntado en el paquete tiene un valor del resultado de la suma de la secuencia (SEQ) del paquete 1 más la longitud del primer paquete. Aunque la longitud del paquete 1 es 0 este cuenta como un paquete por eso el ACK number = (cliente + 1). Este reconocimiento notifica al cliente que el paquete 1 fue recibido por el servidor. También el paquete SYN + ACK enviado por el servidor tiene un número de secuencia con valor y este número se utiliza para rastrear los paquetes enviados por el servidor.

Fig 3 Segundo paso: el paquete SYNC+ACK del server.

3-. El cliente reconoce la recepción del paquete del servidor. El número de ACK es un incremento del número de secuencia enviado por el servidor en el paso 2 o sea (y + 1), también el cliente también actualiza el número de secuencia que es la suma del número de secuencia que en el cliente envió en el primer paso más 1 o sea (x+1). Es importante recordar que cada uno, tanto el cliente y el servidor tienen su propio número de secuencia.

Fig 4 Tercer paso: el paquete ACK del cliente.

Una vez que el three-way handshake ha sido completado, se establece una comunicación bi-direccional sobre la conexión.

La importancia entre las aplicaciones basadas en TCP es que TCP soporta características tales como:

  • El re-ordenamiento de paquetes.
  • La repetición de los paquetes perdidos.
  • El reconocimiento de paquetes.
  • Un flujo de control.
Estas características son importantes para las aplicaciones basadas en mensajes.

La forma en que three-way handshake ayuda a sincronizar la conexión entre dos sistemas, emplea números de secuencia (SEQ) y reconocimiento (ACK) para indicar la transmisión de datos y la recepción. Las banderas (flags) de TCP controlan el flujo de sesión y es técnicamente de lo que se aprovechan los escaneadores de puertos por ejemplo. Son esas banderas las que se utilizan para recolectar información de los puertos.

Los números de puerto a diferencia de las direcciones IP no son únicos, aunque sean únicos para el sistema. Estos elementos forman los End Points entre sistemas. Mientras el sistema cliente utiliza números de puerto de manera aleatoria, los servidores en cambio utilizan números de puerto fijos, para facilitar la comunicación con varios sistemas. El RFC 1700 es un documento de consulta indispensable si se requiere información adicional acerca de los números de puerto asignados.

domingo, 24 de julio de 2016

Notas acerca de los riesgos en un proyecto de software

¿Qué es un riesgo?

No importa que tan bien se hagan los planes, siempre surgirán problemas inesperados. En ningún proyecto hay garantías, incluso la actividad más trivial conlleva problemas inesperados. En cualquier momento puede ocurrir algo que desvié el resultado de una actividad. Es un evento o condición incierta que, si se produce, tiene un efecto positivo o negativo en los objetivos del proyecto.

Factores de riesgo en un proyecto

  • Requerimientos excesivos
  • Falta de involucramiento del usuario o del cliente
  • Subestimación de la naturaleza dinámica o de la complejidad del proyecto
  • Costos o calendarios no realistas
  • Ineficiente administración de proyectos
  • Deficiencias en cada una etapas del ciclo de vida
  • Utilización de procesos o tecnologías inmaduras o no adecuadas para la medida del proyecto
  • Pobre capacitación

Algunos de los problemas que ocasionan los riesgos

  • Baja calidad en los productos de software
  • Retraso en el calendario
  • Perdidas en los costos
  • Moral baja

Características de un riesgo bien analizado

  • Tiene umbrales definidos claramente
  • La probabilidad e impacto estimados son realistas
  • Su plan de mitigación y de contingencias debe ser claro y efectivo
  • La responsabilidad de su monitoreo y control, está claramente asignada a uno o varios responsables

Existen los riesgos positivos

No todos los riesgos son negativos. Algunos eventos o condiciones pueden ser de ayuda para el proyecto por ejemplo: Alguna descuento en los costos de capacitación, encontrar un componente que ahorre tiempo de construcción, etc. Si esto sucede sin duda se le conocerá como una oportunidad, sin embargo desde la perspectiva de la administración de proyectos se le catalogará como riesgo.

Pasos para manejar un riesgo

    22
  • Evitarlo: Una de las mejores maneras para manejar un riesgo es evitarlo, si es posible hacer todo lo posible para que no suceda.
  • Mitigarlo: Si no es posible evitar el riesgo, deben tomarse acciones para disminuir los efectos que se originen cuando el riesgo se transforme en un problema.
  • Transferirlo: Buscar la ayuda de otra instancia que tenga la capacidad para mitigarlo o desaparecerlo.
  • Aceptarlo: Cuando no puede evitarse, mitigarse o transferirse deberá de aceptarse para no caer en la frustración y el desanimo.

lunes, 18 de julio de 2016

Entendiendo conceptos básicos de Threads con GTK#

Históricamente el soporte para las operaciones en paralelo u operaciones concurrentes en los lenguajes de programación no era común, la mayoría de lenguajes de programación proporcionaban dicho soporte como primitivas del sistema operativo que eran muy difíciles de programar y de mantener.

Fue hasta las décadas de los 70’s y 80’s del siglo XX que el departamento de defensa de los Estados Unidos construyo el lenguaje de programación Ada que entre sus capacidades permite a los programadores especificar la programación en paralelo.

Una de las técnicas mas populares para la programación concurrente son los hilos (Threads) esta técnica se basa en el principio de la multi-programación propia de los sistemas operativos modernos.

¿Qué es un Thread?

Un thread es la unidad básica de ejecución de un programa. En otras palabras, es la unidad más pequeña de ejecución para la cual un procesador reserva tiempo de procesador. Es otra forma de que varias actividades se ejecuten al mismo tiempo con el objetivo de paralelizar el código e incrementar el rendimiento de un programa, por eso a los threads se les conoce como procesos livianos (lightweight processes), ya que estos corren en paralelo como si fueran procesos aunque todos se ejecutan dentro de un mismo proceso que comparten recursos críticos tales como la memoria y el CPU. Un thread se compone de:

  • Un Thread ID similar al Process ID.
  • Un contador de programa.
  • Un conjunto de registros.
  • Una pila.

A diferencia de un proceso los hilos comparten recursos como la sección de datos, la sección de código, los descriptores de archivo o las señales entre otras.

Beneficios de la programación multithread

  • El Multithreading (multihilado) permite a un proceso ejecutar múltiples tareas en paralelo. A cada tarea se le otorga su propio hilo de control, lo que ofrece los siguientes beneficios:

  • Rendimiento: Porque todos los Threads se ejecutan dentro de un mismo proceso, el proceso no desperdicia recursos en hacer una copia de él mismo. Los costos de copiar procesos fork y de ejecutar threads varían según el sistema operativo, aun así los threads son menos consumidores de recursos.

  • Simplicidad: Los hilos son mucho más sencillos de programar y de mantener que los procesos.

  • Compartir la memoria global: Como los hilos se ejecutan en un mismo proceso, cada hilo comparte el mismo espacio de direcciones de la memoria global del proceso.

  • Portabilidad: Definitivamente los hilos son más portables que los procesos fork u otros mecanismos de programación concurrente.

  • Capacidad de respuesta: Es posible que un hilo se mantenga haciendo una actividad, por ejemplo de descarga de un archivo mientras que otro puede continuar con una actividad de E/S, aquí se puede programar un esquema basado en el bloqueo de un hilo a nivel individual.

Estados de un Thread, su ciclo vital

Todo hilo debe cumplir con un ciclo de vida, este ciclo de vida comienza con un Thread en el estado Unstarted (inactivo) que es cuando se inicia, el hilo permanecerá en ese estado hasta que se invoque su método Start (Inicia) y así pasar al estado Running (en ejecución) que es cuando el sistema operativo le asigna un procesador, cuando esta en ejecución el hilo ejecuta todas las actividades que están en su delegado ThreadStart hasta terminarlas, una vez terminadas estas actividades pasa al estado Stopped (detenido) ya en este estado termina su ciclo, aunque un hilo puede pasar a este estado sin terminar sus actividades si se invoca su método Abort (abortar).

Otros estados para un hilo son:

  1. Blocked (Bloqueado) cuando el hilo tiene problemas en operaciones de E/S en donde necesita esperar por un recurso disponible.
  2. WaitSleepJoin cuando se le pide al hilo que no se ejecute un determinado tiempo.
  3. Suspend en este estado el hilo se interrumpe hasta que alguien invoque a su método Resume para regresar a la ejecución; en estos estados los hilos no pueden utilizar un procesador así se encuentre uno disponible.

Como nota adicional los métodos Suspend y Resume se encuentran obsoletos por lo que esta funcionalidad se implementa con la clase Monitor.

Para demostrar los conceptos básicos como la creación, el arranque y paro de un Thread he escrito el siguiente código como un ejemplo de como se escribe la creación de uno y la invocación de sus métodos Start() y Abort() respectivamente.

El siguiente programa es una sencilla animación en GTK# que dibuja rectángulos con un color diferente cada determinado tiempo utilizando las clases de dibujo de los ensamblados System.Drawing y gtk-dotnet. Este programa tiene dos botones: play y stop. El botón play inicia la animación en tanto que el botón stop detiene la animación.

Fig 1 Programa GTK# de una animación con un Thread.

  • 1-. La clase program que es la clase inicial del programa que tiene al método Main(string[] args)
  • 2-. La clase MainWindow que contiene la interfaz gráfica del programa aquí se crea el Thread y se controla su comportamiento por medio de los botones.
  • 3-. La clase ColorBoxesCanvas que se encarga de dibujar las figuras de la animación.

Para escribir el ejemplo creamos un proyecto en Monodevelop, creamos las clases, escribimos el código en cada clase correspondiente y compilamos. La solución debe verse como en la siguiente imagen:

Fig 2 Los archivos de la solución en Monodevelop.

Una vez compilada la solución la ejecutamos como se verá como en la siguiente imagen:

Fig 3 El programa al iniciar de su ejecución.


El programa muestra dos botones: play y stop, al oprimir el botón play se ejecuta el método Run()

 protected void Run(object sender,System.EventArgs e)
  {
   worker = new Thread(canvas.Repaint);
   canvas.KeepRunning = true;
   worker.Start();
   controlButtons[0].Visible = false;
   controlButtons[1].Visible = true;
   lblStatusBar.Text = "Running thread ID: " + Thread.CurrentThread.ManagedThreadId
    + " at " + DateTime.Now.ToLongTimeString();
  }

Dentro de este método se crea un objeto Thread llamado worker al que se le pasa el delegado canvas.Repaint como argumento al constructor del objeto. Este delegado especifica la acción que realizará el subproceso durante su ciclo de vida, para este ejemplo el delegado es un método void que no recibe argumentos. Aquí el hilo permanece en estado Unstarted hasta que se llama a su método Start(), ejecutando este método el hilo pasa al estado Running y devuelve el control del programa al proceso que invoco al método Start().

Fig 4 El programa al presionar el botón play y llamar al método Start().

Una vez en estado running el Thread puede pasar al estado Stopped o Aborted cuando termina la ejecución del delegado ThreadStart, esto indica que el subproceso terminado la tarea. Un hilo en ejecución puede forzar entrar al estado Stopped cuando se invoca al método Abort(), después de invocar este método el subproceso se detiene.

 protected void Abort(object sender, System.EventArgs e)
  {
   if (worker.IsAlive)
   {
    worker.Abort();
    controlButtons[0].Visible = true;
    controlButtons[1].Visible = false;
   }
   lblStatusBar.Text = "Stopping thread ID: " + Thread.CurrentThread.ManagedThreadId
    + " at " + DateTime.Now.ToLongTimeString();
  }
Fig 5 El programa al presionar el botón stop y llamar al método Abort()