Aplicaciones Cliente/Servidor mediante el Control Winsock Al igual que un usuario se comunica con el programa por medio del teclado, dos programas se pueden comunicar entre sí por medio de un control especial llamado WinSock Control. Su nombre proviene de Windows Sockets, un término utilizado para hacer referencias a las API de Windows que permiten programar bajo una red. Mediante el uso de sockets es posible comunicarse con un programa que se este ejecutando en una PC remota, ya sea a través de internet o de una LAN. El Winsock Control no se encuentra disponible en la caja de herramientas de Visual Basic, para acceder a él debemos agregarlo manualmente desde el menú Proyecto --> Componentes tildando sobre Microsoft Winsock Control 5.0
Como resultado obtenemos un nuevo control en la caja de herramientas:
Para enviar un mensaje de una computadora a otra debemos desarrollar dos programas diferentes. Uno denominado Servidor y otro denominado Cliente. El Servidor siempre estará esperando recibir una solicitud de conexión de otro programa, el Cliente.
Aplicaciones Cliente/Servidor
Las aplicaciones que trabajan en una red (ya sea en una LAN o en Internet) se basan en la arquitectura Cliente/Servidor. Esta arquitectura de basa en una aplicación principal, que ofrece un servicio (Servidor) y se mantiene a la espera de que una aplicación Cliente se conecte solicitando una determinado información. En este tipo de arquitectura es común tener un único programa Servidor y cientos de aplicaciones Clientes que realizan peticiones para establecer una conexión.
Servidor: Es toda aplicación que se mantiene a la espera de que un Cliente solicite información, la cual será entregada si el Servidor así lo desea. Se dice que este ofrece o sirve un servicio.
Cliente: Es toda aplicación que se conecta a un Servidor para solicitarle alguna información.
Un claro ejemplo de una aplicación Cliente/Servidor es el CiberControl que a través de un programa Servidor permite controlar una gran cantidad de programas Clientes.
WinSock Control El componente Winsock del Visual Basic es el que permite realizar conexiones Cliente/Servidor a través de protocolos TCP y UDP. El Winsock puede trabajar de dos formas, como Cliente (conecta a un Servidor) o como Servidor (recibe conexiones), además, es posible realizar vectores de Winsock, lo que permite administrar varias conexiones con un mismo código común para cada elemento del vector.
Internet usa el protocolo TCP/IP que significa "Transmision Control Protocol / Internet Protocol", es el que se encarga de recibir paquetes de información y redirigirlos al usuario final que los solicitó. Este protocolo es el preferido por todos ya que posee una característica que UDP le envidia, TCP/IP puede verificar que el paquete de información haya llegado con éxito al destinatario final, concretando así la transacción. Por el contrario UDP no puede hacer esto, solo manda el paquete con la información y no verifica que haya llegado satisfactoriamente, poniendo de esta manera en peligro al paquete, ya que puede no llegar entero al destinatario.
Propiedades más importantes del Winsock:
Con algunas de estas propiedades podemos dise�ar un formulario que nos informe la dirección IP de nuestra maquina y su nombre en la red.
- Protocol: Establece o devuelve el protocolo que utilizara el Winsock. Puede tomar uno de los dos siguientes valores: sckTCPProtocol y sckUDPProtocol.
- LocalHostName: Devuelve el nombre de la máquina local.
- LocalIP: Devuelve la dirección IP de la máquina local.
- LocalPort: Establece el puerto que se quiere dejar a la escucha.
- RemoteHost: Establece el equipo remoto al que se quiere solicitar la conexión.
- RemotePort: Establece el número del puerto remoto al que se quiere conectar.
- RemoteHostIP: Devuelve la dirección IP del Host Remoto con el que se conectó.
- State: Verifica si el Control WinSock esta siendo utilizado o no.
Para ello dise�amos el siguiente formulario:
En el evento load del formulario (cuando el formulario se esta cargando en memoria) mostramos las propiedades LocalHostname y LocalIp del Winsock en las correspondientes cajas de texto:
Private Sub Form_Load()
Text1.Text = Winsock1.LocalHostName
Text2.Text = Winsock1.LocalIP
End Sub
Ahora debemos decidir si nuestro formulario será un Servidor o un Cliente, es decir si nuestro formulario estará a la espera de nuevas conexiones (Servidor), o si nuestro formulario se comunicará con un Servidor remoto (Cliente).
Programa Cliente:
La primera acción a realizar y fundamental para toda aplicación de este tipo, es crear la conexión al servidor, ya que solo se puede transmitir información si la conexión Cliente/Servidor se encuentra activa.
Propiedades necesarias:Métodos necesarios:
- RemoteHost: Asignamos la dirección a la que deseamos conectar (puede ser el nombre de la PC o su número de IP). Este valor será ingresado en Text3.
- RemotePort: Asignamos el puerto al que deseamos conectar con el Servidor. Es el número de puerto en que está �escuchando� el programa Servidor. Este valor será ingresado en Text4.
Eventos involucrados:
- Connect: Conecta al Servidor. Será codificado en el botón conectar (Command1 en este caso).
- Close: Cierra la conexión al Servidor. Será codificado en el botón desconectar (Command2 en este caso).
- Connect(): Ocurre cuando hemos establecido con éxito la conexión al Servidor
- Close(): Ocurre cuando el Servidor nos cierra la conexión.
- Error(): Ocurre en caso de errores.
Empezamos la codificación con el botón conectar, que debe ejecutar el método Connect():
Private Sub Command1_Click()
Winsock1.RemoteHost = Text3.Text
Winsock1.RemotePort = Text4.Text
Winsock1.Close
Winsock1.Connect
End Sub
En las primeras dos líneas asignamos los datos de conexión al Host remoto, como son la IP o DNS (RemoteHost) y número de puerto (RemotePort).
En la última línea llamamos al método Connect para realizar la conexión, siempre asegurándonos que el Socket no este utilizándose. Para ello llamamos al método Close que se encarga de cerrar toda conexión pendiente en el Socket.
Si la conexión se realiza con éxito se dispara el evento Connect(), en donde podemos realizar acciones inmediatas, en el momento preciso en que se logra establecer la conexión con el servidor. En este caso vamos a informar en Text5 el éxito de la conexión.
Private Sub Winsock1_Connect()
Text5.Text = Text5.Text & ">>>> Conexión establecida <<<<" & vbCrLf
Text5.SelStart = Len(Text5.Text)
End Sub
vbCrLf (avance de línea y retorno de carro) es equivalente a un Enter, lo cual permite dejar el cursor en la siguiente línea del mensaje.
Mediante Text5.SelStart = Len(Text5.Text) logramos realizar un scroll sobre text5 para observar el final de la caja de texto.
También hay que tener presente que en cualquier momento el Servidor nos puede cerrar la conexión, o bien cerrarse por algún error, para ello contamos con el evento Close(), que se dispara al perder la conexión con el Servidor, lo cual debemos informar:
Private Sub Winsock1_Close()
Winsock1.Close
Text5.Text = Text5.Text & ">>>> Conexión cerrada por el servidor <<<<" & vbCrLf
Text5.SelStart = Len(Text5.Text)
End Sub
En cambio, si queremos cerrar nosotros mismos la conexión con el Servidor basta con llamar al método Close, cuando se presione el botón desconectar (Command2).
Private Sub Command2_Click() Winsock1.Close
Text5.Text = Text5.Text & ">>>> Conexión cerrada por el usuario <<<<" & vbCrLf
Text5.SelStart = Len(Text5.Text)
End Sub
Solo resta programar nuestro Cliente para enviar y recibir datos.
Métodos necesarios:Eventos involucrados:
- SendData: Envía datos al otro extremo de la conexión (Socket remoto).
- GetData: Recibe datos enviados por el extremo remoto (Socket remoto).
- DataArrival(): Ocurre cuando el Socket remoto nos esta enviando datos.
- Error(): Ocurre en caso de errores.
Para enviar datos utilizamos el método SendData en el botón enviar (Command3).
Private Sub Command3_Click()
Dim enviar As String
enviar = Text6.Text
Winsock1.SendData enviar
Text5.Text = Text5.Text & "Cliente --> " & Text6.Text & vbCrLf
Text5.SelStart = Len(Text5.Text)
Text6.Text = ""
End Sub
Cuando el servidor nos envía datos se genera el evento DataArrival() indicando que tenemos nueva información disponible, la cual podemos leer con el método GetData:
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim datos As String
Winsock1.GetData datos
Text5.Text = Text5.Text & "Servidor --> " & datos & vbCrLf
Text5.SelStart = Len(Text5.Text)
End Sub
Por ultimo podemos utilizar el evento error para detectar cualquier problema de conexión. En caso que ocurra uno podemos cerrar la conexión e informar al usuario de dicho error:
Private Sub Winsock1_Error(ByVal Number As Integer, Description As String)
Winsock1.Close
MsgBox Number & " " & Description
End Sub
De esta manera el programa Cliente está terminado, para probarlo es necesario crear el programa Servidor. Pero mientras tanto es posible probarlo con un servidor web, por ejemplo google. Para ello iniciamos el programa y conectamos con www.google.com.ar (DNS), el puerto designado para transferencia de páginas web es el 80. De esta manera podemos verificar si nuestra aplicación es capaz de conectarse o no con una aplicación remota.
Programa Servidor:
El Servidor es una aplicación totalmente independiente de la aplicación Cliente, por lo cual habrá que abrir un nuevo proyecto en Visual Basic y dise�ar el formulario propuesto.
El primer paso será habilitar el Socket para que pueda quedar esperando una conexión, se dice que queda "a la escucha". Para esto solo necesitamos un botón "Escuchar" y un número de puerto local (a elección) en el cual deseamos recibir conexiones entrantes.
Propiedades necesarias:Métodos necesarios:
- LocalPort: Asignamos el puerto local en el cual deseamos recibir conexiones.
Eventos involucrados:
- Listen: Escucha peticiones entrantes.
- Close: Cierra la conexión al Servidor.
- ConnectionRequest(): Ocurre cuando un Cliente solicita una conexión al Servidor.
- Close(): Ocurre cuando el Cliente cierra la conexión.
- Error(): Ocurre en caso de errores.
Iniciamos entonces la codificación con el botón escuchar, que debe llamar al método Listen.
Private Sub Command1_Click()
Winsock1.Close
Winsock1.LocalPort = Text3.Text
Winsock1.Listen
Text5.Text = Text5.Text & ">>>> Esuchando conexiones <<<< " & vbCrLf
Text5.SelStart = Len(Text5.Text)
End Sub
Como se observa la primera acción es cerrar la conexión actual, para luego poder modificar el puerto y crear una nueva conexión sin que nos de errores.
La siguiente línea establece en que puerto deseamos recibir conexiones, y luego llama al Socket para que quede a la escucha de conexiones en ese puerto.
Hasta aquí el Socket sólo esta "escuchando" conexiones. Cuando un cliente intenta conectarse el Winsock dispara el evento ConnectionRequest(), en donde podemos aceptar o rechazar la conexión.
Private Sub Winsock1_ConnectionRequest(ByVal requestID As Long)
Text5.Text = Text5.Text & ">>>> Petición número: " & requestID & " Desde IP:" & Winsock1.RemoteHostIP & " <<<<"
Text5.SelStart = Len(Text5.Text)
Winsock1.Close
Winsock1.Accept requestID
Text5.Text = Text5.Text & ">>>> Conexión aceptada <<<<" & vbCrLf
Text5.SelStart = Len(Text5.Text)
End Sub
Con estas líneas ya estamos conectados completamente con el Cliente. Cuando un Cliente se intenta conectar por nuestro puerto, el Socket lo detectará y generará el evento ConnectionRequest(), que significa "Petición de conexión", además le asigna un número a esa petición. En este momento podemos identificar al cliente por su IP, que se encuentra en la propiedad RemoteHostIP.
Cuando se genera el evento lo que tenemos que hacer es "Aceptar" la conexión entrante (requestID) mediante el método Accept, si no lo hacemos al llegar al End Sub del evento, la conexión del Cliente será cerrada automáticamente.
Es importante destacar que antes de aceptar la conexión con Accept debemos cerrar la conexión con Close, esto que puede parecer ilógico no lo es, porque el socket lo teníamos ocupado y activo "escuchando conexiones", y ahora necesitamos que establezca una conexión par a par con el Cliente. Por ello es que cerramos la función de "Escuchar conexiones del Socket" y aceptamos la conexión entrante, conectando automáticamente en forma directa con el Cliente y dejando de atender a nuevas conexiones entrantes.
A continuación vamos a codificar el botón desconectar para anular la conexión con el programa Cliente:
Private Sub Command2_Click()
Winsock1.Close
Text5.Text = Text5.Text & ">>>> Conexión cerrada por el Servidor <<<<" & vbCrLf
Text5.SelStart = Len(Text5.Text)
End Sub
Si queremos detectar cuando el cliente se desconecta lo hacemos desde el evento Close() del Winsock:
Private Sub Winsock1_Close()
Winsock1.Close
Text5.Text = Text5.Text & ">>>> Conexion cerrada por el Cliente <<<<" & vbCrLf
Text5.SelStart = Len(Text5.Text)
End Sub
Solo resta programar nuestro Servidor para enviar y recibir datos (igual que en el programa Cliente, con la diferencia de invertir Cliente y Servidor cuando informamos en text5 el autor del mensaje).
Métodos necesarios:Eventos involucrados:
- SendData: Envía datos al otro extremo de la conexión (Socket remoto).
- GetData: Recibe datos enviados por el extremo remoto (Socket remoto).
Para enviar datos utilizamos el método SendData desde el botón enviar (Command3).
- DataArrival(): Ocurre cuando el Socket remoto nos esta enviando datos.
- Error(): Ocurre en caso de errores.
Private Sub Command3_Click()
Dim enviar As String
enviar = Text6.Text
Winsock1.SendData enviar
Text5.Text = Text5.Text & "Servidor --> " & Text6.Text & vbCrLf
Text5.SelStart = Len(Text5.Text)
Text6.Text = ""
End Sub
Cuando el cliente nos envía datos se genera el evento DataArrival() indicando que tenemos nueva información disponible, la cual la podemos leer con el método GetData:
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim datos As String
Winsock1.GetData datos
Text5.Text = Text5.Text & "Cliente --> " & datos & vbCrLf
Text5.SelStart = Len(Text5.Text)
End Sub
Por ultimo podemos utilizar el evento error para detectar cualquier problema de conexión. En caso que ocurra uno podemos cerrar la conexión e informar al usuario de dicho error:
Private Sub Winsock1_Error(ByVal Number As Integer, Description As String)
Winsock1.Close
MsgBox Number & " " & Description
End Sub
De esta manera el programa Servidor está terminado y listo para probarlo junto al programa Cliente.