domingo, 26 de julio de 2015

Receta C# No. 5-26: Escritura y Lectura Entre Procesos

Índice

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
3.1 Named pipes (tubierías nombradas)
3.2 Clase NamedPipeServerStream
3.3 Clase NamedPipeClientStream
4. Práctica: Código C#
5. Conclusiones
6. Glosario
7. Literatura & Enlaces

0. Introducción

Alcanzamos la última receta de la serie de recetas C# dedicadas a Archivos, Directorios, y Entrada y Salida. En esta ocasión vamos a construir una aplicación basada en consola para permitir a múltiples procesos comunicarse entre sí y ejecutar operaciones de lectura y escritura en conjunto. Para ello hemos de explorar y conocer las clases NamedPipeServerStream y NamedPipeClientStream para crear un servidor de escucha de conexiones y clientes de conexión para interacción mutua.

1. Problema

Requerimos intercomunicar dos procesos para el envío y recepción de datos.

2. Solución

El namespace System.IO.Pipes cuenta con dos clases diseñadas para permitir que dos procesos se puedan intercomunicar e intercambiar datos. Estas clases siguen el mecanismo de intercomunicación named pipes (o tuberías nombradas).

3. Discusción de la Solución

3.1 Named pipes (tuberías nombradas)

De acuerdo con [2] un named pipe (o tubería nombrada) es uno de los varios mecanismos para la intercomunicación de procesos. A diferencia de los unamed pipes (los cuales se ejecutan de forma anónima y durante el ciclo de vida del proceso), los named pipes, además de contar con un identificador específico, pueden persistir más allá de la ejecución de un proceso y el sistema puede programarse para eliminarlos cuando sea requerido.
Named pipes
Figura 1. Diagrama conceptual de Named pipes.
Y de acuerdo con [3], un nombre alternativo para los named pipes es FIFO [5] debido a que los datos que son escritos primero son los primeros en ser leídos.

3.2 Clase NamedPipeServerStream

La clase NamedPipeServerStream [6] (namespace System.IO.Pipes) pertmite la comunicación entre procesos en los modos: 
  • unidireccional, y 
  • bidireccional
Esta clase actúa como el servidor de escucha de conexiones desde uno o más clientes. Y como explican en [6]
"Named pipes can be used for interprocess communication locally or over a network."
Lo que quiere decir es que podemos intercomunicar procesos inter-máquina (local) e intermáquina (red).


Nota importante de límites de número máximo de named pipes en algunos sistemas operacionales
"For Windows XP Professional and Windows 2000 Server, a maximum of 10 pipes can simultaneously connect over the network."
Ejemplo de uso

Archivo C# UsoNamedPipeServerStream.cs [Enlace alternativo][Enlace alternativo]: 

Sobre la línea 15 de la instrucción using creamos una instancia de NamedPipeServerStream para representar el servidor de escucha de conexiones desde clientes. Con la llamada a WiatForConnection (línea 21) esperamos a que un cliente se conecte para empezar el proceso de envío de datos.

En el bloque try (líneas 25-34) efectuamos las siguientes tareas: 
  • Línea 28: Creación de un objeto StreamWriter para escribir datos desde la consola y enviarlos a los clientes conectados al servidor.
  • Línea 32: Leemos la entrada del usuario y la envíamos a los clientes.
En caso de que se produzca un problema en la conexión con el cliente, se lanzará una excepción IOException reportando la causa del error (línea 38).

Compilación: 

  1. csc /target:exe UsoNamedPipeServerStream.cs

Ejecución assembly

  1. .\UsoNamedPipeServerStream.exe

3.2 Clase NamedPipeClientStream

Con la clase NamedPipeClientStream [7] (namespace System.IO.Pipes) creamos clientes para conexiones a named pipes. Podemos crear n instancias de objetos NamedPipeClientStream para establecer conexión con el servidor manejado por una instancia de NamedPipeServerStream.

Las conexiones se llevan a cabo a través del método Connect [8]. Este método también cuenta con una versión sobrecargada asincrónica [9] para soporte de lectura y escritura asincrónicas entre procesos.

Ejemplo de uso

En la línea 15 creamos un objeto NamedPipeClientStream que se conectará con un named pipe con el identificador servidor. Con el método Connect esperamos a que haya una conexión disponible. En el bloque using creamos un objeto StreamReader para recibir los mensajes desde el named pipe servidor. Mostramos los mensajes enviados desde el servidor en la línea 34.

Compilación: 


  1. csc /target:exe UsoNamedPipeClientStream.cs

Ejecución Assembly


  1. .\UsoNamedPipeClientStream.exe

> Prueba de ejecución: 
Ejecución assemblies UsoNamedPipeServerStream.exe y UsoNamedPipeClientStream.exe
Animación 1. Ejecución assemblies UsoNamedPipeServerStream.exe y UsoNamedPipeClientStream.exe.

4. Práctica: Código C#

Afiancemos nuestro conocimiento diseñando una nueva aplicación consola para crear un servidor y un cliente y facilitar entre ellos la intercomunicación para el envío y recepción de mensajes.

En el punto de entrada de la aplicación, Main (líneas 11-26), determinamos en qué modo debe iniciarse la aplicación consola: cliente o servidor.


En el método IniciarServidorPipe (líneas 28-60) ocurren las siguientes operaciones: 
  • Línea 31: Instanciación de un objeto NamedPipeServerStream para la creación de un named pipe identificado como servidor.
  • Línea 35: Invocación de WaitForConnection para esperar conexiones desde un cliente.
  • Líneas 45-50: Ciclo for para enviar hasta 10 datos  desde servidor hacia el cliente.
  • Líneas 53-56: Ciclo for para recibir hasta 10 datos desde el cliente.
  • Línea 59: Cierre del named pipe servidor.
Respecto al método IniciarClientePipe
  • Línea 65: Instanciación de un objeto NamedPipeClientStream para conexión con un named pipe identificado como servidor.
  • Línea 68: Intento de conexión con el servidor con el método Connect.
  • Líneas 76-76Ciclo for para recibir hasta 10 datos desde el servidor.
  • Líneas 82-87: Ciclo for para enviar hasta 10 datos hacia el servidor.
  • Línea 90: Cierre del cliente con Close.
Compilcación: 

  1. csc /target:exe ServidorClienteNamedPipe.cs

Ejecución assembly

  1. .\ServidorClienteNamedPipe.exe

> Prueba de ejecución: 
Ejecución assembly ServidorClienteNamedPipe.exe
Animación 2. Ejecución assembly ServidorClienteNamedPipe.exe.

5. Conclusiones

Hemos aprendido que la intercomunicación entre procesos es muy sencilla. Esto gracias al uso de las clases NamedPipeServerStream y NamedPipeClienteStream, y al comprensión del mecanismo de intercomunicación de procesos pipe named (o tuberías nombradas).

Finalizamos esta serie de recetas C# de Archivos, Directorio, y Entrada y Salida. A partir de la siguiente serie nos centraremos en Procesamiento XML.

6. Glosario

  • Conexión
  • Dato
  • Intercomunicación
  • Name pipe
  • Proceso
  • Unamed pipe

7. Literatura & Enlaces

[1]: Visual C# 2010 Recipes by Allen Jones and Adam Freeman. Copyright 2010 Allen Jones and Adam Freeman, 978-1-4302-2525-6.
[2]: Named pipe - Wikipedia, the free encyclopedia - https://en.wikipedia.org/wiki/Named_pipe
[3]: Inter-process communication - Wikipedia, the free encyclopedia - https://en.wikipedia.org/wiki/Inter-process_communication
[4]: What is named pipe? - Definition from WhatIs.com - http://whatis.techtarget.com/definition/named-pipe
[5]: FIFO (computing and electronics) - Wikipedia, the free encyclopedia - https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)
[6]: NamedPipeServerStream Class (System.IO.Pipes) - https://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeserverstream(v=vs.110).aspx
[7]: NamedPipeClientStream Class (System.IO.Pipes) - https://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeclientstream(v=vs.110).aspx
[8]: NamedPipeClientStream.Connect Method (System.IO.Pipes) - https://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeclientstream.connect(v=vs.110).aspx
[9]: NamedPipeClientStream.ConnectAsync Method (System.IO.Pipes) - https://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeclientstream.connectasync(v=vs.110).aspx


V

sábado, 25 de julio de 2015

APO1 - Nivel 3 - El Ahorcado

Índice

0. Introducción
1. Problema
1.1 Enunciado
1.2 Interfaz gráfica de usuario (Java)
2. Requerimientos
2.1 R1 - Iniciar un juego
2.2 R2 - Jugar una letra
3. Modelo del Mundo del Problema
4. Modelo de la Interfaz Gráfica de Usuario
5. Modelo de Pruebas Unitarias
6. Repositorio: Exploración de Código Fuente C#
7. Recursos
7.1 Java
7.2 C#
7.2.1 Solución y proyectos
7.2.2 Documentación código fuente
7.2.3 Aplicación en ejecución
7.2.3.1 Captura de pantalla
7.2.3.2 Demostración
8. Cambios
9. Conclusiones
10. Glosario
11. Literatura & Enlaces

0. Introducción

En esta ocasión desarrollaremos una aplicación para jugar al ahorcado. Seguiremos el enfoque de análisis, diseño, e implementación orientado objetos como parte de la metodología desarrollada en el proyecto Cupi2 del Departamento de Ingeniería de Sistemas y Computación de la Universidad de los Andes (Colombia). Presentaremos el resultado de análisis, modelaremos, y desarrollaremos la solución usando el lenguaje de programación C# de .NET. Todo esto con el propósito de ofrecer a la comunidad de programadores que desean fortalecer sus conocimientos de la programación orientada a objetos, y/o desean profundizar en C# y las tecnologías .NET.


[Nota importante: Los materiales aquí usados del proyecto Cupi2 y que constituyen la base para el proceso de traducción desde la plataforma Java a la plataforma .NET, se mantienen como son. Su propósito y uso es únicamente académico y divulgativo.]

1. Problema

1.1 Enunciado

Descripción original del problema [4]

El juego del Ahorcado consiste en tratar de adivinar una palabra escondida usando la menor cantidad posible de letras. Si el jugador tiene más de 6 intentos fallidos, pierde el juego. Al iniciar, el jugador solamente conoce el número de letras de la palabra. A medida que avanza el juego, las letras que escoge el jugador se van poniendo en los espacios vacíos de la palabra, de forma que el jugador pueda intentar deducir cuál es.

Durante un juego de Ahorcado, se debe validar si la letra elegida está o no en la palabra, sin distinguir si la letra es mayúscula o minúscula. Si la letra elegida está en la palabra se deben llenar los campos en los que ocurre la letra. También es necesario tener en cuenta que la letra que se esté jugando, no haya sido jugada anteriormente, de modo que si es letra repetida, no se pierda un intento para el jugador. Adicionalmente se debe validar que el jugador sólo tenga el número de oportunidades determinadas por la secuencia de ahorcar el muñeco en juego y que con cada jugada se valide si se ha ganado o no el juego. Finalmente, es importante tener en cuenta que sólo se puede jugar una letra si el juego está iniciado, de forma que una persona que haya perdido, no puede hacer más jugadas hasta que inicie un nuevo juego.

Se quiere construir una aplicación que permita jugar el juego del Ahorcado, donde las palabras por adivinar pertecen al diccionario de palabras de Cupi2 Nivel 3 y son las siguientes:
  • algoritmo, 
  • contenedora, 
  • avance, 
  • ciclo, 
  • instrucciones, 
  • arreglo, 
  • vector, 
  • inicio, 
  • cuerpo, 
  • recorrido, y 
  • patrón
No se tienen en cuenta los acentos de las palabras. El diccionario de palabras sólo maneja minúsculas y no se tienen en cuenta las tildes.

1.2 Interfaz gráfica de usuario (versión Java)

En la Figura 1 se muestra la interfaz gráfica de usuario de la aplicación Ahorcado construida en el lenguaje de programación Java.
GUI en Java
Figura 1. GUI en Java.

2. Requerimientos

2.1 R1 - Iniciar un juego

Código: R1

Nombre: Iniciar un juego

Descripción: La aplicación inicia un nuevo juego escogiendo una palabra del diccionario. El número de intentos posibles es 6.

Entradas
  • Ninguna
Resultados:
  • Hay una palabra escogida, se muestran los espacios vacíos, todas las letras del alfabeto están disponibles y el número de intentos para jugar.

2.2 R2 - Jugar una letra

Código: R2

Nombre: Jugar una letra

Descripción: El jugador propone una letra para ver si está en la palabra o no.

Entradas:
  • La letra escogida por el jugador
Resultados:
  • Si la letra pertenece a la palabra, entonces se le muestra al usuario las ocurrencias de esa letra en la palabra, en las posiciones correspondientes.
  • Si la letra no está en la palabra, entonces el usuario pierde el intento y se valida si ya está ahorcado o si puede seguir jugando.
  • Si la letra ya había sido jugada se informa al usuario que la letra ya se había jugado.
  • Si la palabra está completa, entonces el juego termina.

3. Modelo del Mundo del Problema

En la Figura 2 se describe el diagrama UML con las entidades que integran el modelo del mundo del problema (dominio). (Original en [6].)
Ahorcado - Modelo del mundo del problema
Figura 2. Ahorcado - Modelo del mundo del problema.

4. Modelo de la Interfaz Gráfica de Usuario

En la Figura 3 se muestra el diagrama UML que describe los controles visuales que integran la interfaz gráfica de usuario. (Original en [7].)
Ahorcado - Modelo de la interfaz gráfica de usuario
Figura 3. Ahorcado - Modelo de la interfaz gráfica de usuario.

5. Modelo de Pruebas Unitarias

En el diagrama UML presentado en la Figura 4 se modelan las clases de pruebas del modelo del mundo del problema. (Original en [8].)
Ahorcado - Modelo de pruebas unitarias
Figura 4. Ahorcado - Modelo de pruebas unitarias.

6. Repositorio: Exploración de Código Fuente C#

En el siguiente enlace de repositorio in GitHub se puede explorar todo el código; el cual comprende: 
  • Modelo del mundo del problema, 
  • Controles de la interfaz gráfica de usuario, 
  • Archivos de configuración, 
  • Recursos (imágenes, íconos, cadenas de texto, etc.), 
  • Clases de pruebas unitarias de la funcionalidad y estado de las entidades del mundo del problema.

7. Recursos

7.1 Java

Recursos del proyecto en su versión original: 

7.2.1 Solución y proyectos

Enlaces de descarga de archivos ZIP con la solución y los proyectos integrales (proyectos C# y pruebas unitarias) de la aplicación: 

7.2.2 Documentación código fuente

Archivo CHM con la documentación del código fuente C#
APO1 - Nivel 3 - El Ahorcado - Documentación
Figura 5. APO1 - Nivel 3 - El Ahorcado - Documentación.

7.2.3 Aplicación en ejecución

7.2.3.1 Capturas de pantalla
APO1 - Nivel 3 - El Ahorcado
Figura 6. APO1 - Nivel 3 - El Ahorcado.
7.2.3.2 Demostración

8. Cambios

Cambios sobresalientes en la solución: 
  • Adición de las enumeraciones EstadoJuego y JuegoAhorcadoConstantes para representar el estado del juego y constantes del juego.
  • Varios métodos de las clases Letra, Palabra, y JuegoAhorcado como propiedades.

9. Conclusiones

A lo largo del proceso de traducción hemos afianzado nuestro conocimiento no sólo en lenguaje de programación C#, sino además en comprender estructuras fijas para la contención de datos. La creación de controles personalizados nos ayuda a crear una interfaz gráfica modular y propensa a fáciles modificaciones.


En la próxima traducción pasaremos al nivel 4 de APO1: seguimiento de una especificación en términos de un contrato para la construcción de elementos de programa.

10. Glosario

  • Arreglo
  • Clase
  • Control
  • Estructura de dato
  • Índice
  • Método

11. Literatura & Enlaces

[1]: Universidad de los Andes - Colombia - http://www.uniandes.edu.co/
[2]: Cupi2 - http://cupi2.uniandes.edu.co/sitio/
[3]: APO1 - http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1
[4]: Enunciado - http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/ahorcado
[5]: Requerimientos Funcionales - http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/ahorcado/requerimientos-funcionales
[6]: Modelo Conceptual - http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/ahorcado/modelo-conceptual
[7]: Interfaz - http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/ahorcado/interfaz
[8]: Pruebas - http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/ahorcado/pruebas


V

Receta C# No. 5-25: Procesamiento de un Archivo de Registro (Log)

Índice

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
3.1 Método static File.ReadLines
3.2 Filtros con LINQ
4. Práctica: Código C#
5. Conclusiones
6. Glosario
7. Literatura & Enlaces

0. Introducción

En la receta C# anterior (5-24) aprendimos a escribir información de eventos (e.g., errores, excepciones) en un archivo de registros de eventos; la oportunidad de ahora consiste en procesar estos archivos con el uso del método static File.ReadLines y el uso de expresiones LINQ para obtener líneas específicas en el archivo.

1. Problema

Contamos con una serie de archivos de registros de eventos y queremos procesar su contenido.

2. Solución

En C# contamos con el método File.ReadLines [2] para leer el contenido de todas las líneas de un archivo de texto. Podemos procesar esas líneas de texto con consultas o filtros LINQ.

3. Discusión de la Solución

3.1 Método static File.ReadLines

El método sobrecargado static File.ReadLines [2] lee cada una de las líneas de un archivo. En la Figura 1 de muestran las versiones sobrecargadas de este método. 
Versiones sobrecargadas de File.ReadLines
Figura 1. Versiones sobrecargadas de File.ReadLines [2].

Vale mencionar, que a diferencia del método File.ReadAllLines [3], File.ReadLines permite trabajar de forma más eficiente con archivos grandes gracias a que la enumeración de la colección de cadenas se ejecuta primero antes de retornar la colección de objetos string [2]; esto no ocurre con File.ReadAllLines, dado que se debe esperar hasta que la colección de objetos string se retorna para hacer cualquier enumeración y/o aplicación de un filtro.


File.ReadLines usa el sistema de codificación UTF-8 para las cadenas de caracteres [2]. Por otra parte, podemos usar LINQ para filtrar las líneas contenidas en un archivo leído por File.ReadLines.

3.2 Filtros con LINQ

No nos extenderemos en esta sección de creación de filtros con el lenguaje de consulta integrado -LINQ-. Incluyo el siguiente enlace para conocer los detalles de la receta que explica el proceso de selección de elementos específicos de una colección -Receta No. 2-17 en C#: Selección de Elementos de un Arreglo o Colección-, además, del siguiente código de ejemplo extraído desde esa misma receta: 

En las líneas 9-28 declaramos la clase Fruta con un constructor, y dos propiedades (i.e., Nombre, y Color). La clase de prueba, FrutaLinq (líneas 30-73) tiene declarado el punto de entrada a la aplicación, Main
  • Línea 35: Creación de una instancia concreta de List con objetos Fruta.
  • Línea 49: Usamos la versión estándar de LINQ para seleccionar aquellas frutas con color distinto a rojo y que su nombre no empiece por el carácter 'p'.
  • Línea 61: Aquí utilizamos métodos de extensión (Métodos de Extensión en C#) de LINQ (i.e., Where, OrderBy, Select) con expresiones lambda para hacer la misma consulta anterior.
Compilación: 

  1. csc /target:exe FrutaLinq.cs

Ejecución assembly

  1. .\FrutaLinq.exe

> Prueba de ejecución: http://ideone.com/b2OgMj

> Prueba de ejecución: 
Ejecución assembly FrutaLinq.exe
Figura 2. Ejecución assembly FrutaLinq.exe.

4. Práctica: Código C#

En esta receta reutilizaremos el archivo log_completo.log ([enlace alternativo][enlace alternativo]) creado en la receta anterior para obtener aquellas entradas (líneas) que sólo incluyan al principio la palabra Error. Lo anterior usando LINQ.

Con la expresión 

IEnumerable<string> registros = File.ReadAllLines("log_completo.log");


(línea 18) leemos todas las líneas del archivo de registros de eventos. Notemos que aquí hemos definido a registros como un tipo IEnumerable para objetos string. Con el ciclo foreach (líneas 19-22) mostramos cada una de las líneas en la colección referenciada por registros.

Con 

IEnumerable<string> registrosError = File.ReadLines("log_completo.log").Where(e => e.StartsWith("Error"));

(línea 25) usamos LINQ y su método de extensión Where para crear la consulta que selecciona aquellos líneas del log que empiezan con los caracteres Error.


Compilación: 


  1. csc /target:exe SeleccionRegistros.cs


Ejecución assembly


  1. .\SeleccionRegistros.exe

> Prueba de ejecución: 
Ejecución assembly SeleccionRegistros.exe
Figura 3. Ejecución assembly SeleccionRegistros.exe.

5. Conclusiones

Hemos visto cómo es de sencillo seleccionar todos o algunos registros de un archivo de log utilizando LINQ (en especial para el segundo caso). Conocer LINQ ya se convierte en una necesidad explícita y urgente (seguramente en la proximidad ya empezaremos a redactar recetas LINQ).


La siguiente receta C# es la última de la serie de Archivos, Directorios, y Entrada y Salida: aprenderemos a enviar y recibir datos entre distintos procesos.

6. Glosario

  • Archivo
  • Consulta
  • Directorio
  • Filtro
  • LINQ
  • Proceso
  • Selección

7. Literatura & Enlaces

[1]: Visual C# 2010 Recipes by Allen Jones and Adam Freeman. Copyright 2010 Allen Jones and Adam Freeman, 978-1-4302-2525-6.
[2]: File.ReadLines Method (System.IO) - https://msdn.microsoft.com/en-us/library/system.io.file.readlines(v=vs.110).aspx
[3]: File.ReadAllLines Method (System.IO) - https://msdn.microsoft.com/en-us/library/system.io.file.readalllines(v=vs.110).aspx
[4]: Receta No. 2-17 en C#: Selección de Elementos de un Arreglo o Colección - http://ortizol.blogspot.com/2014/05/receta-no-2-17-en-c-seleccion-de.html
[5]: Métodos de Extensión en C# - http://ortizol.blogspot.com/2014/09/metodos-de-extension-en-csharp.html


V