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.

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: Eventos involucrados:

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:

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: Eventos involucrados:

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: Para enviar datos utilizamos el método SendData desde el botón enviar (Command3).

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.