Documente Academic
Documente Profesional
Documente Cultură
NET
Práctica 1. Introducción a .NET, aplicaciones básicas, namespaces,
paquetes y código intermedio.
El objetivo de esta práctica es programar una aplicación sencilla para familiarizarnos con el
entorno de programación de .NET. Durante el desarrollo de las prácticas se van realizar
prácticas para dispositivos limitados (PDA).
Conociendo el Entorno de Desarrollo
Comenzamos por crear un nuevo proyecto con Visual Studio 2005 o 2008:
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Todos los proyectos que hagamos, se incorporan a una solución, daremos el nombre
MiSolución o el que nos apetezca a la solución. Ten en cuenta que la solución es un conjunto
de proyectos y el nombre debería representar algo común a todos los proyectos incluidos en
él.
Al crear el proyecto, podemos ver en el explorador de soluciones (Si no aparece, menú
View[Ver]>Explorador de soluciones) que el proyecto está vacío completamente.
Como podemos ver, la solución no contiene ningún fichero de
código. Para introducir un nuevo fichero de código, que será el
programa principal, sobre el
proyecto MiAplicación,
haremos click con el botón
derecho y pulsaremos sobre
añadir‐añadir elemento nuevo.
Al hacer esto, aparecen varias
posibilidades, entre las que se
encuentra Archivo de código.
Hay otras plantillas, como la
de clase o interfaz, que
generan parte del código, la
estructura etc…
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Llamaremos a este
fichero programa.cs. En
él vamos a escribir el
código de la primera
aplicación para Windows
Mobile.
El fichero está vacío,
dado que no se ha
utilizado ninguna
plantilla de clase o de
interfaz.
El programa principal
Vamos a escribir el programa principal. Para ello se pueden utilizar las siguientes clausulas:
public static int Main(),
public static void Main(),
public static void Main(string[] args)
Vamos a probar el siguiente código, que calcula el factorial de un número entero de 64 bits.
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
generar o build. Si hacéis esto mismo con la solución, compilará todos los proyectos
contenidos en la solución.
En la ventana de resultados (si no se ve la pestaña, pulsad en el menú Ver‐Resultados) podréis
ver si la compilación ha sido correcta o no.
Si vamos a la carpeta donde hemos guardado el proyecto, en la ruta
MiSolucion\MiAplicacion\Bin\Debug podemos ver dos ficheros, MiAplicación.exe y
MiAplicación.pdb. Estos ficheros tienen información de depuración, pueden ser un poco más
lentos que los programas sin información de depuración.
Si cambiáramos la configuración a Release (sin debug) crearía un fichero más eficiente, pero
en el caso que nos ocupa, vamos a aprender, así que dejamos el sistema en modo debug.
Depurar el programa
A continuación veremos cómo se depura un programa con Visual Studio. Para depurar,
podemos poner puntos de interrupción pulsando en la parte gris del editor de texto, justo en
la línea en la que queremos insertar el
breakpoint o bien mediante el menú
debug. Si lo que queremos es ejecutar
paso a paso pulsamos F10 o para
meternos dentro de las funciones F11 (menú debug StepOver, StepInto).
Para probarlo usaremos un emulador. En Visual Studio hay un desplegable que permite ver los
emuladores instalados.
En ese menú desplegable seleccionaremos el emulador que queramos ejecutar (es posible que
al depurar nos pregunte de nuevo por el emulador a utilizar).
Pulsaremos ahora F10 y comenzará la ejecución. Al comenzar la depuración el entorno de
desarrollo cambia y nos muestra nuevas ventanas:
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Pila de llamadas Ventana de
Automático: Variables comandos…
Variables en locales:
uso en cada Variables en
momento el método
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Si pulsamos F10, ejecutará la primera instrucción MiAplicacion.factorial(2); y
continuará por la siguiente. Si en la siguiente en lugar de pulsar F10, pulsamos F11, se
introducirá en el método factorial, si posteriormente, en el método factorial volvéis a pulsar
F11 en la llamada recursiva, volverá a llevaros a factorial.
Comprobadlo, echadle un vistazo a la pila de llamadas y podréis ver el efecto de la
recursividad en la pila de llamadas:
Observad también como varía en valor de las variables mediante las ventanas de
Automático y Variables Locales. Estas ventanas permiten cambiar el valor de las
variables que se están utilizando.
Las tripas del programa, MSIL (MS Intermediate Language)
Para ver el código intermedio, generado por el compilador de C#, tendremos que usar una
herramienta llamada ildasm.exe que es un desensamblador de MSIL. Como es posible que sea
difícil de localizar dentro de la maraña de directorios de Windows y como probablemente no
esté en el PATH, una buena forma de usar las utilizades asociadas a Visual Studio es mediante
la consola Visual Studio Command Prompt. Cuando aparezca esta consola, ejecuta ildasm.exe
y aparecerá una utilidad gráfica.
Buscamos el fichero binario MiAplicación.exe y lo analizamos con el desensamblador de
lenguaje intermedio. Como se pude ver, no existe el método factorial, pero si nos fijamos en el
main, podremos comprobar que, como medida de eficiencia, el compilador lo ha incluido en el
interior de main.
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Más adelante veremos cómo varía el código intermedio con programas más complejos.
Acceso a ficheros para depurar
Ahora que sabemos cómo depurar un programa, ver el contenido del código intermedio etc,
vamos a continuar. Las plataformas Windows Mobile no disponen de consola (se puede
instalar usando algunas utilidades GNU) por lo que no es posible hacer depuración sobre
consola, pero si en fichero de texto, que además es muy útil.
Para hacer esto vamos a crear un ensamblado o librería de log. En primer lugar vamos a dar un
espacio de nombres a nuestra aplicación. Hasta el momento, no hemos necesitado que
nuestra aplicación tuviera un nombre único, que la diferenciara de las demás a efectos de
reutilizar código, pero ahora va a ser necesario; por esta razón, cambiamos el código de la
práctica por el siguiente:
using System;
namespace ComputacionRed.MiAplicacion
{
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Acto seguido miramos las propiedades de la aplicación y cambiamos el nombre del
ensamblado en la configuración:
Una vez hecho esto vamos a crear un nuevo proyecto. Sobre MiSolución, botón derecho
Añadir nuevo proyecto. Tipo Class Library. El nombre que le daremos será UtilidadLog.
Cambiamos el namespace de la aplicación tal y como aparece bajo estas líneas.
using System;
using System.Collections.Generic;
using System.Text;
namespace Utilidades.UtilidadLog
{
public class Log
{
}
}
Actualice la información en las propiedades, de forma que el nombre del ensamblado y el
espacio de nombres predeterminado sean Utilidades.UtilidadLog
Observa que el nombre de la clase es independiente del nombre del fichero, no ocurre
igual en java.
A continuación daremos funcionalidad a la clase de Log, de forma que podamos escribir
información a un fichero que permita depurar el programa. Para ello, es necesario declarar en
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
el código mediante using qué parte de la Base Class Library se utilizará. En concreto usaremos
System.IO.
Para escribir en un fichero usaremos la clase TextWriter, cuya documentación puede
encontrarse en http://msdn.microsoft.com/es‐es/library/system.io.textwriter(VS.80).aspx. La
función de log se utilizará en adelante para probar el correcto funcionamiento del programa y
debido a que las aplicaciones gráficas que veremos más adelante son multihilo (si pulsas un
botón y la ejecución tarda, puede que al pulsar otro botón se genere otro hilo de ejecución) y a
que el fichero sobre el que vamos a escribir es un recurso compartido: hay que usar un Mutex.
El paquete que tiene la implementación de TextWriter es System.IO y el que contiene la
implementación de Mutex es System.Threading. A continuación se muestra como utilizar
ambos en el programa de la clase Log. Esta clase muestra cómo usar un mutex y como escribir
en un fichero de texto.
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Utilidades.UtilidadLog
{
public class Log
{
/* TextWriter */
TextWriter tw = null;
Mutex fileMutex;
int indent = 0;
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
}
}
}
Los atributos de la clase Log son una referencia a la clase Textwriter, que permite
escribir al fichero; un semáforo Mutex, que controlará el acceso al recurso compartido
(fichero) ; y una variable de tipo entero que almacena la indentación a añadir a cada
línea.
El constructor de la clase crea una instancia de la clase Textwriter proporcionándole el
nombre del fichero a utilizar. Si compruebas la documentación del constructor de
StreamWriter podrás ver qué implica el segundo parámetro (true).
A continuación podemos ver una función cuanto menos extraña para aquellos sin
experiencia en C++. Es la función ~Log() que se conoce como destructor. En C++ esa
función se utiliza para liberar memoria una vez concluye la ejecución de la clase y el
objeto se destruye. E n los lenguajes como Java o cualquiera de los presentes en .NET,
no es necesario liberar memoria, eso lo hace el recolector de basura; en cambio, se
permite el uso de esta función para realizar una serie de tareas antes de destruir el
objeto (como en este caso, hacer flush y cerrar el fichero). Aunque no es necesario, en
ocasiones es útil.
El siguiente método, es Trace. Este método escribe una traza de log en el fichero.
Primero comprueba el semáforo y si es necesario espera un tiempo dado hasta que
deje de ser usado. En ese momento tabula el texto, escribe el mensaje, hace flush y
por último libera el semáforo para que otros métodos puedan usar el fichero.
El resto de los métodos no requieren explicación.
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Utilizando la clase Log desde otro programa
Para poder utilizar la clase Log desde MiAplicación, hacemos click con el botón derecho sobre
la carpeta References contenida en el proyecto aplicación y luego sobre Add Reference.
Vamos a la pestaña Proyectos y ahí encontraremos UtildadLog. La seleccionamos y pulsamos
aceptar.
A partir de ese momento podremos utilizar la
clase Log, contenida en el espacio de
nombres Utilidades.UtilidadLog.
A continuación probaremos la clase Log, pero
antes vamos a facilitar la tarea modificando
las propiedades del emulador. Para ello, en el
menú File del emulador, seleccionamos
configure… cuando aparece el cuadro de
diálogo en la caja de texto Shared Folder
navegamos hasta la ruta donde se encuentre
la aplicación compilada, es decir, carpetaDelProyecto\bin\debug.
A partir de ese momento si abrimos el explorador de ficheros en la PDA emulada y navegamos,
veremos que existe un directorio llamado Storage Card que simula una tarjeta SD introducida
en el slot. Si consultamos los ficheros contenidos en ella, veremos cómo aparecen los
contenidos en el directorio seleccionado.
Ahora vamos a modificar el programa para probar la clase de Log. Utilice el siguiente código:
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
using System;
using Utilidades.UtilidadLog;
namespace ComputacionRed.MiAplicacion
{
Como se puede apreciar, el fichero seleccionado para guardar los resultados del log se
encuentra en la carpeta de la aplicación dentro del PC (no de la PDA).
Lo siguiente que haremos, será probar la aplicación. Para ver el fichero con comodidad (puede
verse directamente en la PDA emulada, pero debido a que el tamaño de la pantalla no es muy
grande, es preferible hacerlo en el PC) lo abrimos con visual studio. A partir de este momento
no hace falta cerrarlo y volverlo a abrir para ver los cambios, si el fichero cambia, Visual Studio
lo notificará.
El resultado de la ejecución debe ser algo similar a esto:
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Observe que una llamada a una función dentro de otra función aumenta la
indentación.
Práctica 2. Introducción a .NET, programación gráfica.
El objetivo de esta práctica es programar una aplicación sencilla para familiarizarnos con el
entorno visual de programación de .NET con Formularios de Windows.
Conociendo el Entorno de Desarrollo
Comenzamos por crear un nuevo proyecto con Visual Studio 2005 o 2008:
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
solución que ya teníamos creada.
Una vez creado, aparecerá el interfaz de usuario. En el que podemos ver una imagen de lo que
será el programa. Además existen menús útiles para el desarrollo como son (Están
identificados en la imagen):
El explorador de soluciones
Vista de clases
Propiedades
Toolbox
Resultados
Lista de errores
Si no ves alguno de las pestañas marcadas con círculos sobre las imágenes, puedes usar el
menú View (ver) y pulsar sobre cada una de las que necesitas. Luego puedes arrastrarlas por la
pantalla para colocarlas donde te resulten más cómodas de usar.
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Aplicación Hello world
Se trata de la aplicación que todo el mundo ha hecho alguna vez para comenzar a aprender un
lenguaje. La aplicación tiene el efecto positivo de mostrar al usuario que algo funciona, a partir
de ahí… lo que imagines
Un vistazo al código
Antes de comenzar con la aplicación vamos a echar un vistazo al código generado por Visual
Studio y que nos permitirá programar la aplicación.
Vamos a ver el programa principal, la clase que permite la ejecución del formulario o ventana
sobre la que colocaremos controles como botones o cajas de texto. Para ver el código pulse
sobre program.cs como indica la figura:
Al hacer esto, pulsar sobre Ver Código o hacer doble click sobre
Program.cs, aparece el código en la pantalla principal.
El código que se verá será este (o muy parecido):
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace miApp
{
static class Program
{
/// <summary>
/// The main entry point for the
application.
/// </summary>
[MTAThread]
static void Main()
{
Application.Run(new Form1());
}
}
}
Este código es un programa principal como el que hemos visto en la anterior práctica, la única
diferencia es que ahora existe un atributo llamado MTAThread que se usa en las aplicaciones
de Formularios para conocer el lugar en el que comienza el programa.
Consulta el código de la clase Form1.cs, comprobarás que es una clase que hereda de
Form (la que gestiona los formularios en Windows).
A continuación vamos a añadir funcionalidad a la aplicación. Para comenzar, debes poder usar
el ToolBox o caja de herramientas. En ella encontrarás componentes gráficos para añadir a tu
aplicación.
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Para añadir nuevos controles gráficos, simplemente arrástralos desde el toolbox directamente
a la pantalla.
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Añade varios elementos:
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Un textbox
Un botón con el nombre hola
Otro con el nombre mundo
Cada uno de los controles que se colocan en la pantalla tiene una serie de propiedades. Una es
el nombre dentro del programa Name otra es la información que aparece en pantalla Caption.
Al hacer dobre click sobre un botón, Visual Studio creará un método. Este método puede
usarse para cambiar las propiedades de los controles.
Añada también unas etiquetas Label de modo que el interfaz de usuario quede de la siguiente
manera:
Tu turno:
Da un nombre adecuado al nameSpace
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Coloca entradas de Log para ver qué ocurre con la aplicación. Pon una al comienzo de
cada clase y método, de forma que veas el flujo de la ejecución.
Para ello, tendrás que importar la clase de Log (añadir referencia…)
Tendrás que pasar la instancia de Log de una clase a otra para que todos
escriban sobre el mismo fichero. Para ello, añade un atributo Log a la clase
Form1.
Añade funcionalidad a los métodos de los botones Hola y Mundo de forma que al
pulsar el primero aparezca la palabra Hola en el cuadro de texto. Para modificar el
texto utiliza la propiedad Text de del cuadro de texto.
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Práctica 3. Introducción a sockets en .NET
Ahora que conoces el API de sockets de otros sistemas operativos y lenguajes como Java,
vamos a aprender cómo usar sockets desde .NET con el lenguaje C#.
En esta práctica, usaremos el Framework de .NET en lugar del Compact Framework, en
cualquier caso, el contenido de estas prácticas es trasladable a pocket pc directamente.
Estructura de clases
Para utilizar sockets es necesario importar las librerías de la class library System.Net y
System.Net.Sockets. Para ello, creamos un proyecto de tipo aplicación visual
con el SDK correspondiente y le damos el nombre de VistaCliente.cs al fichero
de código con el formulario. El otro fichero será program.cs. Introducimos el
siguiente código en los ficheros:
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
/* Programa.cs */
using System;
using System.Text;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
namespace ComputacionRed.Sockets.Cliente
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[MTAThread]
static void Main()
{
}
}
namespace ComputacionRed.Sockets.Cliente
{
public partial class VistaCliente : Form
{
public VistaCliente()
{
InitializeComponent();
}
}
}
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
La clase VistaCliente, es una clase que hereda de Form, por tanto se utiliza para proporcionar
una GUI al usuario. Por otro lado, tendremos una clase controlador ClienteConnection que
crearemos dentro del fichero programa.cs:
/* Programa.cs */
using System;
using System.Text;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
namespace ComputacionRed.Sockets.Cliente
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[MTAThread]
static void Main()
{
}
}
Socket socket;
Form vv = null;
writeLog log = null;
public ClienteConnection(Form vv)
{
this.vv = vv;
}
/* destructor */
~ClienteConnection()
{
try
{
if (socket != null)
if (socket.Connected)
socket.Disconnect(false);
}
catch (Exception ex)
{
/* lo hemos intentado ... */
}
}
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
En la clase ClienteConnection tendremos los siguientes atributos:
IPAddress dirServidor : Estructura de dirección IP para conectar con el servidor
o http://msdn.microsoft.com/en‐us/library/system.net.ipaddress.aspx
IPEndPoint endPointServidor: Contiene tanto la dirección IP como el puerto
local etc necesarios para conectar con el servidor
o http://msdn.microsoft.com/en‐us/library/system.net.ipendpoint.aspx
Int32 serverPort: puerto del servidor
Socket socket: estructura donde alojar el estado del socket
o http://msdn.microsoft.com/en‐us/library/system.net.sockets.socket.aspx
o API de sockets de Berkeley
Form vv : Para poder controlar la visualización
writeLog log: Lo veremos más adelante, es un callback (delegado en .NET)
Localice el constructor de la clase ClienteConnection. ¿Qué parámetro recibe?
¿Qué hace el destructor de la clase ClienteConnection?
En la clase VistaCliente usaremos el siguiente código para el constructor y los métodos de
inicialización:
public VistaCliente()
{
InitializeComponent();
}
public void setController(ClienteConnection cc)
{
this.cc = cc;
}
}
La clase VistaCliente tiene los siguientes atributos:
ClienteConnection cc = enlace con el controlador
byte[] sendBytes: buffer de datos a enviar al servidor
byte[] receiveBytes: buffer de datos con la respuesta del servidor.
Si echamos un vistazo al código anterior, veremos una declaración similar a un tipo:
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Consiste en un puntero a función, es decir, existe un tipo de función llamada writeLog, que
recibe una String y la imprime para que el usuario tenga información a modo de log.
Interfaz gráfico
A continuación vamos a diseñar el siguiente interfaz gráfico:
serverNameTextBox
textCheckBoxUserInput
IPTextbox sendDataTextB
serverPort oxUserInput
button1
sendButton updateBinary
ConnectionState Button
conectButton
binarySendData
testConnectionButton
responseText
responseBinary
logTextbox
Como puedes comprobar, hay varios controles (cajas de texto, botones, checkbox…) y algunos
de ellos están introducidos dentro de un contenedor. Esto es opcional, en cualquier caso, el
control que engloba a los demás (como por ejemplo Connection que engloba 3 cajas de texto,
tres botones y un label) puedes localizarlo en el toolbox como GroupBox.
Para que el código que se proporciona en los siguientes apartados funcione correctamente,
debes asegurarte de que los diferentes controles tienen la propiedad Name (dentro del
apartado Design) que se indica en las cajas de texto apuntadas por las diferentes flechas.
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Función de log
A continuación vamos a resolver el problema del log. En la clase VistaCliente creamos la
función writeLog como se muestra a continuación:
Dicha función escribe añade texto en la caja de texto logTextbox cuando se la invoca.
Si prestamos atención, veremos que dicho método tiene los mismos tipos definidos en la
declaración del apartado anterior public delegate void writeLog(string msg) por
lo que puede usarse como delegado.
En la clase ClienteConnection, inmediatamente después del destructor, creamos los métodos:
/* set log */
public void setLogFn(writeLog fn)
{
this.log = fn;
}
/* write log */
public void trace(string msg)
{
StackTrace st = new StackTrace(false);
string caller = st.GetFrame(1).GetMethod().Name;
log(caller + " : " + msg + "\r\n");
}
/* presenta la vista */
public Form getVista()
{
return vv;
}
El método setLogFn recibe un puntero a una función de tipo writeLog y la guarda en el atributo
log. En método trace recibe una string, contruye un pila de llamadas y accede a la anterior para
conocer desde que función ha sido llamada la función de log para así incluirlo en el texto de la
línea de log. Finalmente, escribe la línea y le añade al final un retorno de carro y vuelve al
comienzo de la línea ( \r\n es equivalente al \n de C/C++). La función getVista devuelve una
instancia de la clase Form, así tanto el controlador como la vista, permanecen unidos.
Compruebe como ambas clases VistaCliente y ClienteConnection están enlazadas
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Sustituye el código del main por el siguiente:
}
}
¿Qué hace el código?
Comprobando la conectividad
En este apartado vamos a comprobar la conectividad de la red antes de usarla. Para ello, el
usuario dispone de un botón con el mensaje Test connection que cambiará el label
ConnectionState indicando ok o error dependiendo del problema.
En primer lugar vamos a diseñar dicha función. Para dar un error detallado sería necesario
interrogar al API de NDIS de Windows (controla los dispositivos de red) de forma que se
pudiera averiguar si existe conectividad o no, pero lo vamos a hacer desde el nivel más alto,
desde sockets. Lo primero que haremos será definir un tipo enumerado con los posibles
errores o estados:
Los posibles estados son:
1. No hay error
2. Existe un problema con el DNS (lo cual no significa que no haya conexión)
3. Problema con la librería de sockets, con independencia del DNS no se puede abrir una
conexión.
4. Hay problemas con el DNS y con los sockets
5. Es posible crear un socket pero probablemente la conexión sólo es local o un firewall
bloquea el tráfico
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
A continuación, vamos a plantear la estrategia del test:
1. Tratamos de resolver el nombre www.google.es
a. Si falla dnsProblem y continuamos
2. Tenemos o no DNS, pero puede que sólo local
a. Tratamos de conectar a una IP y si hay éxito continuamos
b. Tratamos de conectar a una IP y si no hay éxito salimos con error
socketProblem o dnsAndSocketProblem.
3. Tratamos de descargar una página web
a. Si hay éxito: ok
b. Si falla: networkErrorOrUnreachable
Por lo tanto, el código del método para probar la conectividad es el siguiente (inclúyelo como
métodos de la clase ClienteConnection:
try
{
testIP = Dns.GetHostEntry("www.google.es").AddressList[0];
testEndPoint = new IPEndPoint(testIP, 80);
}
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Acabas de ver tu primer programa con sockets en .NET:
¿Cuál es el proceso para abrir una conexión con otro equipo?
¿Qué NameSpaces se utilizan?
¿Qué clases?
¿Notas diferencias con otros APIs?
Ahora incorpora la funcionalidad al botón de prueba (doble click y Visual Studio generará el
método, el código es el siguiente:
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Interactuando con un servidor, preparando los datos
Antes de comenzar a enviar datos al servidor, es necesario prepararlos. El cliente que estamos
diseñando permite introducir datos de dos formas:
1. En modo texto: cualquier letra introducida se codifica con ASCII y se envía salvo el
código \n que se traduce a retorno de carro. Esto es interesante por si se quiere usar
HTTP directamente o para probar un servidor que estamos programando
a. Para ello, el usuario introduciría GET /index.html HTTP/1.0\n\n
directamente en el cuadro de texto sendDataTextBoxUserInput, haría click en
la casilla textCheckBoxUserInput (mirar la figura del GUI) y luego en
updateBinaryButton
2. Modo hexadecimal: Se introduce la información en hexadecimal. Si se quiere
introducir un buffer de datos 2FC487, se teclea 2F C4 87 y no se marca la casilla de
modo texto. En cualquier caso, siempre es necesario pulsar el botón Update Binary
antes de enviar algo al servidor.
Por lo tanto, necesitamos un método que prepare los datos del usuario para su envío al
servidor. El método se invoca cuando se pulsa el botón updateBinaryButton. Por tanto, para
programarlo, haga doble click sobre dicho botón. Analiza el siguiente código y úsalo:
sendBytes = Encoding.ASCII.GetBytes(sendDataText);
}
else
{
if (sendDataTextBoxUserInput.Text.Length >= 2)
{
string delimiter = " ";
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
}
}
}
if (sendBytes != null)
for (int i = 0; i < sendBytes.Length; i++)
{
binarySendData.AppendText(sendBytes[i].ToString("x") + "
");
}
}
¿Qué hacen los métodos de la clase String llamados Split, IndexOfAny y Equals?
¿Qué hace Encoding.ASCII.GetBytes?
¿Cómo se comprueba si los datos son texto o hexadecimales?
¿Cómo se comprueban los errores de formato en el caso hexadecimal?
Comprueba si todos los errores se corrigen
Utiliza la depuración línea por línea para ver qué hace cada parte del código. Lo
mejor es poner un breakpoint al comienzo del método y luego ir línea por línea con
F10.
Interactuando con un servidor, iniciando la conexión
Del apartado de prueba de conectividad, habrás aprendido a abrir un socket, ahora lo haremos
paso por paso.
Funcionalidad del botón Resolve
Cuando se pulsa el botón Resolve (button1), debe usarse este código:
{
if (serverNameTextBox.Text.Length > 0)
{
IpTextbox.Text =
cc.getServerIP(serverNameTextBox.Text).ToString();
}
else
cc.trace("por favor, incluye el nombre del servidor");
}
Por lo tanto, puede comprobarse que la funcionalidad está en la clase ClienteConnection pese
a que los resultados (IP traducida) se muestren en la caja de texto IpTextbox.
Utiliza los siguientes métodos dentro de la clase ClienteConnection:
}
/* dada una ip o nombre de maquina, cambia la direccion del servidor
*/
public void setServerIP(String serverIPString)
{
dirServidor = Dns.GetHostEntry(serverIPString).AddressList[0];
}
public void setServerPort(String port)
{
Int32 result = 0;
try{
Int32.TryParse(port, out result);
serverPort = result;
}catch(Exception ex)
{
trace("es correcto el puerto?");
}
}
Razona sobre lo que hace cada uno de ellos.
¿Cuáles cambian el valor de atributos de la clase ClienteConnection?
Pruébalo con www.google.es y con www.uc3m.es
Claramente google usa balanceo de carga con DNS (puedes comprobarlo desde un
intérprete de comandos (cmd) con el comando nslookup www.google.es
Si lo ejecutas varias veces puedes ver cómo cambian el grupo de IPs que
proporcionan el servicio de google.
¿Por qué el programa devuelve sólo la primera?. ¿Cómo lo cambiarías para
que te diera de las tres aleatoriamente?
Funcionalidad del botón connect! (conectButton)
Este método debe proporcionar al controlador todos los datos de dirección del servidor,
puerto necesario para conectar y además realizar control de errores. Utiliza el siguiente
código:
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
El único método que quedaría por programar sería connect() dentro de la clase
ClienteConnection. Este método debe hacer lo siguiente (para nuestros propósitos):
1. Comprobar si el socket es nulo.
a. Si es null, lo creará
b. Si no, comprueba si está conectado, y en ese caso lo desconecta
2. Crea un objeto IPEndPoint con la información necesaria
3. Crea un socket
4. Conecta
Analiza el siguiente código y úsalo en la aplicación:
/* conecta al servidor */
public void connect()
{
try
{
if (socket != null)
if (socket.Connected)
{
trace("cerrando antiguas conexiones...");
socket.Disconnect(true);
}
trace("creando el endpoint...");
endPointServidor = new IPEndPoint(dirServidor, serverPort);
trace("creando el socket...");
socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
trace("conectando");
socket.Connect(endPointServidor);
}
catch (Exception ex)
{
trace("error en la conexión" + ex.Message);
}
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Interactuando con un servidor, enviando y recibiendo datos
Finalmente debemos dar funcionalidad al botón send (sendButton) para que, a través de la
clase ClienteConnection, envíe los datos al servidor.
Utiliza el siguiente código para el botón send:
¿Qué hace este método?
¿Qué elementos de la GUI están involucrados?
Utiliza el siguiente código para la clase ClienteConnection:
¿Sabías que C#, a diferencia de Java, permite parámetros por referencia en los
métodos?
¿Qué palabra reservada crees que le indica al compilador que es por referencia y no
por valor?
Haz peticiones HTTP a varios servidores, prueba que todo funcione bien.
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Práctica 4. Servidor en .NET
En esta práctica vamos a crear un sencillo servidor para atender las peticiones de los clientes.
Para ello crearemos una aplicación de consola:
Y usaremos el siguiente código como base para implementar el protocolo que comentaremos
a continuación:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace ComputacionRed.Sockets.Servidor
{
class Program
{
static void Main(string[] args)
{
IPAddress direc =
Dns.GetHostEntry("localhost").AddressList[0];
socket.Listen(100);
try
{
do
{
count = handler.Receive(bytes);
data = System.Text.Encoding.ASCII.GetString(bytes, 0,
count);
bytes = System.Text.Encoding.ASCII.GetBytes(response);
handler.Send(bytes,
System.Text.Encoding.ASCII.GetByteCount(response), SocketFlags.None);
Console.WriteLine("Conexion finalizada");
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception ex)
{
Console.WriteLine("Excepcion" + ex.Message);
}
}
}
}
Analiza las líneas de código suministradas
Ahora debes crear un protocolo con el siguiente formato. Lo que se enviarán serán cadenas de
texto con la estructura $Comando$Valor. El protocolo es sin estado y los comandos y sus
posibles valores son:
Hello: Debe ir acompañado del nombre del cliente (ej. $Hello$Dani ). El servidor debe
responder $Hello$NiceToSeeYouAgain
Echo: Debe ir acompañado de un texto de longitud variable (ej. $Echo$Texto a repetir).
El servidor debe contestar $Echo$ + el texto mandado por el cliente
Date: No tiene valor, se envía únicamente el comando. El servidor debe responder
$Date$Dia/Mes/Año
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.
Apuntes de Computación en la Red. Prácticas demostrativas de .NET
Time: No tiene valor, se envía únicamente el comando. El servidor debe responder
$Date$Hora:Minuto
Random: No tiene acompañamiento. El servidor debe devolver 20 bytes aleatorios.
Exit: finaliza la conexión
Para hacerlo, puede necesitar las siguientes Clases/métodos (usa google y el código
proporcionado hasta ahora para conseguirlo):
1. System.DateTime.Now
2. String.Split
3. System.Text.Encoding.ASCII.GetBytes
4. Random
5. System.Text.Encoding.ASCII.GetByteCount
Cambia ahora a protocolo con sesión, usa el mensaje de Hello para ello.
Cliente en consola
Ahora que has probado el servidor, crear un cliente basado en consola que interactúe con el
servidor, esta vez, sin ayuda…
Daniel Díaz Sánchez, Andrés Marín López, Florina Almenarez (http://pervasive.it.uc3m.es)
Departamento de Ingeniería Telemática. Universidad Carlos III de Madrid.