Utilizando Sockets en Vb .Net
-
Upload
jhonedward -
Category
Documents
-
view
215 -
download
11
Transcript of Utilizando Sockets en Vb .Net
UTILIZANDO SOCKETS EN VB .NET[Enviar y recibir mensajes]
Fecha: 12/Jul/04 (09/07/04)Autor: Tilli, Pablo D. [email protected]
Introducción Hoy quiero aportar un granito de arena escribiendo mi primer tutorial, para que el intercambio de información entre los desarrolladores siga creciendo día a día. Creo que es necesaria esta divulgación de conocimientos, ya que como es mi caso (y supongo el de muchas personas mas) fue uno de mis principales medios de aprendizaje. Si bien concurro a cursos y a la universidad, este camino tiene otras características que hacen mucho más llevadera la etapa de aprender, permitiendo manejar mejor los tiempos de cada uno. Bueno, ahora si es hora de explicar un poco de que vamos a hablar en este tutorial. La idea es poder implementar dos clases: Cliente y Servidor. Probablemente el nombre de las clases digan todo, pero igual voy explicar un poco mas de que se trata. Un objeto de la clase Cliente, podrá conectarse a un objeto de la clase Servidor, permitiendo el envió de mensajes en los dos sentidos (Cliente a Servidor y Servidor a Cliente). Esto puede ser útil en una gran cantidad de aplicaciones, donde se necesita una comunicación entre dos o más computadoras conectadas en red. Un poco de teoría Se que esto de la teoría muchas veces resulta aburrido, pero la verdad es importante tener una base teórica antes de ponerse a trabajar en el código: “Uno no puede codificar lo que no sabe”. Como dije al principio, el objetivo es lograr una conexión entre varias computadoras. Para esto necesitamos algún medio por el cual se pueda mantener una “conversación” entre las mismas. Aquí entran en juego los famosos “Sockets”, pero ¿Qué es un socket? ¿Qué es un socket? Seguramente habrás escuchado nombrar la palabra Socket muchas veces, y posiblemente te preguntaste que era, así que intentare sacar tu duda de la manera mas simple: un Socket es una relación entre un puerto de un equipo y el puerto de otro equipo. Ahora nombre la palabra puerto, ¿pero de que se trata esto de los puertos? ¿Qué es un puerto? Los puertos son básicamente, una entrada/salida de información. Estos se encuentran identificados por un número entero y muchos se encuentran reservados para determinadas tareas, por ejemplo: el puerto 80 es para un servidor web. Conclusión Ahora bien, para que nos sirve saber que es un Socket y un Puerto (Al menos tener una idea de que son). Si miramos con atención nos daremos cuenta que es justo lo que necesitamos para nuestro propósito.
Tendremos un objeto de la clase Servidor, que creara un Socket que se quedara “escuchando” en un puerto que elijamos nosotros. El servidor seria un extremo del Socket, pero para que un Socket este realmente completo necesitamos otro extremo, que obviamente serán objetos de la clase Cliente, que se conectaran al puerto del equipo donde este el objeto servidor. Si todavía no queda del todo claro, sólo seguí leyendo y todo se ira aclarando de a poco, cuando veamos algo de código. Manos a la obra Antes de mostrar el código, les voy a describir las propiedades, métodos y eventos de las clases a implementar. El Servidor
Propiedades PuertoDeEscucha() As String Establece/devuelve el puerto donde se quiere que el servidor quede “escuchando”.
Métodos Escuchar()
Inicia el proceso de escuchar peticiones de conexión de parte de los clientes, en el puerto establecido en la propiedad PuertoDeEscucha. ObtenerDatos(ByVal IDCliente As Net.IPEndPoint) As String Obtiene los últimos datos enviados por el cliente especificado. Cerrar(ByVal IDCliente As Net.IPEndPoint) Cierra la conexión con el cliente especificado. Cerrar() Cierra todas las conexiones abiertas con los clientes. EnviarDatos(ByVal IDCliente As Net.IPEndPoint, ByVal Datos As String) Envía un mensaje al cliente especificado. EnviarDatos(ByVal Datos As String) Envía un mensaje a todas los clientes.
Eventos NuevaConexion(ByVal IDTerminal As Net.IPEndPoint) Se produce cuando un Cliente se conecta al Servidor, y nos devuelve un ID, para que podamos identificarlo mas tarde. DatosRecibidos(ByVal IDTerminal As Net.IPEndPoint) Se produce cuando un Cliente nos envía un mensaje. Para obtener los datos recibidos, usaremos el método ObtenerDatos pasándole como parámetro el ID del cliente.
ConexionTerminada(ByVal IDTerminal As Net.IPEndPoint) Nos avisa que se ha cerrado la conexión con el Cliente recibido como parámetro. El Cliente
Propiedades IPDelHost() As String Establece/devuelve la dirección IP (o el nombre DNS) del equipo donde se encuentra el objeto de la clase Servidor. PuertoDelHost() As String Establece/devuelve el numero de puerto en el que estará escuchando el objeto de la clase Servidor al cual nos queremos conectar.
Métodos Conectar() Permite conectarse al objeto Servidor que se encuentra escuchando en la dirección especificada por la propiedad IPDelHost en el puerto establecido en la propiedad PuertoDelHost. EnviarDatos(ByVal Datos As String) Envía un mensaje al objeto de la clase Servidor a la que se este conectado.
Eventos ConexionTerminada() Se produce cuando se termina la conexión con el objeto Servidor. DatosRecibidos(ByVal Datos As String) Nos avisa que el servidor nos ha enviado un mensaje, y el mismo se encuentra en el parámetro “Datos”. Al fin, ahora si llego la hora de ver código… empecemos.
Código de la clase Servidor Imports SystemImports System.ThreadingImports System.Net.SocketsImports System.IOImports System.Text Public Class WinSockServer #Region "ESTRUCTURAS" Private Structure InfoDeUnCliente 'Esta estructura permite guardar la información sobre un cliente Public Socket As Socket 'Socket utilizado para mantener la conexion con el cliente
Public Thread As Thread 'Thread utilizado para escuchar al cliente Public UltimosDatosRecibidos As String 'Ultimos datos enviados por el cliente End Structure#End Region #Region "VARIABLES" Private tcpLsn As TcpListener Private Clientes As New Hashtable() 'Aqui se guarda la informacion de todos los clientes conectados Private tcpThd As Thread Private IDClienteActual As Net.IPEndPoint 'Ultimo cliente conectado Private m_PuertoDeEscucha As String#End Region #Region "EVENTOS" Public Event NuevaConexion(ByVal IDTerminal As Net.IPEndPoint) Public Event DatosRecibidos(ByVal IDTerminal As Net.IPEndPoint) Public Event ConexionTerminada(ByVal IDTerminal As Net.IPEndPoint)#End Region #Region "PROPIEDADES" Property PuertoDeEscucha() As String Get PuertoDeEscucha = m_PuertoDeEscucha End Get Set(ByVal Value As String) m_PuertoDeEscucha = Value End Set End Property#End Region #Region "METODOS" Public Sub Escuchar() tcpLsn = New TcpListener(PuertoDeEscucha) 'Inicio la escucha tcpLsn.Start() 'Creo un thread para que se quede escuchando la llegada de un cliente tcpThd = New Thread(AddressOf EsperarCliente) tcpThd.Start() End Sub Public Function ObtenerDatos(ByVal IDCliente As Net.IPEndPoint) As String Dim InfoClienteSolicitado As InfoDeUnCliente 'Obtengo la informacion del cliente solicitado InfoClienteSolicitado = Clientes(IDCliente) ObtenerDatos = InfoClienteSolicitado.UltimosDatosRecibidos End Function Public Sub Cerrar(ByVal IDCliente As Net.IPEndPoint) Dim InfoClienteActual As InfoDeUnCliente 'Obtengo la informacion del cliente solicitado InfoClienteActual = Clientes(IDCliente) 'Cierro la conexion con el cliente InfoClienteActual.Socket.Close() End Sub
Public Sub Cerrar() Dim InfoClienteActual As InfoDeUnCliente 'Recorro todos los clientes y voy cerrando las conexiones For Each InfoClienteActual In Clientes.Values Call Cerrar(InfoClienteActual.Socket.RemoteEndPoint) Next End Sub Public Sub EnviarDatos(ByVal IDCliente As Net.IPEndPoint, ByVal Datos As String) Dim Cliente As InfoDeUnCliente 'Obtengo la informacion del cliente al que se le quiere enviar el mensaje Cliente = Clientes(IDCliente) 'Le envio el mensaje Cliente.Socket.Send(Encoding.ASCII.GetBytes(Datos)) End Sub Public Sub EnviarDatos(ByVal Datos As String) Dim Cliente As InfoDeUnCliente 'Recorro todos los clientes conectados, y les envio el mensaje recibido 'en el parametro Datos For Each Cliente In Clientes.Values EnviarDatos(Cliente.Socket.RemoteEndPoint, Datos) Next End Sub #End Region #Region "FUNCIONES PRIVADAS" Private Sub EsperarCliente() Dim InfoClienteActual As InfoDeUnCliente With InfoClienteActual While True 'Cuando se recibe la conexion, guardo la informacion del cliente 'Guardo el Socket que utilizo para mantener la conexion con el cliente .Socket = tcpLsn.AcceptSocket() 'Se queda esperando la conexion de un cliente 'Guardo el el RemoteEndPoint, que utilizo para identificar al cliente IDClienteActual = .Socket.RemoteEndPoint 'Creo un Thread para que se encargue de escuchar los mensaje del cliente .Thread = New Thread(AddressOf LeerSocket) 'Agrego la informacion del cliente al HashArray Clientes, donde esta la 'informacion de todos estos SyncLock Me Clientes.Add(IDClienteActual, InfoClienteActual) End SyncLock 'Genero el evento Nueva conexion RaiseEvent NuevaConexion(IDClienteActual) 'Inicio el thread encargado de escuchar los mensajes del cliente .Thread.Start() End While
End With End Sub Private Sub LeerSocket() Dim IDReal As Net.IPEndPoint 'ID del cliente que se va a escuchar Dim Recibir() As Byte 'Array utilizado para recibir los datos que llegan Dim InfoClienteActual As InfoDeUnCliente 'Informacion del cliente que se va escuchar Dim Ret As Integer = 0 IDReal = IDClienteActual InfoClienteActual = Clientes(IDReal) With InfoClienteActual While True If .Socket.Connected Then Recibir = New Byte(100) {} Try 'Me quedo esperando a que llegue un mensaje desde el cliente Ret = .Socket.Receive(Recibir, Recibir.Length, SocketFlags.None) If Ret > 0 Then 'Guardo el mensaje recibido .UltimosDatosRecibidos = Encoding.ASCII.GetString(Recibir) Clientes(IDReal) = InfoClienteActual 'Genero el evento de la recepcion del mensaje RaiseEvent DatosRecibidos(IDReal) Else 'Genero el evento de la finalizacion de la conexion RaiseEvent ConexionTerminada(IDReal) Exit While End If Catch e As Exception If Not .Socket.Connected Then 'Genero el evento de la finalizacion de la conexion RaiseEvent ConexionTerminada(IDReal) Exit While End If End Try End If End While Call CerrarThread(IDReal) End With End Sub Private Sub CerrarThread(ByVal IDCliente As Net.IPEndPoint) Dim InfoClienteActual As InfoDeUnCliente 'Cierro el thread que se encargaba de escuchar al cliente especificado InfoClienteActual = Clientes(IDCliente) Try InfoClienteActual.Thread.Abort() Catch e As Exception SyncLock Me
'Elimino el cliente del HashArray que guarda la informacion de los clientes Clientes.Remove(IDCliente) End SyncLock End Try End Sub #End Region End Class
Código de la clase Cliente Imports SystemImports System.NetImports System.Net.SocketsImports System.ThreadingImports System.TextImports System.IO Public Class Cliente #Region "VARIABLES" Private Stm As Stream 'Utilizado para enviar datos al Servidor y recibir datos del mismo Private m_IPDelHost As String 'Direccion del objeto de la clase Servidor Private m_PuertoDelHost As String 'Puerto donde escucha el objeto de la clase Servidor#End Region #Region "EVENTOS" Public Event ConexionTerminada() Public Event DatosRecibidos(ByVal datos As String)#End Region #Region "PROPIEDADES" Public Property IPDelHost() As String Get IPDelHost = m_IPDelHost End Get Set(ByVal Value As String) m_IPDelHost = Value End Set End Property Public Property PuertoDelHost() As String Get PuertoDelHost = m_PuertoDelHost End Get Set(ByVal Value As String) m_PuertoDelHost = Value End Set End Property#End Region #Region "METODOS" Public Sub Conectar() Dim tcpClnt As TcpClient Dim tcpThd As Thread 'Se encarga de escuchar mensajes enviados por el Servidor
tcpClnt = New TcpClient() 'Me conecto al objeto de la clase Servidor, ' determinado por las propiedades IPDelHost y PuertoDelHost tcpClnt.Connect(IPDelHost, PuertoDelHost) Stm = tcpClnt.GetStream() 'Creo e inicio un thread para que escuche los mensajes enviados por el Servidor tcpThd = New Thread(AddressOf LeerSocket) tcpThd.Start() End Sub Public Sub EnviarDatos(ByVal Datos As String) Dim BufferDeEscritura() As Byte BufferDeEscritura = Encoding.ASCII.GetBytes(Datos) If Not (Stm Is Nothing) Then 'Envio los datos al Servidor Stm.Write(BufferDeEscritura, 0, BufferDeEscritura.Length) End If End Sub #End Region #Region "FUNCIONES PRIVADAS" Private Sub LeerSocket() Dim BufferDeLectura() As Byte While True Try BufferDeLectura = New Byte(100) {} 'Me quedo esperando a que llegue algun mensaje Stm.Read(BufferDeLectura, 0, BufferDeLectura.Length) 'Genero el evento DatosRecibidos, ya que se han recibido datos desde el Servidor RaiseEvent DatosRecibidos(Encoding.ASCII.GetString(BufferDeLectura)) Catch e As Exception Exit While End Try End While 'Finalizo la conexion, por lo tanto genero el evento correspondiente RaiseEvent ConexionTerminada() End Sub#End Region End Class
¿Cómo utilizar las clases? Bueno, hemos terminado con lo que se refiere al código de las clases Cliente y Servidor, pero antes de terminar con el tutorial vamos a ver un pequeño ejemplo de como utilizarlas. En mi caso tengo la clase Cliente por un lado (Cliente.dll) y el Servidor por el otro (Servidor.dll). Lo que vamos ha hacer en este ejemplo es crear dos pequeñas aplicaciones donde una se quede escuchando (El Servidor), y otra que se conecta a la que escucha (Cliente). Ambas podrán enviar y recibir mensajes.
Aplicación Servidor
Nota: Se debe establecer una referencia a Servidor.dll. Public Class Form1 Inherits System.Windows.Forms.Form " Código generado por el Diseñador de Windows Forms " Dim WithEvents WinSockServer As New Servidor() Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load With WinSockServer 'Establezco el puerto donde escuchar .PuertoDeEscucha = 8050 'Comienzo la escucha .Escuchar() End With End Sub Private Sub WinSockServer_NuevaConexion(ByVal IDTerminal As System.Net.IPEndPoint) Handles WinSockServer.NuevaConexion 'Muestro quien se conecto MsgBox("Se ha conectado un nuevo cliente desde la IP= " & IDTerminal.Address.ToString & _ ",Puerto = " & IDTerminal.Port) End Sub Private Sub WinSockServer_ConexionTerminada(ByVal IDTerminal As System.Net.IPEndPoint) Handles WinSockServer.ConexionTerminada 'Muestro con quien se termino la conexion MsgBox("Se ha desconectado el cliente desde la IP= " & IDTerminal.Address.ToString & _ ",Puerto = " & IDTerminal.Port) End Sub Private Sub WinSockServer_DatosRecibidos(ByVal IDTerminal As System.Net.IPEndPoint) Handles WinSockServer.DatosRecibidos 'Muestro quien envio el mensaje MsgBox("Nuevo mensaje desde el cliente de la IP= " & IDTerminal.Address.ToString & _ ",Puerto = " & IDTerminal.Port ) 'Muestro el mensaje recibido Call MsgBox(WinSockServer.ObtenerDatos(IDTerminal)) End Sub Private Sub btnEnviarMensaje_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEnviarMensaje.Click
'Envio el texto escrito en el textbox txtMensaje a todos los clientes WinSockServer.EnviarDatos(txtMensaje.Text) End SubEnd Class
Aplicación Cliente
Nota: Se debe establecer una referencia a Cliente.dll. Public Class Form1 Inherits System.Windows.Forms.Form " Código generado por el Diseñador de Windows Forms " Dim WithEvents WinSockCliente As New Cliente Private Sub btnConectar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConectar.Click With WinSockCliente 'Determino a donde se quiere conectar el usuario .IPDelHost = txtIP.Text .PuertoDelHost = txtPuerto.Text 'Me conecto .Conectar() 'Deshabilito la posibilidad de conexion txtIP.Enabled = False txtPuerto.Enabled = False btnConectar.Enabled = False 'Habilito la posibilidad de enviar mensajes btnEnviarMensaje.Enabled = True txtMensaje.Enabled = True End With End Sub Private Sub WinSockCliente_DatosRecibidos(ByVal datos As String) Handles WinSockCliente.DatosRecibidos MsgBox("El servidor envio el siguiente mensaje: " & datos) End Sub Private Sub WinSockCliente_ConexionTerminada() Handles WinSockCliente.ConexionTerminada MsgBox("Finalizo la conexion") 'Habilito la posibilidad de una reconexion txtIP.Enabled = True txtPuerto.Enabled = True
btnConectar.Enabled = True 'Deshabilito la posibilidad de enviar mensajes btnEnviarMensaje.Enabled = False txtMensaje.Enabled = False End Sub Private Sub btnEnviarMensaje_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEnviarMensaje.Click 'Envio lo que esta escrito en la caja de texto del mensaje WinSockCliente.EnviarDatos(txtMensaje.Text) End Sub Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing End End Sub End Class
Conclusión Como conclusión, creo que si bien queda bastante por hacer (Validaciones, agregar mayor funcionalidad a las clases, etc.) este tutorial puede ser considerado como una introducción a la utilización de Sockets, que cubre lo básico para lograr una comunicación entre aplicaciones que se encuentran en distintas computadoras. Quisiera que cualquier duda, sugerencia, crítica, mejora o cualquier otra cosa, me la hagan llegar mi correo electrónico ([email protected]). Espero, que este tutorial sea el primero de una larga lista, y espero también que a partir de las críticas que vaya recibiendo, pueda ir mejorando la manera de presentar el tutorial.
http://www.mundoprogramacion.net/colabora/puntonet/pablotilli_socketsvbnet.htm