Dcsefg a Gdf

8
Pontificia Universidad Cat´ olica de Chile Rev : 2432 Escuela de Ingenier´ ıa Departamento de Ciencia de la Computaci´ on IIC1222 – Programaci´on Avanzada Tutorial 06 - Sockets Realizado por Bernardita Torres y Andrea V´ asquez 1. Introducci´on Muchas veces necesitaremos desarrollar aplicaciones que tengan que conectarse a un proceso determinado que est´ a corriendo en una otra m´aquina. C# nos provee las clases Socket, TcpClient y TcpListener, dentro del namespace System.Net.Sockets, que nos permiten trabajar de forma transparente con conexiones. En este tutorial revisaremos sus m´ etodos principales, y desarrollaremos tres ejercicios guiados. 2. Sobre las conexiones Un socket es una combinaci´on entre la direcci´on de una m´aquina y un puerto. Existen distintos tipos de direcciones, siendo la m´as conocida la Internet Protocol address odirecci´onIP. ´ Esta es una identificaci´on num´ erica que es asignada a todo dispositivo que se conecte usando el Internet Protocol. Por ejemplo, la direcci´on 146.155.99.60 identifica al computador que tiene la p´agina de la Universidad. Una vez que tenemos la direcci´on de la m´aquina, se debe determinar cu´ al proceso con el queremos comunicarnos. Esto se hace a trav´ es del puerto, que es un n´ umero entero positivo entre 0 y 65535, que determina el proceso con el cual nos conectaremos en la m´aquina determina por la direcci´on. Todo este proceso de comunicaci´ on se realiza siguiendo un cierto protocolo, es decir, un conjunto de reglas que determinan el formato y la forma de transmisi´on de los datos. Nosotros nos preocuparemos de un protocolo, el Transmission Control Protocol: TCP. ´ Este es un protocolo orientado a la conexi´on, que asegura la llegada de la informaci´on de un extremo a otro. A pesar de que no lo usaremos, es importante saber de que existe otro protocolo llamado User Datagram Protocol o UDP. 3. Clase Socket Un socket representa uno de los extremos de la comunicaci´ on, y est´ a compuesto por una direcci´on, un puerto y el protocolo subyacente que se utilizar´a para establecer la comunicaci´ on. 3.1. Constructor Si vemos el canal de comunicaci´ on que estableceremos como un stream, el constructor de la clase se hace intuitivo: using System.Net.Sockets; public Socket ( AddressFamily addressFamily , SocketType socketType , ProtocolType protocolType} AddressFamily : Enumeraci´on, nos interesa AddressFamily.InterNetwork, que indica que usaremos direcciones IP version 4 SocketType : Enumeraci´on, usaremos SocketType.Stream. 1

description

archivo hueco

Transcript of Dcsefg a Gdf

Page 1: Dcsefg a Gdf

Pontificia Universidad Catolica de Chile Rev : 2432

Escuela de Ingenierıa

Departamento de Ciencia de la Computacion

IIC1222 – Programacion Avanzada

Tutorial 06 - Sockets

Realizado por Bernardita Torres y Andrea Vasquez

1. Introduccion

Muchas veces necesitaremos desarrollar aplicaciones que tengan que conectarse a un proceso determinado queesta corriendo en una otra maquina. C# nos provee las clases Socket, TcpClient y TcpListener, dentro del namespace

System.Net.Sockets, que nos permiten trabajar de forma transparente con conexiones. En este tutorial revisaremossus metodos principales, y desarrollaremos tres ejercicios guiados.

2. Sobre las conexiones

Un socket es una combinacion entre la direccion de una maquina y un puerto. Existen distintos tipos de direcciones,siendo la mas conocida la Internet Protocol address o direccion IP. Esta es una identificacion numerica que es asignadaa todo dispositivo que se conecte usando el Internet Protocol. Por ejemplo, la direccion 146.155.99.60 identifica alcomputador que tiene la pagina de la Universidad. Una vez que tenemos la direccion de la maquina, se debe determinarcual proceso con el queremos comunicarnos. Esto se hace a traves del puerto, que es un numero entero positivo entre0 y 65535, que determina el proceso con el cual nos conectaremos en la maquina determina por la direccion. Todoeste proceso de comunicacion se realiza siguiendo un cierto protocolo, es decir, un conjunto de reglas que determinanel formato y la forma de transmision de los datos. Nosotros nos preocuparemos de un protocolo, el TransmissionControl Protocol: TCP. Este es un protocolo orientado a la conexion, que asegura la llegada de la informacion deun extremo a otro. A pesar de que no lo usaremos, es importante saber de que existe otro protocolo llamado User

Datagram Protocol o UDP.

3. Clase Socket

Un socket representa uno de los extremos de la comunicacion, y esta compuesto por una direccion, un puerto y elprotocolo subyacente que se utilizara para establecer la comunicacion.

3.1. Constructor

Si vemos el canal de comunicacion que estableceremos como un stream, el constructor de la clase se hace intuitivo:

using System.Net.Sockets;

public Socket(AddressFamily addressFamily , SocketType socketType ,

ProtocolType protocolType}

AddressFamily : Enumeracion, nos interesa AddressFamily.InterNetwork, que indica que usaremos direccionesIP version 4

SocketType: Enumeracion, usaremos SocketType.Stream.

1

Page 2: Dcsefg a Gdf

ProtocolType: Enumeracion, como queremos usar TCP, escogemos ProtocolType.Tcp.

3.2. Metodos

Una vez que tenemos creado el Socket, los metodos que llamaremos dependeran del lado en que estamos trabajando.Del lado del servidor, tenemos:

Bind → Asocia al Socket con una direccion IP local. Recibe de parametro el IP del computador local, junto conel puerto en el cual se estabelcera la conexion.

Listen → Pone al Socket en un estado de espera por conexiones entrantes (esta “escuchando”). Recibe comoparametro la cantidad maxima de conexiones que pueden estar esperando para acceder al Socket. Este metododebe ser llamado despues que Bind.

Accept → Espera por las conexiones entrantes, y retorna un nuevo Socket para la conexion recibida.

Para el Socket del cliente son relevantes los siguientes metodos:

Connect → Recibe de parametros la direccion IP del servidor y el puerto al cual el cliente desea conectarse.

Para enviar informacion entre cliente y servidor usamos Send (byte[] data) y Receive (byte[] data), pero estosmetodos tienen dos grandes inconvenientes. En primer lugar nos obligan a trabajar con arreglos de byte, lo cual esengorroso, y en segundo lugar son metodos que bloquean la aplicacion (es decir, se quedan esperando hasta que elmensaje haya sido enviado / recibido o se lance una excepcion, sin poder hacer nada mas mientras esto no ocurra).En general el manejo de conexiones con la clase Socket es tedioso y repetitivo, y por lo mismo en la siguiente seccionestudiaremos una mejor forma de hacerlo.

4. Clases TcpClient y TcpListener

TcpClient y TcpListener son dos clases que nos permiten manejar de una forma mas simple las conexiones. Entreotras cosas, facilitan el establecimiento de la conexion, mandar y recibir datos (exponiendo el canal de comunicacioncomo un stream) y escuchar conexiones entrantes.

4.1. TcpClient

Para que TcpClient pueda conectarse e intercambiar datos, debe haber un TcpListener creado con un protocoloTCP, a la escucha de solicitudes de conexin entrantes. Existen 2 maneras de conectarse con un TcpListener o host1. Laprimera es creando el TcpClient y luego conectarse a travs de alguno de los metodos Connect de la clase. La segundaes creando el TcpClient usando el constructor que requiere un nombre de host y numero de puerto; este constructorintentara realizar automaticamente la conexion. Una vez que se ha realizado la conexion, es posible mandar y recibirdatos a traves del stream de TcpClient.

TcpClient()→ Constructor vacıo.

TcpClient(IPEndPoint)→ Constructor a traves de un EndPoint2

TcpClient(string, int32)→ Constructor a traves de nombre de host y numero de puerto.

Connect(IPEndPoint)→ Conecta el cliente a un host TCP remoto con el extremo de red remoto especificado.

Connect(IPAddress, Int32)→ Conecta el cliente a un host TCP remoto con la direccin IP y el nmero de puertoespecificados.

Connect(IPAddress[], Int32)→ Conecta el cliente a un host TCP remoto con las direcciones IP y el nmerode puerto que se hayan especificado.

1Host es una maquina distante a cuyo contenido se puede acceder a traves de la red2IPEndPoint es una clase que representa auna direccion IP y un numero de puerto determinados. Su constructor es ası: IPEnd-

Point(IPAddress, Int32)

2

Page 3: Dcsefg a Gdf

Connect(string, Int32)→ Conecta el cliente al puerto especificado, en el host3 especificado.

GetStream()→ Devuelve un objeto del tipo NetworkStream.

Read(byte[], int offset, int size))→ Recibe datos procedentes del host.

Write(byte[], int offset, int size)→ Envıa datos al host remoto.

Close()→ Cierra la conexion.

4.2. TcpListener

Esta clase es la que espera y acepta conexiones entrantes, es por ello que esta se identifica con el host. Una vez quese ha creado un objeto de tipo TcpListener, se comienza a esperar conexiones para despues aceptarlas.

TcpListener(IPEndPoint)→ Constructor a traves de un EndPoint

TcpListener(string, int32)→ Constructor a traves de nombre de host y numero de puerto.

Start()→ Comienza la escucha de conexiones.

AcceptTcpClient()→ Acepta una solicitud de conexion entrante. Devuelve un objeto de tipo TcpClient

Stop()→ Termina la escucha. NO cierra conexiones existentes.

5. Ejercicios

5.1. Ejercicio 1

5.1.1. Enunciado

En el ejemplo de Controles de la clase 16 se implemento un cambiador de imagenes. Supongase que ahora se deseallevar mas alla el programa, haciendo un Viewer que permita ver las mismas imagenes desde otra ventana, pero quepermita pasar a la siguiente solamente si se ha pasado a otra imagen desde el cambiador. ¿Que cambios habrıa quehacerle al ejemplo, usando la clase Socket, para lograr lo pedido?

5.1.2. Solucion

En primer lugar se debe implementar el Form para el Viewer. Para ello procedemos a crear una Windows Application,y agregamos al Form un PictureBox y un boton. En la clase Form1 del Viewer hacemos lo siguiente:

public partial class Form1 : Form

{

// Agregamos a los atributos existentes lo siguiente:

Socket s;

// Aqui guardaremos los datos que recibiremos del socket.

//Es de largo 4 para ser consistentes , pues al convertir

//de int a byte[] se retorna un arreglo de largo 4.

byte[] data = { 0, 0, 0, 0 };

public Form1 ()

{

InitializeComponent ();

imagesList = Directory.GetFiles(@"imgs", "*.jpg");

// Creamos el socket y nos conectamos a t r a v s del puerto 8000

s = new Socket(AddressFamily.InterNetwork , SocketType.Stream ,

ProtocolType.Tcp);

3Un host Ej: www.google.com

3

Page 4: Dcsefg a Gdf

s.Connect(IPAddress.Parse("127.0.0.1"), 8000);

s.Receive(data);

// Cambiamos la imagen del PictureBox segun lo que haya habido inicialmente

//en el ImageController , convirtiendo el arreglo de bytes en un int usando

//el metodo BitConverter.ToInt32

ChangePictureBox (BitConverter.ToInt32(data , 0));

}

// Se mantinen los metodos existentes y se agrega la subscripcion

// al boton. De esta forma , y para evitarnos problema de bloqueo

// de sockets , la imagen se cambiara cada vez que se aprete el

// boton del Viewer (y se haya cambiado en el ImageController)

private void button1_Click(object sender , EventArgs e)

{

// Nos aseguramos que el socket este listo para ser leido

//(s.Poll (..) retorna true).

if (s.Poll(-1, SelectMode.SelectRead ))

{

s.Receive(data);

ChangePictureBox (BitConverter.ToInt32(data , 0));

}

}

}

En la clase ImageController:

public partial class ImageController : Form

{

// Agregamos los siguientes atributos

Socket newConnection;

Socket s;

public ImageControler ()

{

InitializeComponent ();

// Toma todas las im genes de la carpeta imgs

imagesList = Directory.GetFiles(@"imgs", "*.jpg");

// Rellena la lista de imagenes

FillList ();

try

{

// Creamos el socket TCP/IP

s = new Socket(AddressFamily.InterNetwork , SocketType.Stream , ProtocolType.Tcp);

// Creamos los objetos que nos permitiran conectarnos al

// IP local en el puerto 8000

IPAddress ipAddress = IPAddress.Parse("127.0.0.1");

IPEndPoint ipEnd = new IPEndPoint(ipAddress , 8000);

// Le vinculamos al puerto

s.Bind(ipEnd );

// Le decimos que escuche , y espere hasta 10 conexiones

s.Listen (10);

// Esperamos la conexi n

newConnection = s.Accept ();

}

4

Page 5: Dcsefg a Gdf

catch(Exception)

{

s.Close ();

}

// Va a la primera imagen

ChangeImage (0);

}

private void buttonChange_Click(object sender , EventArgs e)

{

// Cuando se haga click en el boton , avanzamos a la siguiente

// imagen de la lista

ChangeImage(currentIndex + 1);

// Enviamos la informacion de la nueva imagen

byte[] data = BitConverter.GetBytes(currentIndex );

if (newConnection != null)

newConnection.Send(data);

}

// Continua el resto de los m t o d o s

5.2. Ejercicio 3

5.2.1. Enunciado

Usted tiene un restaurant y se le ha ocurrido desarrollar una novedad tecnologica para la toma de pedidos. En cadamesa, habra un computador en el cual los clientes podran realizar su pedido. En la cocina, existira otro computadorque recibe los pedidos que llegan de las mesas y los procesa, para despues mostrarle al Chef lo que debe preparar. Deesta manera, los clientes no tendran que esperar a que un mozo llegue a la mesa a atenderlos y los pedidos llegaranrapidamente a la cocina. Se debe mandar un pedido por persona en la mesa y este contiene lo siguiente: Entrada,PlatoFondo, Postre y Bebestible. Ademas, debe tener un numero de pedido y numero de asiento y mesa de la personaque ordeno el pedido. Implementa la clase Pedido, ClienteConexion y Servidor con todo lo necesario para resolver esteproblema

5.2.2. Solucion

[Serializable]

public class Pedido

{

private string entrada;

private string platoFondo;

private string postre;

private string bebestible;

private Guid id;

private int asiento;

private int mesa;

// Guid quiere decir Global Unique Identifier

public Pedido ()

{

id = new Guid ();

}

public string Entrada {

get { return entrada; }

set { entrada = value; }

5

Page 6: Dcsefg a Gdf

}

public string PlatoFondo {

get { return platoFondo; }

set { platoFondo = value; }

}

public string Postre {

get { return postre; }

set { postre = value; }

}

public string Bebestible {

get { return bebestible; }

set { bebestible = value; }

}

public int Asiento {

get { return asiento; }

set { asiento = value; }

}

public int Mesa {

get { return mesa; }

set { mesa = value; }

}

}

using System.Collections.Generic;

using System.Runtime.Serialization;

using System.Net.Sockets;

public class ClienteConexion

{

private List <Pedido > pedidos;

public ClienteConexion (){}

// Recibe pedidos de alguna parte , para agregarlos a la lista que se envia

public void CargarPedido(Pedido p)

{

pedidos.Add(p);

}

// Envia pedidos a la cocina

public void EnviarPedidos ()

{

// Se abre conexion

TcpClient client = new TcpClient("www.cocina.com", 10);

// Se crea el formatter y el stream se obtiene de Client

IFormatter formatter = new BinaryFormatter ();

NetworkStream stream = client.GetStream ();

// Se intenta serializar y se envia.

try

{

formatter.Serialize(stream , pedidos );

6

Page 7: Dcsefg a Gdf

}

catch (SerializationException e)

{

Console.WriteLine("No se pudo serializar: " + e.Message );

}

finally

{

stream.Close ();

}

//Se cierra TcpClient

client.Close ();

}

}

using System.Collections.Generic;

using System.Runtime.Serialization;

using System.Net.Sockets;

public class Servidor

{

private Chef chef;

private List <Pedido > pedidos = null;

public Servidor(Chef c)

{

chef = c;

}

// Recibe pedidos desde la mesa

public void RecibirPedidos ()

{

TcpListener server = new TcpListener (10);

server.Start ();

// Pido el TcpClient para poder acceder al stream.

TcpClient client = server.AcceptTcpClient ();

NetworkStream stream = client.GetStream ();

try

{

BinaryFormatter formatter = new BinaryFormatter ();

pedidos = (List <Pedido >) formatter.Deserialize(stream );

}

catch (SerializationException e)

{

Console.WriteLine("No se pudo deserializar: " + e.Message );

}

finally

{

stream.Close ();

}

client.Close ();

server.Stop ();

ProcesarPedidos ();

}

7

Page 8: Dcsefg a Gdf

// Escribe el pedido y manda el pedido a ser cocinado por el chef

public void ProcesarPedidos ()

{

foreach(Pedido p in pedidos)

{

Console.WriteLine("Pedido de cliente numero " + p.Asiento +

", de la Mesa " + p.Mesa + ":");

Console.WriteLine("Entrada: " + p.Entrada );

Console.WriteLine("Plato de Fondo: " + p.PlatoFondo );

Console.WriteLine("Postre: " + p.Postre );

Console.WriteLine("Para tomar: " + p.Bebestible );

Console.WriteLine("");

chef.Cocinar(p);

}

}

}

8