using System;
using System.IO;
using System.Net.Sockets;
namespace Sockets
{
/// <summary>
/// Delegado empleado para los eventos de recepción de comandos
/// </summary>
delegate void DelegadoComandoRecibido(CanalComunicacion canal, UInt16 comando, BinaryReader datos);
/// <summary>
/// Administra una canal (Socket) de comunicación entre los dos extremos de la conexión
/// </summary>
class CanalComunicacion
{
/// <summary>
/// Constructor
/// </summary>
public CanalComunicacion(Socket socket)
{
Socket = socket;
//Establecer opciones de socket
socket
.LingerState = new LingerOption
(true,
5);
//Establecer recepción asíncrona
comenzarRecepcion();
}
/// <summary>
/// Destructor
/// </summary>
~CanalComunicacion()
{
Cerrar();
}
#region Campos
/// <summary>
/// Socket usado para el envío y recepción de datos
/// </summary>
public readonly Socket Socket;
public bool Conectado
{
get
{
return Socket.Connected;
}
}
#endregion
/// <summary>
/// Evento invocado cuando se recibe un comando por este canal
/// </summary>
public event DelegadoComandoRecibido ComandoRecibido;
/// <summary>
/// Evento invocado cuando se cierra el canal o un error corta la comunicación
/// </summary>
public event EventHandler CanalCerrado;
/// <summary>
/// Cierra la comunicación entre los dos extremos
/// </summary>
public void Cerrar()
{
if (Socket.Connected)
{
Socket.Shutdown(SocketShutdown.Both);
Socket.Close();
}
if (CanalCerrado != null)
CanalCerrado(this, EventArgs.Empty);
}
/// <summary>
/// Envía un comando al otro extremo de la conexión por el canal especificado
/// </summary>
public void EnviarComando(UInt16 comando, params object[] datos)
{
MemoryStream buffer
= new MemoryStream
(2 + datos
.Length*50); BinaryWriter escritor
= new BinaryWriter
(buffer
); UInt32 tamañoSecuencia = 0;
//Este valor es el primer elemento que se envía, pero se calcula al finalizar de generar el buffer
escritor.Write(tamañoSecuencia);
escritor.Write(comando);
foreach (object parametro in datos)
{
{
escritor.Write(parametro.ToString());
}
{
byte[] bytes = (byte[]) parametro;
escritor.Write((UInt32) bytes.Length);
escritor.Write(bytes);
}
else if (parametro
is UInt16
) {
escritor.Write((UInt16) parametro);
}
else if (parametro
is UInt32
) {
escritor.Write((UInt32) parametro);
}
else if (parametro
is UInt64
) {
escritor.Write((UInt64) parametro);
}
else if (parametro
is int) {
escritor.Write((int) parametro);
}
}
//Calcular el tamaño de la secuencia y escribirlo en el buffer
tamañoSecuencia = (UInt32) buffer.Length - 4; //No contar el tamaño propio del numero (4 bytes)
buffer.Position = 0;
escritor.Write(tamañoSecuencia);
Socket
.BeginSend(buffer
.GetBuffer(),
0,
(int) buffer
.Length,
0,
new AsyncCallback
(finEnvio
), buffer
); }
/// <summary>
/// Finaliza un envío asíncrono y libera los recursos
/// </summary>
private void finEnvio(IAsyncResult ar)
{
if (Socket.Connected == false)
{
Cerrar();
}
else
{
Socket.EndSend(ar);
MemoryStream buffer = ar.AsyncState as MemoryStream;
if (buffer != null)
buffer.Close();
}
}
/// <summary>
/// Comienza la recepción asíncrona de los datos enviados por el otro extremo
/// </summary>
private void comenzarRecepcion()
{
if (Socket.Connected == false)
{
Cerrar();
}
else
{
Buffer buffer
= new Buffer
{ BytesBuffer
= new byte[4], TamañoSecuencia
= -1 }; Socket
.BeginReceive(buffer
.BytesBuffer,
0, buffer
.BytesBuffer.Length,
0,
new AsyncCallback
(finRecepcion
),
buffer);
}
}
class Buffer
{
public Int32 TamañoSecuencia;
public byte[] BytesBuffer;
}
/// <summary>
/// Termina de recibir los datos enviados por el otro extremo de la conexión,
/// y los procesa
/// </summary>
private void finRecepcion(IAsyncResult ar)
{
if (Socket.Connected == false)
{
Cerrar();
return;
}
int recibidos = Socket.EndReceive(ar);
if (recibidos > 0)
{
Buffer buffer = (Buffer)ar.AsyncState;
if (buffer.TamañoSecuencia < 0)//Recibido tamaño de la próxima secuencia
{
buffer.TamañoSecuencia = BitConverter.ToInt32(buffer.BytesBuffer, 0);
//Recibir el resto de la secuencia
buffer
.BytesBuffer = new byte[buffer
.TamañoSecuencia
]; Socket.BeginReceive(buffer.BytesBuffer, 0, buffer.TamañoSecuencia, 0,
new AsyncCallback
(finRecepcion
), buffer
); }
else//Datos recibidos
{
//Comenzar a recibir siguiente comando
comenzarRecepcion();
//Invocar eventos
MemoryStream bufferStream
= new MemoryStream
(buffer
.BytesBuffer); BinaryReader lector
= new BinaryReader
(bufferStream
);
UInt16 comando = lector.ReadUInt16();
if (ComandoRecibido != null)
ComandoRecibido(this, comando, lector);
lector.Close();
}
}
else
{
Cerrar();
}
}
}
}