You are on page 1of 8

7.

SOCKETS
En este proyecto nos hemos decantado por los sockets para implementar la
comunicacin cmara-ordenador. Son responsables del importante papel de enviar los
comandos que provocan el movimiento de la cmara, as como de recibir las
imgenes que captamos a travs de ella. Por ello, creemos necesario dedicarles esta
seccin.

7.1 QU SON?
Los sockets son la interfaz ms difundida que hay para la comunicacin de
procesos. Socket designa un concepto abstracto por el cual dos programas
(posiblemente situados en computadoras distintas) pueden intercambiarse cualquier
flujo de datos, de manera transparente, sin conocer los detalles de como se transmiten
esos datos, y generalmente de manera fiable y ordenada.
Para que dos programas puedan comunicarse entre si es necesario que un
programa sea capaz de localizar al otro, y adems, que ambos programas sena
capaces de intercambiarse cualguier secuencia de octetos, es decir, datos relevantes a
su finalidad.
Para ello son necesarios los tres recursos que originan el concepto de socket, y
gracias a los cuales ste queda definido:

Un protocolo de comunicaciones, que permite el intercambio de octetos.

Una direccin del Protocolo de Red (direccin IP, si se utiliza el protocolo


TCP/IP), que identifica una computadora.

Un nmero de puerto, que identifica a un programa dentro de una


computadora.

De aqu se deduce que la propiedades inherentes a los sockets dependen de las


caractersticas del protocolo en el que se implementan. El protocolo ms utilizado es
TCP, gracias al cual los sockets tienen las propiedades de ser orientados a conexin y
de garantizar la transmisin de todos los octetos sin errores ni omisiones, y que stos
llegan a su destino en el mismo orden en que se transmitieron.
Aunque tambin puede usarse el protocolo UDP. ste es un protocolo no
orientado a conexin. Slo se garantiza que si un mensaje llega, llega bien. En ningn
caso se garantiza que llege o que lleguen los mensajes en el mismoorden que se
mandaron. Esto lo hace adecuado para el envo de mensajes frecuentes pero no
demasiado importantes, como por ejemplo, mensajes para los refrescos
(actualizaciones) de un grfico.
En los orgenes de Internet, las primeras computadoras en implementar sus
protocolos fueron aquellas de la universidad de Berkeley. Dicha implementacin tuvo
lugar en una variante del sistema operativo Unix conocida como BSD Unix. Pronto
se hizo evidente que los programadores necesitaran un medio sencillo y eficaz para
escribir programas capaces de intercomunicarse entre s. Esta necesidad dio origen a
la primera especificacin e implementacin de sockets, tambin en Unix, en 1981,
conocidos como BSD sockets (o Berkeley sockets). Se hicieron para proveer al
desarrollador de una API mediante la cual pudiera utilizar el protocolo sin
complicaciones. Hoy da son un estndar de facto, y estn implementados como
bibliotecas de programacin para multitud de sistemas operativos.
Los sockets se caracterizan por ser una interfaz mediante la cual podemos
comunicarnos con otros procesos, utilizando descriptores de ficheros. Es decir, como
todo en Unix se realiza escribiendo y leyendo ficheros, los sockets se basan en esto
tambin. Cuando establecemos una comunicacin entre dos sockets, cada uno tiene
un descriptor de fichero en el que escribe y lee para comunicarse con el otro socket.

Figura 7.1: socket entre dos anfitriones

Los sockets permiten implementar una arquitectura cliente-servidor. La


comunicacin ha de ser iniciada por uno de los programas que se denomina programa
cliente. El segundo programa espera a que otro inicie la comunicacin, por este
motivo se denomina programa servidor.
Desde el punto de vista de programacin, un socket no es ms que un fichero
que se abre de una manera especial. As, un socket es un fichero existente en la
mquina cliente y en la mquina servidora, que sirve en ltima instancia para que el
programa servidor y el cliente lean y escriban la informacin. Esta informacin ser
la transmitida por las diferentes capas de red.
Los sockets, al ser de bajo nivel, no resultan muy cmodos para el programador.
Al no permitir el paso directo de argumento, el programador tiene que encargarse de
abrir/cerrar los flujos de entrada/salida, colocar en ellos los argumentos que quiere
pasar, extraer los resultados, etc. Adems estn muy ligados a la plataforma donde se
ejecutan y el cdigo es difcil de reutilizar. Pero por otro lado los sockets son rpidos,
al ser de bajo nivel introducen poca sobrecarga a las aplicaciones, lo que los hace
ideales para nuestra aplicacin. Y como son tan populares y difundidos,
prcticamente todos los lenguajes de programacin los soportan, y por supuesto
tambin que aqu se emplea.

7.2 SOCKETS EN C#
La programacin de sockets en .NET es posible gracias a la clase Socket
presente en el espacio de nombres System.Net.Sockets. Esta clase Socket tiene varios
mtodos y propiedades y un constructor.

7.2.1 Creacin
El primer paso es crear un objeto de esta clase, usando el constructor para ello.
As es como creamos el socket:
move_cam = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Cdigo 7.1: declaracin de un socket

El primer parmetro es la familia de la direccin (AddressFamily) que ser


usada, en este caso, InterNetwork ( que viene a ser IP versin 4). Con el siguiente
especificamos el tipo de socket, escogiendo sockets fiables orientados a conexin y
de doble sentido (stream) en vez de no fiables y no orientados a conexin
(datagramas). Obviamente, especificamos Stream como tipo y finalmente estaremos
usando TCP/IP, por lo que especificamos Tcp como tipo de protocolo.
3

7.2.2 Conexin
Una vez hemos creado un socket necesitamos crear una conexin con el
servidor. Las dos aplicaciones que necesitan comunicarse tienen primero que crear
una conexin entre ellas. Las dos aplicaciones necesitarn identificar a la otra.
Veamos como funciona esto en .NET.
Para conectar con la computadora remota necesitamos conocer la direccin IP y
el puerto al cual conectar. En .NET hay una clase en el espacio de nombres
System.Net llamada IPEndPoint, la cual representa una como una direccin IP y un
nmero de puerto. Para conseguir la direccin dada por una cadena de caracteres se
usa el mtodo Parse. Una vez el punto final est listo se usa el mtodo Connect de la
clase Socket para conectar al punto final (el servidor o computadora remota).
IPEndPoint RemoteCam = new
System.Net.IPEndPoint(IPAddress.Parse("192.168.1.253"),80);
move_cam.Connect(RemoteCam);
Cdigo 7.2: conexin de un socket con un servidor conocido

Si el servidor est funcionando y escuchando, la conexin tendr xito. Si en


cambio el servidor no est operativo, ser lanzada una excepcin llamada
SocketException. Asumiendo que la conexin est hecha, ya se puede mandar
informacin al otro lado.

7.2.3 Sockets Sncronos/asncronos


Cuando un lado (cliente o servidor) enva informacin al otro lado se que ste
tiene que leer los datos. Pero, cmo sabe el otro lado que la informacin ha llegado?
Hay dos opciones: la aplicacin comprueba regularmente si han llegado datos o
alguna clase de mecanismo notifica a la aplicacin y sta puede leer los datos en ese
momento.
Sockets Sncronos
Se manda informacin de una aplicacin a otra usando el mtodo Send. Send
bloquea, es decir, espera hasta que los datos hayan sido enviados o hasta que se lance
un excepcin. De la misma forma que hay un mtodo Send para enviar existe un
mtodo Receive para recibir los bytes. Igualmente, Receive bloquea la ejecucin del
programa hasta que algn tipo de informacin sea recibida o hasta que se lance una
excepcin.
En este cdigo se muestra como se enva una cadena Txx: todo lo que se mande
ha de hacerse en forma de bytes, por lo que previamente hay que convertir los
caracteres en bytes. A continuacin, se recibe un chorro de bytes proveniente del
servidor, que quedan almacenados en el vector Rx, y la longitud de ste en iRx.
4

try
{

String Txx = "Hello There";


byte[] Txx = System.Text.Encoding.ASCII.GetBytes(Txx);
move_cam.Send(Txx);

}
catch (SocketException se)
{
MessageBox.Show ( se.Message );
}
byte [] Rx = new byte[1024];
int iRx = move_cam.Receive(Rx);
Cdigo 7.3: ejemplo de enviar y recibir sncronamente con un socket

Sockets Asncronos
La clase Socket de .NET ofrece un mtodo llamado BeginReceive para recibir
datos asncronamente, es decir, de manera que no exista bloqueo. Necesita que se le
pase, entre otros parmetros, un buffer, que ser donde se almacenen los datos
recibidos, y una funcin callback que (delegado) que ser llamada en cualquier que se
reciban datos. Esto significa que la funcin BeginAsyncRead, ha sido completada. A
continuacin se muestran las signaturas de ambas funciones:

Public IAsyncResul BeginReceive (byte[] buffer, int offset, int size,


SocketFlags socketFlags, AsyncCallback callback, object state)
void AsyncCallback (IAsyncResult ar)
Cdigo 7.4: signatura de BeginReceive y su mtodo callback

El mtodo callback devuelve void y recibe un parmetro, interfaz IAsyncResult,


que contiene el estado de la operacin asncrona.
Digamos que hacemos una llamada a BeginReceive y despus de un tiempo los
datos llegan y nuestra funcin callback es llamada. Los datos estn ahora disponibles
en el buffer que se pas como primer parmetro, cuando se hizo la llamada al mtodo
BeginReceive.
Pero antes de acceder al buffer es necesario llamar a la funcin EndReceive
sobre el socket. EndReceive devolver el nmero de bytes recibidos. No es legal
acceder al buffer antes de llamar a EndReceive. El siguiente cdigo muestra como
hacer una recepcin asncrona:

byte[] m_DataBuffer = new byte [10];


IAsyncResult m_asynResult;
public AsyncCallback pfnCallBack ;
public Socket m_socClient;
// create the socket...
public void OnConnect()
{
m_socClient = new Socket (AddressFamily.InterNetwork,SocketType.Stream
,ProtocolType.Tcp );
// get the remote IP address...
IPAddress ip = IPAddress.Parse ("10.10.120.122");
int iPortNo = 8221;
//create the end point
IPEndPoint ipEnd = new IPEndPoint (ip.Address,iPortNo);
//connect to the remote host...
m_socClient.Connect ( ipEnd );
//watch for data ( asynchronously )...
WaitForData();
}
public void WaitForData()
{
if ( pfnCallBack == null )
pfnCallBack = new AsyncCallback (OnDataReceived);
// now start to listen for any data...
m_asynResult =
m_socClient.BeginReceive
(m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,pfnCallBack,null);
}
public void OnDataReceived(IAsyncResult asyn)
{
//end receive...
int iRx = 0 ;
iRx = m_socClient.EndReceive (asyn);
char[] chars = new char[iRx + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(m_DataBuffer, 0, iRx, chars, 0);
System.String szData = new System.String(chars);
WaitForData();
}
Cdigo 7.5: ejemplo de recepcin asncrona

La funcin OnConnect hace una conexin con el servidor y luego una llamada a
WaitForData. Si nos ceimos a lo que es la recepcin de datos, esto es integramente
hecho en WaitForData, que crea la funcin callback y hace una llamada a
BeginReceive pasndole un buffer global y la funcin callback. Cuando los datos
llegan OnDataReceive es llamado y, por consiguiente, el mtodo EndReceive del
socket, que devolver el nmero de bytes recibidos. A partir de aqu habr que
gestionar los datos recibidos, en este caso son copiado en una cadena y se realiza una
nueva llamada a WaitForData, que llamar BeginReceive otra vez y as
sucesivamente.

Realmente, es lo mismo procedimiento que llevamos a cabo en la WiimoteLib


para recibir los reports del Wiimote.

Un detalle interesante es que el mtodo BeginReceive devuelve una interfaz


IAsyncResult, que es lo mismo que se le pasa al mtodo callback. La interfaz
IasyncResult tiene varias propiedades. La primera de ellas AsyncState- es un objeto
de la misma naturaleza que el ltimo parmetro que requiere BeginReceive. La
segunda propiedad es AsyncWaitHandle que discutiremos en un momento. La tercera
propiedad indica si la recepcin fue realmente asncrona o si termin sncronamente.
El siguiente parmetro es IsComplete que indica si la operacin ha sido completada o
no.
En cuanto a AsyncWaitHandle, es de tipo WaitHandle, una clase definida en el
espacio de nombres System.Threading. La clase WaitHandle encapsula un manejador
y ofrece una forma de esperar para que ese manejador llegue a estar marcado. Para
ello la clase tiene varios mtodos estticos como WaitOne (que es similar a
WaitForSingleObject), WaitAll (similar a WaitForMultipleObjects con waitAll true),
WaitAny, etc. Tambin hay versiones de estas funciones con temporizadores
disponibles.
El manejador en AsyncWaitHandle (WaitHandle) es marcado cuando la
operacin de recepcin se completa. As, si esperamos indefinidamente sobre ese
manejador seremos capaces de saber cundo la recepcin es completada. Esto
significa que si pasamos WaitHandle a un hilo diferente, el hilo diferente puede
esperar por el manejador y notificarnos cundo los datos ya han llegado y podemos
leerlos. Esto supone una alternativa al uso de la funcin callback. Si elegimos usar
este mecanismo de WaitHandle, el parmetro de la funcin callback en la llamada a
BeginReceive ser null, como se muestra en el siguiente ejemplo:
//m_asynResult is declared of type IAsyncResult and assumming that m_socClient
has made a connection.
m_asynResult =
m_socClient.BeginReceive(m_DataBuffer,0,m_DataBuffer.Length,SocketFlags.None,nu
ll,null);
if ( m_asynResult.AsyncWaitHandle.WaitOne () )
{
int iRx = 0 ;
iRx = m_socClient.EndReceive (m_asynResult);
char[] chars = new char[iRx + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(m_DataBuffer, 0, iRx, chars, 0);
System.String szData = new System.String(chars);
txtDataRx.Text = txtDataRx.Text + szData;
}
Cdigo 7.6: mtodo alternativo para una recepcin asncrona

En el lado del servidor, la aplicacin tiene que enviar y recibir datos. Pero
adems, el servidor tiene que permitir a los clientes hacer conexiones. El servidor no
necesita conocer la direccin IP del cliente. Realmente no le importa dnde est el
cliente porque no es l, sino el cliente, el responsable de hacer la conexin. La
responsabilidad del servidor es gestionar las conexiones del cliente.
En el lado del servidor se tiene un socket llamado oyente que escucha un
nmero de puerto especfico para conexiones de cliente. Cuando el cliente hace una
conexin, el servidor necesita aceptarla y entonces, se envan y reciben datos a travs
del socket que han conseguido al aceptar la conexin. El siguiente cdigo ilustra
cmo el servidor escucha las conexiones y las acepta:
public Socket m_socListener;
public void StartListening()
{
m_socListener = new
Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
IPEndPoint ipLocal = new IPEndPoint ( IPAddress.Any ,8221);
m_socListener.Bind( ipLocal );
m_socListener.Listen (4);
m_socListener.BeginAccept(new AsyncCallback ( OnClientConnect ),null);
cmdListen.Enabled = false;
}
public void OnClientConnect(IAsyncResult asyn)
{
m_socWorker = m_socListener.EndAccept (asyn);
WaitForData(m_socWorker);
}
Cdigo 7.7: implementacin de socket en servidor

En realidad, el cdigo es similar al del cliente asncrono. Primero de todo


necesitamos crear el socket oyente y asociarlo a una direccin IP. Como en principio
no conocemos cual va a ser esa direccin, usamos Any, para luego asociarle la del
cliente mediante el mtodo Bind. En cambio, hemos pasado un nmero de puerto
concreto, que es el puerto por el que este socket escucha. Despus hemos llamado a la
funcin Listen. El cuatro indica la mxima longitud de la cola de conexiones
pendientes.
Luego hacemos una llamada a BeginAccept pasndole un delegado callback.
BeginAccept es un mtodo sin bloqueo que, cuando un cliente ha hecho una peticin
de conexin, propicia una llamada a la rutina callback, donde puede aceptarse la
conexin llamando a EndAccept. EndAccept devuelve un objeto socket que
representa la conexin entrante.

You might also like