Etiquetas de Technorati:
Winsock,
C# Hola, ya tenia mucho tiempo sin publicar nada pero la mezcla del trabajo y la escuela esta algo complicada.
Bueno hoy le traigo un ejemplo de cómo utilizar los WinSock en c# con Visual Studio en las versiones 2005 y 2008.
Este tema que me parece muy interesante aunque es algo avanzado aun cuando no son muchas líneas código, pero tratare de explicar antes la tecnología aplicada a este ejemplo.
Para este ejemplo usamos el Winsock para establecer una conexión entre el cliente y el servidor. Esto es lo que se usa en todas las computadores hoy en día. La red es una herramienta esencial y muy importante para los programadores y se debe aprender a utilizarla.
Es este articulo veremos un sencillo programa de chat, el cual consistirá en una aplicación que será el servidor y otra el cliente que se puede conectar directamente desde Internet o desde una LAN para simplemente intercambiar mensajes de texto.
Control Winsock en C#
El manejo de sockets es complejo realizando todo el trabajo desde cero, porque se tienen que controlar diversos factores como crear encabezados, tamaños de paquetes, tablas de hash, manejo de hilos y muchas mas cosas pero con el control Winsock nos evitaremos todo ese problema, este control es un componente COM y lo podemos añadir a nuestro programa en c# (o cualquier otro lenguaje por ser COM) de una manera muy fácil esto se vera en la construcción del ejemplo.
Que se necesita para este ejemplo???
Se necesita una computadora con Windows, y tener instalado Visual Studio en cualquiera de sus versiones de 2005 o 2008.
Ejemplo:
Bueno aquí empieza lo chido, el código.
Cliente
Lo primero que aremos será construir la aplicación cliente pare ello abriremos una instancia de Visual Studio y crearemos un nuevo proyecto del tipo WindowsApplication y la diseñaremos mas o menos así.
Después el la caja e herramientas demos clic con el botón derecho y seleccionamos choose Items como se muestra en la imagen.

Se nos desplegara una ventana para elegir el control que ocuparemos para el ejemplo que es el Microsoft WinSock Control, versión 6.0

Lo seleccionamos y damos OK y este control nos aparecerá en caja de herramientas del Visual Studio como se muestra en la imagen.

Luego arrastramos el control a la forma y así lo tendríamos agregado en nuestra aplicación.
Esta es la imagen del control en la forma, se ve así ya que es un control que no es visible en la ejecución pero como es COM no se pone en la parte de abajo como los otros controles no visibles de visual Studio.

Una vez diseñada la forma se le introduce el siguiente código.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WinSockClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// se le agregan los eventos necesarios al
//control axWinsock1
this.axWinsock1.Error += new AxMSWinsockLib.
DMSWinsockControlEvents_ErrorEventHandler
(this.axWinsock1_Error);
this.axWinsock1.ConnectEvent +=
new System.EventHandler
(this.axWinsock1_ConnectEvent);
this.axWinsock1.DataArrival +=
new AxMSWinsockLib.
DMSWinsockControlEvents_DataArrivalEventHandler
(this.axWinsock1_DataArrival);
}
//variable para tener un control del estado de
//conexion entre el cliente y el servidor
bool isConnected = false;
private void btnConectar_Click(object sender,EventArgs e)
{
//si esta conectado se cierra y si no se abre
//la conexion
if(isConnected == false)
{
try
{
//se cierra el winsock si esta abierto.
axWinsock1.Close();
//se llama al método connect del winsock
//que pide una IP y un puerto a cual conectarse
axWinsock1.Connect(IPTxt.Text, PuertoText.Text);
isConnected = true;
labelEstado.Text = "Conectado por
el puerto: " + PuertoText.Text;
labelEstado.ForeColor = Color.Green;
btnConectar.Text = "Desconectar";
}
// con un bloque try-catch se cacha
//cualquier error y lo muestra en el log
catch (System.Windows.Forms.AxHost.InvalidActiveXStateException g)
{
ListBoxLog.Items.Add(g.ToString());
}
catch (Exception ex)
{
ListBoxLog.Items.Add(ex.Message);
}
}
else
{
try
{
//Esto cierra la conexion con el cliente
axWinsock1.Close();
this.ListBoxLog.Items.Add("Desconectado...");
btnConectar.Text = "Conectar";
labelEstado.ForeColor = Color.Red;
labelEstado.Text = "Desconectado";
isConnected = false;
}
catch
{
MessageBox.Show("Ocurrió un error,
el puerto no esta abierto",
"ERROR",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
}
private void btnEnviar_Click(object sender,EventArgs e)
{
try
{
//si esta conectado puede enviar información
if(isConnected)
{
//esto se envía la información que tenga
//textbox txtMsg hacia el servidor
axWinsock1.SendData(txtMsg.Text);
//con esto visualizamos en pantalla
//el mensaje que se acaba de enviar
ListBoxLog.Items.Add("Cliente- "
+ axWinsock1.LocalIP
+ ": " + txtMsg.Text);
//borramos el contenido del texbox para
//dejarlo listo para mandar un nuevo mensaje
txtMsg.Text = "";
}
else
MessageBox.Show("No se esta conectado con el servidor");
}
// con un bloque try-catch se cacha
//cualquier error y lo muestra en el log
catch (AxMSWinsockLib.AxWinsock.InvalidActiveXStateException g)
{
ListBoxLog.Items.Add(g.ToString());
}
catch (Exception ex)
{
ListBoxLog.Items.Add(ex.Message);
}
}
//en caso de algún error puramente del winsock
//se activa este evento y se escribe en el log
//la información del error
void axWinsock1_Error(object sender,
AxMSWinsockLib.DMSWinsockControlEvents_ErrorEvent e)
{
ListBoxLog.Items.Add("Error : " + e.description);
isConnected = false;
}
// Este evento se activa cuando el servidor envía
//y el winsock del cliente atrapa esa información
private void axWinsock1_DataArrival(object sender,
AxMSWinsockLib.DMSWinsockControlEvents_DataArrivalEvent e)
{
//creamos una variable para obtener
//los datos que llegaron
string data = "";
//creamos otra variable tipo object
//porque el método que se utiliza para
//recibir datos es object
object dat = (object)data;
//obtenemos la información que envió
//el servidor en forma de object
axWinsock1.GetData(ref dat);
//hacemos un cast a esa variable
//y la guardamos en la variable data
data = (string)dat;
//mostramos la información en log
ListBoxLog.Items.Add("Servidor - "
+ axWinsock1.RemoteHostIP
+ ": " + data);
}
//Esto pasa cuando se conecta a un servidor,
//muestra la información de conexion en el log
private void axWinsock1_ConnectEvent(object sender, EventArgs e)
{
ListBoxLog.Items.Add(
"Conectado con el servidor en: "
+ axWinsock1.RemoteHostIP);
isConnected = true;
}
}
}
Servidor
Una ves construido el cliente pasamos a construir la aplicación servidor que es muy similar al cliente, para esto abrimos otra instancia de Visual Studio y creamos un proyecto del tipo WindowsApplication y lo diseñamos mas o menos así.

De la misma forma como e hizo en el cliente, agregamos el componente Microsoft WinSock Control, versión 6.0.
Después agregamos el código del servidor.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WinSockServer
{
public partial class ServerForm1 : Form
{
public ServerForm1()
{
InitializeComponent();
}
//variable para el tener un control
//sobre el estado de la conexion
bool isConnected = false;
private void btnEnviar_Click(object sender,EventArgs e)
{
try
{
if(isConnected)
{
//con esto se envía el texto que este el en txtMsg
xWinsock1.SendData(txtMsg.Text);
//Se muestra en pantalla la información que se envió
ListBoxLog.Items.Add("Servidor: "+ txtMsg.Text);
//se borra la info del txtmsg
txtMsg.Text = "";
}
else
MessageBox.Show("No estas conectado");
}
// con un bloque try-catch se cacha cualquier error
//que ocurra, y se muestra en el log
catch(AxMSWinsockLib.AxWinsock.InvalidActiveXStateException g)
{
ListBoxLog.Items.Add(g.ToString());
}
catch(Exception ex)
{
ListBoxLog.Items.Add(ex.Message);
}
}
private void btnEscuchar_Click(object sender,EventArgs e)
{
try
{
int x = 0;
if(portText.Text != "" &&
int.TryParse(portText.Text, out x))
{
//Para crear una conexion se necesita
//que el servidor(host) siempre este
//escuchando por un puerto especifico,
//y si un cliente se quiere conectar
//a el debe ir a abrir ese puerto
//Con esto se le asigna el puerto al winsock
axWinsock1.LocalPort = Int32.Parse(portText.Text);
isConnected = true;
//este comando abre la conexion
//con el puerto y se mantiene escuchando
//por el puerto especificado
axWinsock1.Listen();
labelEstado.ForeColor = Color.Green;
labelEstado.Text = "Escuchando por el puerto:";
labelPuerto.Text = portText.Text;
labelPuerto.ForeColor = Color.Green;
pictureBox1.Show();
pictureBox2.Hide();
ListBoxLog.Items.Add("Escuchando por el puerto: " + portText.Text);
buttonDesconectar.Enabled = true;
buttonEscuchar.Enabled = false;
buttonEnviar.Enabled = true;
}
else
{
MessageBox.Show("Debe asignar un puerto para escuchar","ERROR",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
}
catch
{
MessageBox.Show("Ocurrió un error, lo mas probable es que el puerto seleccionado no este disponible");
}
}
private void Desconectar_Click(object sender, EventArgs e)
{
try
{
//cierra la conexión con el cliente
axWinsock1.Close();
this.ListBoxLog.Items.Add("Desconectado.........");
buttonEscuchar.Enabled = true;
buttonDesconectar.Enabled = false;
buttonEnviar.Enabled = false;
labelEstado.Text = "Sin Escuchar ningún puerto";
labelPuerto.Text = "";
labelEstado.ForeColor = Color.Red;
pictureBox2.Show();
pictureBox1.Hide();
}
catch
{
MessageBox.Show("Ocurrió un error, el puerto no esta abierto", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
//cuando se hace una petición de
//conexión de parte de un cliente
private void axWinsock1_ConnectionRequest(object sender, AxMSWinsockLib.DMSWinsockControlEvents_ConnectionRequestEvent e)
{
if (isConnected == true)
{
axWinsock1.Close();
}
//este comando acepta la conexión con el cliente
axWinsock1.Accept(e.requestID);
isConnected = true;
//escribe en el log si el cliente esta conectado.
ListBoxLog.Items.Add("Cliente Conectado: " + axWinsock1.RemoteHostIP);
}
private void axWinsock1_DataArrival(object sender, AxMSWinsockLib.DMSWinsockControlEvents_DataArrivalEvent e)
{
//creamos una variable para obtener los
//datos que llegaron
string data = "";
//creamos otra variable tipo object porque
//el método que se utiliza para recibir
//datos es object
object dat = (object)data;
//obtenemos la información que envió el
//servidor en forma de object
axWinsock1.GetData(ref dat);
//hacemos un cast a esa variable y la
//guardamos en la variable data
data = (string)dat;
//mostraos la información en log
ListBoxLog.Items.Add(axWinsock1.RemoteHostIP + ": " + data);
}
private void Form1_Load(object sender, EventArgs e)
{
//se muestra el nombre del equipo
lblHost.Text = axWinsock1.LocalHostName;
//se muestra la IP del equipo
lblIPlocal.Text = axWinsock1.LocalIP;
}
}
}
Por ultimo en la imagen se ve a las dos aplicaciones interactuando, para que esto funcione se debe primero abrir la aplicación servidor y ponerla a escuchar por algún puerto libre y después abrir la app cliente y ponerle la IP de la app servidor así como el puerto por la cual esta escuchando, con esto se dará la conexión y todo funcionara bien, este programa lo hice lo probé de forma local pero también funciona sin ningún problema por una LAN o por Internet.
Bueno, espero le haya servido a alguien. Si alguien ve algún error o tienen una duda por favor coméntela.
Para descargar las dos aplicaciones con todo el código fuente aquí les dejo el link de mi SkyDrive Winsocks Cliente-Servidor