lunes, 26 de diciembre de 2016

Usando el control de validación CompareValidator ASP .NET - parte II

Como un complemento al primer ejercicio del control ASP .NET CompareValidator, aqui presento un segundo ejemplo que muestra como utilizar el control CompareValidator para comparar el valor de un control ASP.NET con un tipo específico de dato .NET (Currency, Date, Double, Integer, String) o bien comparar si teniendo dos valores: uno es menor o mayor al otro o si ambos valores son iguales.

Este segundo ejemplo consta de dos archivos: CompareValidatorSample2.aspx y CompareValidatorApp2.aspx.cs

El código fuente del archivo CompareValidatorSample2.aspx

<%@ Page
 Language           = "C#"
 AutoEventWireup    = "false"
 Inherits           = "Samples.ASP.CompareValidatorApp2"
 ValidateRequest    = "false"
 EnableSessionState = "false"
%>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>ASP.NET Compare validator sample 2</title>  
 </head>
 <body>
  <form id="Form1" method="post" runat="server">
  <p></p>
    <table>
    <tr>
        <td>Decimal average</td>
        <td><asp:TextBox ID="txtAverage" runat="server">
        </asp:TextBox></td>
        <td><asp:CompareValidator ID="cmpvtxtAverage" runat="server" 
        Type="Double" 
        ControlToValidate="txtAverage" 
        Operator="DataTypeCheck" 
        ErrorMessage="* must be decimal">
        </asp:CompareValidator>
        <asp:RequiredFieldValidator id="reqvtxtAverage" 
        Runat="Server" 
        ControlToValidate="txtAverage" 
        ErrorMessage="* required">
        </asp:RequiredFieldValidator></td>
    </tr>
    </table>
    <table>
    <tr>
    <td colspan="4">Type 2 integers,the second integer must be greater than first</td>
    </tr>
    <tr>
        <td>First integer</td>
        <td colspan="2">
   <asp:TextBox ID="txtValue1" 
         runat="server">
         </asp:TextBox>
  </td>
  <td>
         <asp:RequiredFieldValidator id="reqvtxtValue1" 
         Runat="server" 
         ControlToValidate="txtValue1" 
         ErrorMessage="* required">
         </asp:RequiredFieldValidator>
  </td>
 </tr>
 <tr>
        <td>Second integer</td>
        <td colspan="2">
   <asp:TextBox ID="txtValue2" 
         runat="server">
         </asp:TextBox>
  </td>
  <td>
        <asp:RequiredFieldValidator id="reqvtxtValue2" 
        Runat="server" 
        ControlToValidate="txtValue2" 
        ErrorMessage="* required">
        </asp:RequiredFieldValidator>
  </td>
    </tr>
    <tr>
        <td colspan="4">
        <asp:CompareValidator ID="cmpvTwoNumbers" 
        runat="server" 
        ControlToValidate="txtValue1" 
        ControlToCompare="txtValue2"
  Type="Integer"
        Operator="LessThan" 
        ErrorMessage="* first value greater than second">
        </asp:CompareValidator>
  </td>
    </tr>
    </table>
 <br>
    <asp:Button id="btnSubmit" 
    runat="server" 
    Text="Send"></asp:Button>
 <p><asp:Label id="lblMsg" 
 Runat="server">
 </asp:Label></p>
  </form>
 </body>
</html>

El código fuente del archivo CompareValidatorApp2.aspx.cs


Fig 4 El ejemplo muestra la comparación de un valor contra un tipo específico de dato y la comparación del valor de un control contra el valor de otro control.


Fig 5 Comparando el valor de un control con un tipo específico de dato .NET.


Fig 6 Comparando el valor de un control contra el valor de otro control.


Algunas propiedades esenciales del control CompareValidator.

  • ControltoValidate: El control de donde obtendremos el valor a comparar
  • ControlToCompare: El control con el cual se compara el valor obtenido de la propiedad ControltoValidate
  • Display: Esta propiedad tiene 3 valores:
    1. Static: es la propiedad predeterminada, reserva un espacio suficiente en la página para mostrar el mensaje de error.
    2. Dynamic: el espacio para mostrar el mensaje no se reserva, cuando el mensaje se despliega se desplaza el contenido existente en la página.
    3. None: el mensaje no será desplegado en el lugar del control sino en el control ValidatorSummary si se localiza en la misma página.
  • Errormessage: el texto del mensaje que se desplegará si no se cumplen las condiciones.
  • Operator: La operación que efectuará el control en los valores de los campos, los valores son los siguientes:
    1. LessThan:El valor del ControlToValidate debe ser menor al valor del ControlToCompare.
    2. Equal: El valor del ControlToValidate debe ser igual al valor del ControlToCompare.
    3. NotEqual: El valor del ControlToValidate no debe ser igual al valor del ControlToCompare.
    4. LessThanEqual: El valor del ControlToValidate debe ser menor o igual al valor del ControlToCompare.
    5. GreaterThan: El valor del ControlToValidate debe ser mayor al valor del ControlToCompare.
    6. GreaterThanEqual: El valor del ControlToValidate debe ser mayor o igual al valor del ControlToCompare.
  • Type: El tipo de datos de los valores a comparar, los tipos de datos disponibles para este control son:
    1. Currency: Moneda
    2. Date: Fecha
    3. Double: Valor de punto flotante
    4. Integer: Entero sin punto decimal
    5. String: Cadena

Descargar el código fuente del ejemplo (Download the source code)

miércoles, 21 de diciembre de 2016

Notas acerca de la recursión en C#

La recursión es una técnica de programación que se programa mediante un método que ya sea por iteración o por decisión resuelve un problema hasta llegar a un caso base , un método recursivo es un método que se llama así mismo ya sea directamente o indirectamente a través de otro método. Los enfoques recursivos para resolver problemas tienen varios elementos en común. El método en si sabe como resolver el o los casos más sencillos los llamados casos base.

Como un ejemplo ilustrativo, escribí un sencillo programa que contiene las funciones Fibonacci y factorial.


El programa se escribió en un proyecto Xamarin

Fig 1. El programa en el entorno de Xamarin


Para ejecutar el programa desde una consola de terminal, habilitar en el proyecto la opción "Run on external console"

Fig 2. Habilitando la opción "Run on external console"


Fig 3. El programa en ejecucción


Descargar el código fuente del ejemplo (Download the source code)

lunes, 19 de diciembre de 2016

Usando los controles de validación ASP.NET IV (CompareValidator)

Otra de las tareas muy útiles al construir formularios para aplicaciones ASP.NET es poder comparar el valor de un control en el formulario con el valor de otro control, en estos casos se utiliza el control CompareValidator, este control también puede comparar el valor del control con un valor constante o si se requiere asegurarnos que ese valor sea del tipo de datos que nosotros necesitamos.

Como primer ejemplo de este control mostraré la una página ASP.NET con unos campos solicitando una contraseña y su confirmación.

Este primer ejemplo consta de dos archivos: CompareValidatorSample.aspx y Samples.ASP.CompareValidatorApp.aspx.cs.

El código fuente del archivo CompareValidatorSample.aspx

El código fuente del archivo Samples.ASP.CompareValidatorApp.aspx.cs

Fig 1 Utilizando el control CompareValidator para confirmar contraseñas.


Fig 2 Si las contraseñas no son idénticas el control muestra un mensaje.


Fig 3 Si las contraseñas coinciden, se envían los datos al servidor.


Descargar el código fuente del ejemplo (Download the source code)

miércoles, 14 de diciembre de 2016

Enumeraciones con C#

Un tipo de datos de enumeración enum es usado para definir un conjunto de elementos constantes, este tipo de datos se usan para agrupar constantes y en programas de opción múltiple. Las enumeraciones pueden ser de cualquier tipo de datos (integer, short, long) excepto el tipo de datos char.

Un breve ejemplo:


Ahora con esta enumeración podemos hacer un programa que tenga el siguiente método:


La ejecucción del programa producirá la siguiente salida.

Fig 1. La ejecucción del ejemplo de enumeraciones


Download el código fuente del ejemplo para Xamarin o VS

domingo, 4 de diciembre de 2016

Entendiendo las expresiones lambda (lambda expressions) en C#

Introducción

La programación imperativa es uno de los paradigmas de computación mas ampliamente utilizados por la mayoría de lenguajes de programación de alto nivel debido al gran soporte académico y comercial y a que los programas son relativamente independientes del tipo de computadora donde se ejecutan porque los lenguajes de programación deben abstraer las operaciones del modelo de maquina para la cual se diseñaron.

La programación imperativa se basa en el modelo de la máquina de Von Neuman, del cual la mayoría de las computadoras personales y estaciones de trabajo tienen elementos comunes.

Aunque menos utilizado existe otro paradigma que a diferencia del imperativo se basa en las matemáticas (aplicación de funciones) con el cual igualmente podemos expresar operaciones computacionales de forma mas compacta y abstracta, este paradigma se conoce como programación funcional.

Uno de los muchos elementos del paradigma funcional que .NET incluye desde la versión 3.0 son las expresiones lambda (lambda expression).

Programación Funcional

Los conceptos básicos de la programación funcional datan de 1956 a 1958 con el trabajo de Jonh McCarthy en el desarrollo y diseno de LISP (List Processor), este lenguaje esta basado en el calculo lambda que sento las bases de los lenguajes funcionales, características como:

  • Recursión: se utiliza para para realizar operaciones repetitivas, no utiliza la iteración.
  • Funciones de primer orden: las funciones tienen el mismo nivel que cualquier otro elemento del lenguaje,pueden aplicarse a valores, evaluarse, regresar un valor y ser parámetros de otras funciones.
  • No requiere asignación: el computo se realiza aplicando funciones a los argumentos.
  • Garbage collector: Se reclaman los objetos que no están siendo utilizado por el programa.
  • Tipado dinámico (Dynamic typing): La comprobación del tipo se realiza en tiempo de ejecución, los valores tienen tipos, pero las variables no.

El paradigma funcional se basa en el concepto matemático de función, que la mayoria de los lenguajes de programación imperativos y funcionales comparten y cuya definición es

(1)Una función f es una regla que asigna a cada elemento x de un conjunto A exactamente un elemento llamado f(x) de conjunto B

Donde la programación funcional marca su diferencia con la imperativa es que para la programación funcional cada programa es equivalente a esta definición, donde x es el argumento o dominio de f mientras que y es el rango de f o la salida sea los programas son cajas negras donde solo importa el que se esta computando y no el como se esta computando que es el caso de la programación imperativa.

En resumen cuando se programa de forma funcional se piensa más en expresiones y su significado que en una secuencia de operaciones en memoria.

Tipos Delegate y métodos anónimos

Desde sus primeras versiones .NET introdujo el objeto delegate (delegado) que es un tipo particular de objeto (un delegate deriva de la clase base System.Delegate), que puede encapsular la referencia a un método estático o de una instancia como si fuera un mecanismo de callback(devolución de llamada) similar a los apuntadores de función de C y C++ pero con la importante diferencia de que proporciona un tipado seguro (type-safety) para evitar errores en tiempo de ejecución y que puedan detectarse en tiempo de compilación si la función no coincide con la firma del método al que hace referencia.

Esto posibilita en un contexto de programación orientada a objetos que los métodos pueden recibir como argumentos otros métodos además de tipos primitivos y de referencia. Veamos un ejemplo para ilustrar estos conceptos con C#.

En versiones anteriores a C# 2.0 (1.1,1.0) los delegate se utilizaban como parámetros, veamos el siguiente ejemplo que se compone de dos clases:

  1. La clase Temp.cs que es la clase que encapsula las operaciones.
  2. La clase Program.cs que es la clase principal que asigna los métodos al delegado.

Ejemplo 1.1 Uso de métodos como parámetros en C# 1.1.

Aquí código fuente completo de la clase Temp.cs


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


Observamos que los métodos que implementan la funcionalidad deben declararse de una manera completamente procedural e imperativa.
public static double GetCelsius(double fahrenheit)
  {
   return (fahrenheit - 32) * (5 / 9D);
  }

  public static double GetKelvin(double fahrenheit)
  {
   return fahrenheit + 460;
  }

Las versiones posteriores de C# al incorporar los métodos anónimos se acerca más a la programación funcional al asociar un bloque de código a un delegate sin necesidad de tener toda su implementación en un método sino dentro de la misma declaración del objeto, como se muestra en el siguiente código que es el mismo código anterior pero usando métodos anónimos.

Ejemplo 1.2 Uso de métodos como parámetros utilizando métodos anónimos.


Aquí observamos la diferencia con respecto al código anterior del Ejemplo 1.1.

    Console.WriteLine("{0} Fahrenheit = {1:0.00} Celsius", x, ApplyF(x,
    delegate (double fahrenheit)
   {
    return (fahrenheit - 32) * (5 / 9D);
   }));
   Console.WriteLine("{0} Fahrenheit = {1:0.00} Kelvin", x,
   ApplyF(x,
            delegate (double fahrenheit)
   {
    return fahrenheit + 460;
   }));

Expresiones Lambda (Lambda Expressions)

Las expresiones lambda provienen del cálculo lambda (lambda calculus) desarrollado por Alonzo Church en los años 1930’s como una notación para representar todas las funciones computables equivalentes a una máquina de Turing, todos los lenguajes funcionales pueden ser vistos como una variante sintáctica del cálculo lambda.

Las expresiones Lambda son útiles para sintetizar funciones con pocos parámetros que regresan algún valor, esta expresión consiste básicamente en una regla de sustitución que expresa tal cual una función o sea un mapeo de los elementos del conjunto dominio a los elementos de un codominio por ejemplo en la siguiente expresión:

        cuadrado : integer → integer donde cuadrado(n) = n²
    

Se reduce a una notación que produce una función anónima donde los únicos símbolos son la letra lambda (λ) y el punto (.).

    λn.n²

En la mayoría de los lenguajes funcionales las funciones anónimas son valores representados por la palabra reservada lambda, como el caso de LISP.

Lambda (n)(**n)

Aunque C# no utiliza los símbolos de la notación matemática lambda, el operador lambda es => que significa “tiende a” o “va hacia a”, la estructura de una expresión lambda en C# es:

(Argumentos de entrada) => (salida al procesarlos)
En caso de únicamente una variable la sintaxis es:
(x) => (x*x)
En caso de múltiples argumentos la sintaxis es:
(x,y,z) => ()

Aquí es importante saber que es el tipo delegate que dicta el tipo de los parámetros de entrada y de salida.

Ejemplo 1.3 Métodos como parámetros utilizando expresiones lambda.


Observamos que de los métodos anónimos a las expresiones Lambda, nos queda una sintaxis más legible y compacta.


ApplyF(x, (fahrenheit) => ((fahrenheit - 32) * (5 / 9D))));
ApplyF(x, (fahrenheit) => (fahrenheit + 460)));

Al ejecutar todos los programas observamos que el resultado es el mismo en cada uno, lo que cambio fue únicamente la sintaxis.
Fig 1. El resultado del código usando delegados.


Conclusión

Para los nuevos retos en el desarrollo de software, es importante que los lenguajes de programación incorporen características de un paradigma de programación diferente a ellos para extender sus capacidades y así poder expresar algoritmos de una manera compacta y más concisa esto da como resultado un código más legible.

Download el código fuente del ejemplo 1.1 Samples.Delegates para Xamarin o VS

Download el código fuente del ejemplo 1.2 Samples.AnonymousMethods para Xamarin o VS

Download el código fuente del ejemplo 1.3 Samples.LambdaExpression para Xamarin o VS

domingo, 27 de noviembre de 2016

Utilizando DNS (Domain Name System) en .NET con C#.

Cualquier dispositivo conectado a una red TCP/IP siempre tiene asignado un identificador único de 32 bits que se le conoce como dirección IP (IP Address) o dirección de red. Esta dirección es jerárquica y consta de cuatro octetos (valores de 8 bits) que se dividen en dos partes: la primera parte identifica a la red y la segunda identifica los hosts (cualquier dispositivo conectado) en esa red. Las dos partes son requeridas para formar una dirección IP.

Fig 1. Una dirección IP V4 jerárquica.


Cada dirección IP identifica solamente a un único dispositivo en una red TCP/IP como Internet, aunque es una ventaja para las máquinas esto representa un problema para los humanos, ya que sería muy difícil para una persona recordar cada una de los computadoras que utiliza por su dirección IP.

Debido a que la gente asocia mucho mejor nombres para las cosas, los diseñadores de Internet inventaron el DNS (Domain Name System) que es una manera de asociar nombres que la gente puede recordar con la dirección IP de una máquina.

DNS (Domain Name System)

Antes del DNS los dispositivos dependían de un único archivo de texto (/etc/hosts) como una tabla de hosts, para asociar los nombres de los sistemas en direcciones IP, a mediano plazo esta solución era ineficiente para mapear los nombres de hosts en direcciones, además de que no existía ninguna técnica para distribuir automáticamente la información acerca de los dispositivos recientemente agregados.

Por el contrario, DNS en lugar de depender de una sola gran tabla, es un sistema jerárquico de bases de datos distribuidas que no depende de una base de datos centralizada para resolver los nombres de hosts obteniendo las direcciones IP ya que DNS distribuye su información a través de miles de servidores de nombres. En la parte superior de la jerarquía DNS, un grupo de servidores raíz sirven a un dominio raíz.

Usando Domain Name System con las clases de .NET

.NET proporciona varias clases para apoyar la interacción con servidores DNS, las más comunes son:

IPAddress: representa tal cual una dirección IP, la dirección misma esta disponible como la propiedad Address, y se convierte en su formato decimal con el método .ToString() también realiza la operación inversa con el método Parse() el cual recibe el formato decimal como argumento. Además de estos métodos esta clase también tiene constantes estáticas que regresan las direcciones especiales Loopback y Broadcast.

IPHostEntry: encapsula toda la información referente a un dispositivo, lo más utilizado de esta clase es:

  1. HostName: regresa una cadena con el nombre del dispositivo.
  2. AddressList: regresa un arreglo de objetos IPAddress con las direcciones IP asignadas al dispositivo.
  3. Aliases: contiene un arreglo de alias asociados a un dispositivo.

Dns: hace posible la comunicación con el servidor DNS predeterminado para la traducción de un nombre a su dirección IP o direcciones IP asociadas. Esta clase difiere de las clases IPAddress y IPHostEntry porque esta clase tiene la habilidad de comunicarse con servidores DNS para obtener información. Lo más utilizado de esta clase es:

  1. GetHostName: Obtiene el nombre de host del dispositivo local.
  2. GetHostEntry: Resuelve una dirección IP o un nombre de host y regresa un objeto IPHostEntry con un arreglo de direcciones IP (objetos IPAddress).
  3. GetHostAddresses: Regresa un arreglo de direcciones IP (objetos IPAddress)

Como ejemplo de la utilización de estas clases escribí el siguiente programa que emula la funcionalidad de la utilidad nslookup, en esta caso utilizando las clases de .NET en una interfaz gráfica en GTK#.

Fig 2. El programa obteniendo una dirección tipo A del host www.mit.edu.


Fig 3 El programa obteniendo una dirección tipo C del host www.rt.com.


Fig 4 Obteniendo una lista de direcciones IP clase C del host www.yahoo.com.


En el código de la aplicación primero se distingue entre una dirección IP y el nombre de un host, según esta distinción se obtiene una clase IPHostEntry o bien un arreglo de clase IPAddress, al invocar el método GetHostEntry en el primer caso o el método GetHostAddress en el segundo.


En el primer caso se obtiene el arreglo de IPAddress de la propiedad AddressList en el segundo solamente se recorre y se imprime dicho arreglo.

Nota: El :NET Framework desde la version 1.1 soporta IPV6 a través de las clases que se encuentran en el ensamblado System.Net.

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

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

Aquí código fuente completo de la clase MainWindowServer (Server)

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Gtk;
using System.Threading;


namespace GtkSamples.Networking.UDPTimeServer
{
 public class MainWindow : Gtk.Window
 {
 VBox mainLayout = new VBox();
 Button btnStart = new Button("Start");
 HBox controlBox = new HBox();
 SpinButton sbtnPort = new SpinButton(7D, 10000D, 1D);
 SpinButton sbtnMinsToRun = new SpinButton(1D,11D,1D);
 TextView txtLog = new TextView();
 int serverPort = 1;

 public MainWindow() : base(WindowType.Toplevel)
 {
  this.Title = "UDP Time Server";
  this.SetDefaultSize(366,111);
  this.DeleteEvent += new DeleteEventHandler(OnWindowDelete);
  this.btnStart.Clicked += new EventHandler(Start);
  mainLayout.BorderWidth = 8;
  controlBox.BorderWidth = 8;
  controlBox.Spacing = 6;
            controlBox.PackStart(new Label("Port 1-10000"),false,true,0);
  sbtnMinsToRun.ClimbRate = 1D;
  sbtnMinsToRun.Numeric = true;
            controlBox.PackStart(sbtnPort,true,true,0);
  controlBox.PackStart(new Label("Minutes to run (max 11)"),false,true,0);
  sbtnMinsToRun.ClimbRate = 1D;
  sbtnMinsToRun.Numeric = true;
  controlBox.PackStart(sbtnMinsToRun, true, true, 0);
            controlBox.PackStart(btnStart,false,false,0);
  mainLayout.PackStart(controlBox,false,true,0);
            mainLayout.PackStart(txtLog,true,true,0);
  this.Add(mainLayout);
            this.ShowAll();
  }

  protected void OnWindowDelete(object o,DeleteEventArgs args)
  {
  System.Environment.Exit(Environment.ExitCode);
  }

  protected void Start(object o,EventArgs args)
  {
  try
  {
  Thread worker = new Thread(Run);
  worker.IsBackground = true;
  worker.Start(); 
  }
  catch (System.Exception ex)
  {
  txtLog.Buffer.Text += ex.Message;
  }
  }

  void Run()
  {
  controlBox.Hide();
  serverPort = Convert.ToInt32(sbtnPort.Text);
  uint mins = Convert.ToUInt32(sbtnMinsToRun.Text);
  DateTime timeToStart = DateTime.Now;
  DateTime timeToStop = DateTime.Now.AddMinutes(mins);
  //Step 1 create the UdpClient
  UdpClient udpClient = new UdpClient();
  StringBuilder buf = new StringBuilder();
  buf.AppendFormat("UDP Server running at port {0}\n", serverPort);
  buf.AppendFormat("Server starting at {0}\n", timeToStart.ToString());
  buf.AppendFormat("Server will stop at {0}\n", timeToStop.ToString());
  txtLog.Buffer.Text = buf.ToString();
  do
  {
  StringBuilder response = new StringBuilder();
  response.AppendLine("Sending the current datetime to client");
  response.AppendLine("The current date is ");
  response.AppendLine(DateTime.Now.ToLongDateString());
  response.AppendLine("the current time is ");
  response.AppendLine(DateTime.Now.ToLongTimeString());
  //step 2 create the remote endpoint to send the data.
  IPEndPoint IPremoteEP = new IPEndPoint(IPAddress.Loopback,serverPort);
  //step 3 convert the string data to a byte array and sends it
  byte[] data = Encoding.UTF8.GetBytes(response.ToString());
  udpClient.Send(data,data.Length,IPremoteEP);
  } while (DateTime.Now < timeToStop);
  udpClient.Close();
  txtLog.Buffer.Text += "Server stopping at " + DateTime.Now.ToString();
  }
 }
}

El código fuente de la clase Program (Server)


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

El código fuente completo de la clase MainWindow (cliente)

using System;
using Gtk;
using System.Net;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace GtkSamples.Networking.UDPClient
{
    public class MainWindow : Gtk.Window
    {
    VBox mainLayout = new VBox();
 HBox buttonBox = new HBox();
    Table tableLayout = new Table(4,2,false);
 SpinButton spbtnPort = new SpinButton(7D, 10000D, 1D);
 Entry txtHostName = new Entry();
 TextView txtLog = new TextView();
 Button btnRequest = new Button("Request");
 Button btnClose = new Button("Close");
 Label lblMsg = new Label("Output ");
 int portToReceive = 0;
  ScrolledWindow scrollLog = new ScrolledWindow ();

  public MainWindow():base(WindowType.Toplevel)
        {
            this.Title = "UDP GTK# Chat Example";
   this.WidthRequest = 516;
            this.DeleteEvent += new DeleteEventHandler(OnWindowDelete);
   this.btnClose.Clicked += new EventHandler(Close);
   this.btnRequest.Clicked += new EventHandler(Request);
            mainLayout.BorderWidth = 8;
   tableLayout.RowSpacing = 6;
   tableLayout.ColumnSpacing = 6;
   tableLayout.Attach(new Label("Port to connect "), 0, 1, 0, 1);
   spbtnPort.Numeric = true;
   tableLayout.Attach(spbtnPort, 1, 2, 0, 1);
   tableLayout.Attach(new Label("IP/Hostname"), 0, 1, 1, 2);
   tableLayout.Attach(txtHostName, 1,2, 1, 2);
   tableLayout.Attach(new Label("Response"), 0, 1, 2, 3);
   txtLog.Editable = false;
   scrollLog.Add (txtLog);
   tableLayout.Attach(scrollLog, 1, 2, 2, 3);
   buttonBox.Spacing = 6;
   buttonBox.PackStart(btnRequest, true, true, 0);
   buttonBox.PackStart(btnClose, true, true, 0);

            mainLayout.PackStart(tableLayout,false,true,0);
   mainLayout.PackStart(buttonBox, false, true, 0);
   mainLayout.Add(lblMsg);
            this.Add(mainLayout);
            this.ShowAll();
        }

  protected void OnWindowDelete(object o, DeleteEventArgs args)
  {
   System.Environment.Exit(Environment.ExitCode);
  }

  protected void Close(object o, EventArgs args)
  {
   System.Environment.Exit(Environment.ExitCode);
  }

  protected void Request(object o, EventArgs args)
  {
    if (string.IsNullOrEmpty(txtHostName.Text))
   {
    lblMsg.Text += "Hostname null";
   }
   else
   {
    portToReceive = Convert.ToInt32(spbtnPort.Text);
    //step 1 Create a UdpClient and specifies a port number to bind
    UdpClient client = new UdpClient(portToReceive);
    string text = null;
    try
    {
     //step 2 Create the IPEndPoint to receive the byte array
     IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
     //step 3 Get the byte array
     byte[] data = client.Receive(ref anyIP);
     //step 4 Convert the byte array to string
     text = Encoding.UTF8.GetString(data);
     txtLog.Buffer.Text = text;
    }
    catch (System.Exception ex)
    {
     lblMsg.Text += ex.Message;
    }
    finally
    {
     //step 5 Close the Socket
     client.Close();
    }
   }
  }

    }
}

El código fuente de la clase Program (cliente)

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


Download el proyecto GTK# Udp Server para Xamarin o Visual Studio

Download el proyecto GTK# Udp Client para Xamarin o Visual Studio

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

El código fuente completo de la clase MainWindowServer.cs.
using System;
using Gtk;
using System.IO;
using System.Threading;
using System.Net;

namespace Samples.GtkNetworking
{
    public class MainWindowServer : Gtk.Window
    {
        VBox mainLayout = new VBox();
        HBox controlLayout = new HBox(false,2);
        Entry txtMsg = new Entry();
        Button btnSend = new Button(Stock.Ok);
        TextView txtChat = new TextView();
        TextView txtLog = new TextView();
        Label msgLabel = new Label("Message: ");
        System.Net.Sockets.Socket connection = null;
        BinaryWriter writer = null;
        System.Net.Sockets.TcpListener listener = null;
        Thread readThread = null;

        public MainWindowServer() : base(WindowType.Toplevel)
        {
            this.Title = "GTK# Network Server";
            this.SetDefaultSize(343, 288);
   this.DeleteEvent += new DeleteEventHandler(OnWindowDelete);
   this.btnSend.Clicked += new EventHandler(SendMessage);
   mainLayout.BorderWidth = 8;
   controlLayout.BorderWidth = 8;
            controlLayout.PackStart(msgLabel,false,true,0);
            controlLayout.PackStart(txtMsg,true,true,0);
            controlLayout.PackStart(btnSend,false,false,0);
            mainLayout.PackStart(controlLayout,false,true,0);
            mainLayout.PackStart(txtChat,true,true,0);
            mainLayout.PackStart(new Label("Log "),false,true,0);
            mainLayout.PackStart(txtLog,true,true,0);
            this.Add(mainLayout);
            this.ShowAll();
            readThread = new Thread(RunServer);
            readThread.Start();
        }

        protected void OnWindowDelete(object o, DeleteEventArgs args)
  {
            //Terminate all
            System.Environment.Exit(System.Environment.ExitCode);
  }

        protected void SendMessage(object o,EventArgs args)
        {
            try
            {
                writer.Write(ChatMessage(txtMsg.Text));
                txtChat.Buffer.Text += ChatMessage(txtMsg.Text);
                txtMsg.Text = string.Empty;
            }
            catch (System.Net.Sockets.SocketException error)
            {
                LogMessage("Error: " + error.Message);
            }
        }

        string ChatMessage(string msg)
        {
            return string.Format("Server ({0}): {1} {2}",
            DateTime.Now.ToShortTimeString(),
            msg,
            Environment.NewLine);
        }

        void LogMessage(string msg)
        {
            txtLog.Buffer.Text += string.Format("{0} : {1}{2}",
            DateTime.Now.ToShortTimeString(),
            msg,
            Environment.NewLine);
        }

        void RunServer()
        {
            int counter = 1;
            try
            {
                //step 1: create endpoint
                IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any,6000);
                //Step 2: create TcpListener
                listener = new System.Net.Sockets.TcpListener(ipEndPoint);
                //Step 3: waits for connection request
                listener.Start();
                //Step 4: establish connection upon client request
                while(true)
                {
                    LogMessage("Waiting for connection...");
                    //step 5: accept an incoming connection
                    connection = listener.AcceptSocket();
                    //step 6: create the network stream object associated with socket
                    //NOTE: Use the namespace to avoid conflicts with GTK.Socket
                    System.Net.Sockets.NetworkStream socketStream = new System.Net.Sockets.NetworkStream(connection);
                    //step7: create the objects for transferring data across stream
                    using(writer = new BinaryWriter(socketStream))
                    {
                        using(BinaryReader reader = new BinaryReader(socketStream))
                        {
                            LogMessage(TcpFlags.SYN + " : " + counter);
                            //inform client that connection was ACK
                            writer.Write(ChatMessage(TcpFlags.ACK));
                            string theReply = "";
                            //read string data sent from client until receive the FIN signal
                            do
                            {
                                try
                                {
                                //read the string sent to the server
                                theReply = reader.ReadString();
                                //display the message except the FIN 
                                if(!theReply.Equals(TcpFlags.FIN))                                
                                    txtChat.Buffer.Text += theReply;
                                    else
                                    {
                                        LogMessage("Received " + TcpFlags.FIN);
                                        LogMessage("Send " + TcpFlags.FIN + " to Client");
                                        writer.Write(TcpFlags.FIN);
                                    }     
                                }
                                catch (System.Exception)
                                {
                                    break;
                                }
                            } while (theReply != TcpFlags.FIN 
                    && connection.Connected);
                    //Close connection
                    LogMessage("Close connection");
                        }
                    }
                    socketStream.Close();
                    connection.Close();
                    ++counter;
                }
            }
            catch (System.Exception ex)
            {
                LogMessage("Ex " + ex.ToString());
            }
        }

    }
}
El código fuente de la clase Program (Server).

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

El código fuente de la clase MainWindow (cliente)
using System;
using Gtk;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Threading;

namespace Samples.GtkNetworking
{
 public class MainWindow : Gtk.Window
 {
  VBox mainLayout = new VBox();
  HBox controlLayout = new HBox(false, 2);
  HBox connectedLayout = new HBox(false,2);
  Entry txtMsg = new Entry();
  Button btnSend = new Button(Stock.Ok);
  Button btnDisconnect = new Button(Stock.Disconnect);
  TextView txtChat = new TextView();
  TextView txtLog = new TextView();
  Label msgLabel = new Label("Message: ");
  TcpClient client = new TcpClient();
  NetworkStream output = null;
  BinaryWriter writer = null;
  BinaryReader reader = null;
  string message = "";
  Thread readThread = null;


  public MainWindow() : base(WindowType.Toplevel)
  {
   this.Title = "GTK# Network client";
   this.SetDefaultSize(343, 288);
   this.DeleteEvent += new DeleteEventHandler(OnWindowDelete);
   this.btnSend.Clicked += new EventHandler(SendMessage);
   this.btnDisconnect.Clicked += new EventHandler(SendDisconnect);
   mainLayout.BorderWidth = 8;
   connectedLayout.BorderWidth = 8;
   connectedLayout.PackStart(btnDisconnect,false,false,0);

   controlLayout.BorderWidth = 8;
   controlLayout.PackStart(msgLabel,false,true,0);
   controlLayout.PackStart(txtMsg,true,true,0);
   controlLayout.PackStart(btnSend,false,false,0);
   mainLayout.PackStart(connectedLayout,false,true,0);
   mainLayout.PackStart(txtChat,true,true,0);
   mainLayout.PackStart(controlLayout,false,true,0);
   mainLayout.PackStart(new Label("Log"),false,true,0);
   mainLayout.PackStart(txtLog,true,true,0);
   this.Add(mainLayout);
   this.ShowAll();
   readThread = new Thread(new ThreadStart(RunClient));
   readThread.Start();
  }

  protected void OnWindowDelete(object o, DeleteEventArgs args)
  {
   System.Environment.Exit(System.Environment.ExitCode);
  }

  protected void SendDisconnect(object o,EventArgs args)
  {
   if(client.Connected)
   {
    try
    {
    //Send disconnected signal
    writer.Write(TcpFlags.FIN);
    }
    catch(SocketException error)
    {
    LogMessage("Error " + error.Message);
    }
   }
   else
    LogMessage("You are not connected");
  }

  protected void SendMessage(object o,EventArgs args)
  {
   try
   {
    writer.Write(ChatMessage(txtMsg.Text));
       txtChat.Buffer.Text += ChatMessage(txtMsg.Text);
    txtMsg.Text = string.Empty;
   }
   catch (SocketException error)
   {
    LogMessage("Error " + error.Message);
   }
  }

  String ChatMessage(string msg)
  {
   return string.Format("Client ({0}): {1} {2}",
   DateTime.Now.ToLongTimeString(),
   msg,
   Environment.NewLine);
  }

  void LogMessage(string msg)
        {
            txtLog.Buffer.Text += string.Format("{0} : {1}{2}",
            DateTime.Now.ToShortTimeString(),
            msg,
            Environment.NewLine);
        }

  protected void RunClient()
  {
   try
   {
    //step 1 create a local endpoint
    IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Loopback,6000);
    //step 2 create a socket client and connect
    client.Connect(localEndPoint);
    LogMessage(TcpFlags.SYN);
    //step 3 get the network stream associated with tcpclient
    output = client.GetStream();
    //step 4 create the objects for writing and reading across the stream
    using(writer = new BinaryWriter(output))
    {
    using(reader = new BinaryReader(output))
    {
    //Receive message until receive the FIN signal
    do
    {
     try
     {
      message = reader.ReadString();
      if(!message.Equals(TcpFlags.FIN))
       txtChat.Buffer.Text += message;
      else
       LogMessage("Received " + TcpFlags.FIN + " from server");
     }
     catch (System.Exception error)
     {
      LogMessage("Error: " + error.Message);
     }
    } while (message != TcpFlags.FIN);
    LogMessage("Closing connection...");
    }
    }
    output.Close();
    client.Close(); 
   }
   catch (System.Exception ex)
   {
    LogMessage(" Ex " + ex.Message);
   }
  }
 }
}
El código fuente de la clase Program (cliente)

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.

El código fuente de la clase TcpFlags

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.


Descarga el código fuente de la clase TcpFlags.cs

Descarga la solución del servidor TCP GTK#.

Descarga la solución del cliente TCP GTK#.