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.


El código fuente de este programa GTK# se divide en 3 clases:

  • 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.

Aquí el código fuente de la clase Program.cs

using System;
using Gtk;

namespace ColorBoxesThread
{
 class MainClass
 {
  public static void Main(string[] args)
  {
   Application.Init();
   MainWindow win = new MainWindow();
   win.Show();
   Console.WriteLine("GTK# Clock example running...");
   Application.Run();
  }
 }
}

Aquí el código fuente de la clase MainWindow.cs

using System;
using Gtk;
using System.Threading;

namespace ColorBoxesThread
{
 public class MainWindow : Gtk.Window
 {
  ColorBoxesCanvas canvas = null;
  Thread worker = null;
  VBox mainLayout = new VBox();
  HBox controlButtonsLayout = new HBox();
  Label lblStatusBar = new Label("Ready");
  Button[] controlButtons = { 
   new Button("gtk-media-play"),
   new Button("gtk-media-stop")
  };

  public MainWindow() : base(Gtk.WindowType.Toplevel)
  {
   this.Title = "Sample GTK# thread example";
   this.SetDefaultSize(343, 311);
   this.DeleteEvent += new DeleteEventHandler(OnWindowDelete);
   this.BorderWidth = 6;
   canvas = new ColorBoxesCanvas();
   //Adjustment the controls
   mainLayout.BorderWidth = 1;
   mainLayout.Spacing = 6;

   controlButtonsLayout.Spacing = 3;
   controlButtonsLayout.BorderWidth = 1;
   controlButtonsLayout.Homogeneous = true;

   for (var i = 0; i < controlButtons.Length;i++)
   {
    controlButtons[i].CanFocus = true;
    controlButtons[i].UseStock = true;
   }
   //Setting the control panel

   controlButtons[0].Clicked += new EventHandler(Run); 
   controlButtons[1].Clicked += new EventHandler(Abort);

   //Adding the button panel
   foreach (var button in controlButtons)
   {
    controlButtonsLayout.Add(button);
   }
   //Adding to the layout
   mainLayout.Add(canvas);
   mainLayout.Add(controlButtonsLayout);
   mainLayout.Add(lblStatusBar);
   this.Add(mainLayout);
   this.ShowAll();
  }

  protected void OnWindowDelete(object o, DeleteEventArgs args)
  {
   if (worker.IsAlive)
    worker.Abort();
   Application.Quit();
  }

  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();
  }

  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();
  }
 }
}

Aquí el código fuente de la clase ColorBoxesCanvas.cs

using System;
using Gtk;
using System.Drawing;

namespace ColorBoxesThread
{
 public class ColorBoxesCanvas : DrawingArea
 {
  public bool KeepRunning { set; get; }
  int sleepTime = 100;
  int red = 0,green = 0,blue = 0;
  Random random1 = new Random((int)DateTime.Now.Ticks & 0x0000FFFF);
  Random random2 = null;
  Random random3 = null;


  public ColorBoxesCanvas()
  {
   random2 = new Random((int)DateTime.Now.Ticks & 0x0000FFFF);
   SetSizeRequest(341,266);
   this.ExposeEvent += new ExposeEventHandler(ExposeHandler);
  }

  public void Repaint()
  {
   Console.WriteLine("Thread running...");
   while (KeepRunning)
   {
    this.ExposeEvent += new ExposeEventHandler(ExposeHandler);
    this.QueueDrawArea(0, 0, this.Allocation.Width, this.Allocation.Height);
    System.Threading.Thread.Sleep(sleepTime);
   }
  }

  protected void ExposeHandler(object o,ExposeEventArgs args)
  {
   random3 = new Random((int)DateTime.Now.Ticks & 0x0000FFFF);
   Gdk.EventExpose ev = args.Event;
   Gdk.Window window = ev.Window;
   using (Graphics g = Gtk.DotNet.Graphics.FromDrawable(window))
   {
    SolidBrush backBrush = new SolidBrush(Color.White);
    g.FillRectangle(backBrush,0,0,this.Allocation.Width,this.Allocation.Height);
    for (int j = 30; j < Allocation.Height - 15; j += 30)
    {
     for (int i = 5; i < Allocation.Width - 15; i += 30)
     {
      red = GetValue(random1);
      green = GetValue(random2);
      blue = GetValue(random3);
      SolidBrush foreBrush = new SolidBrush(Color.FromArgb(red,green,blue));
      g.FillRectangle(foreBrush,i, j, 25, 25);
      Pen forePen = new Pen(Color.Black);
      g.DrawRectangle(forePen,i - 1, j - 1, 25, 25);
     }

    }
   }
  }

  static int GetValue(Random r)
  {
   byte[] values = new byte[6];
   r.NextBytes(values);
   int index = new Random().Next(0, 6);
   int face = values[index];
   return face;
  }
 }
}

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()


Download el código fuente para Xamarin Studio o Visual Studio