Tutorial: Un mundo de sockets

A

Tutorial: Un mundo de sockets.

Buenas. En primer lugar, este tutorial va dirigido tanto a los programadores como a los
curiosos, que desean saber cómo funciona la comunicación entre ordenadores. Si alguna
vez te has preguntado cómo funciona un servidor web, un FTP, el correo o el irc, y,
especialmente has querido diseñar un aplicación similar, ésta debería ser una buena
introducción.

Hoy en día, una de las principales aplicaciones de la informática es la comunicación
entre máquinas, a través de redes. Ésto nos permite hacer conexiones de una máquina
a otra y enviar datos con un objetivo. Cuando uno ve un video por streaming, o habla
con un amigo a través de mensajería instantanea, puede hacerlo porque hay una máquina
que le ofrece unos datos (un servidor) y otra que hace la petición y los interpreta
(un cliente).

Hagamos un gráfico ascii:


  • Servidor [IP] [puerto] <------- Protocolo --------> [puerto [IP] Cliente

    Es muy sencillo:

    IP: Nombre de la máquina en una red, una identificación única. (ej: 10.0.0.1)
    Puerto: Es el medio de acceso a la máquina. (ej: 80)
    Protocolo: El método formal acordado de comunicación. (ej: TCP)

    Tanto los puertos como los protocolos están definidos por una serie de entidades
    que crean normas para que sean standard. Por ejemplo, un servidor Web suele usar
    TCP como protocolo y puerto 80, uno de IRC los puertos desde el 6667 al 6669... etc

    Supongamos que uno abre su navegador y escribe la dirección de Google. Desde que se pulsa
    "Ir a" hasta que vemos la web y podemos utilizarla, ocurren una serie de cosas:

  • Cliente: Hola, soy Mozilla Firefox, desde X IP y me gustaría ver la web.

  • Servidor: Hola, soy Apache, desde X IP, perteneciente al dominio de Google
    ahora te envío lo que necesitas.

  • Cliente: Mmm, me están enviando datos, una cabecera... HTML... voy a interpretarlos.

  • Usuario: Joé, mil resultados para "porn".

    Bien, ahora que sabemos ésto, diseñaremos una aplicación que utilice los
    conceptos, para comunicarse entre dos máquinas. En este caso, crearemos
    una simple aplicación de Chat en Visual Basic 6.0, ya que vi una petición
    en un thread anterior al respecto.

    Empecemos por hacer una interfaz para el servidor. Algo así podría servir:

    http://img133.imageshack.us/img133/6660/tutorial13sk.jpg

    Como se puede observar, no hay más que dos textBox (cajas de texto), un botón
    para enviar y un control de Winsock, el que nos dará el uso de la IP, puerto y protocolo.

  • La textbox pequeña es: text1

  • La textbox grande es: text2

  • Y el boton: command1

  • El socket: winsock1

    Ahora, en el código fuente, estableceremos:

    '-------------------------------------
    ' Para iniciar el socket
    '-------------------------------------

    Private Sub form_load()
    Winsock1.LocalPort = 1111 ' Este será nuestro puerto.
    Winsock1.Listen ' Y aquí le decimos que escuche por conexiones entrantes.
    End Sub

    '-------------------------------------
    ' Para aceptar conexiones entrantes
    '-------------------------------------

    Private Sub WinSock1_ConnectionRequest(ByVal requestID As Long)
    Winsock1.Accept requestID ' Aceptamos la conexión de un cliente
    End Sub

    '--------------------------------------
    ' Para interpretar los datos recibidos
    '--------------------------------------

    Private Sub WinSock1_DataArrival(ByVal bytesTotal As Long)
    Dim datos As String ' Declaramos una variable
    Winsock1.GetData datos ' Metemos en la variable los datos que recibimos
    Text2.Text = Text2.Text & datos ' Añadimos al textbox grande el texto
    Text2.SelStart = Len(Text2.Text) ' Ponemos el cursor al final del textbox grande
    End Sub

    '-----------------------------------------
    ' Para cuando pulsemos el botón de enviar
    '-----------------------------------------

    Private Sub Command1_Click()
    Dim datos As String ' Declaramos una variable
    datos = text1.Text ' Metemos en la variable los datos a enviar
    text2.Text = text2.Text & datos & vbCrLf ' Añadimos al textbox grande el texto
    WinSock1.SendData datos ' Enviamos los datos
    Text2.SelStart = Len(Text2.Text) ' Ponemos el cursor al final de textbox grande
    Text1.Text = "" ' Borramos el texto del textbox 1
    Text1.SetFocus ' Y le damos foco

    End Sub

    Y ese sería todo el código necesario para el servidor.
    Diseñemos ahora el cliente:

    http://img479.imageshack.us/img479/6946/tutorial26cz.jpg

    Os recomiendo que dupliqueis el proyecto del servidor, puesto que el cliente es casi
    igual y solamente hay que hacer unas pequeñas modificaciones. Concretamente,
    quitaremos la parte de aceptar conexiones entrantes y haremos que nuestro cliente
    se conecte al servidor. Quedaría así:

    '-------------------------------------
    ' Para iniciar el socket
    '-------------------------------------

    Private Sub form_load()
    WinSock1.RemoteHost = Text3.text ' Le pasamos a WinSock la IP especificada en el textbox
    WinSock1.RemotePort = 1111 ' Y también el puerto
    WinSock1.Connect ' Le decimos que conecte
    End Sub

    '--------------------------------------
    ' Para interpretar los datos recibidos
    '--------------------------------------

    Private Sub WinSock1_DataArrival(ByVal bytesTotal As Long)
    Dim datos As String ' Declaramos una variable
    Winsock1.GetData datos ' Metemos en la variable los datos que recibimos
    Text2.Text = Text2.Text & datos ' Añadimos al textbox grande el texto
    Text2.SelStart = Len(Text2.Text) ' Ponemos el cursor al final del textbox grande
    End Sub

    '-----------------------------------------
    ' Para cuando pulsemos el botón de enviar
    '-----------------------------------------

    Private Sub Command1_Click()
    Dim datos As String ' Declaramos una variable
    datos = text1.Text ' Metemos en la variable los datos a enviar
    text2.Text = text2.Text & datos & vbCrLf ' Añadimos al textbox grande el texto
    WinSock1.SendData datos ' Enviamos los datos
    Text2.SelStart = Len(Text2.Text) ' Ponemos el cursor al final de textbox grande
    Text1.Text = "" ' Borramos el texto del textbox 1
    Text1.SetFocus ' Y le damos foco

    End Sub

    Y no hay más ! Es así de simple. Espero este tutorial haya servido de algo.
    Un saludo y hasta otra. :-)

IS4kO

Buen tutorial Archville muy bien resumida la idea cliente <-> servidor y el programa de ejemplo muy basico con los conceptos importantes

Enhorabuena :)

SeiYa

Yo lo que hago es que el servidor tenga un winsock indexado de manera que pueda tener varias conexiones y solo escuche, de momento con lo que me postearon en el otro post voy perfecto ;)

Xav0

Archville, mas gente como tu es la ke hace falta ;)

Un saludo y muchas gracias por el tutorial.

erdanblo

Hace tiempo estube buscando sobre esto pero en PHP, ahora es tarde, a ver si me lo miro mejor y hago unas preguntitas que no me aclaro yo aun mucho con los sockets.

pd. ¡GRACIAS!

VerXeR

yo hice exactamente el mismo proyecto en una web que tenia manuales de visualbasic

Whose

Muy currado Archville, lo añado a la lista de recursos y le pongo la chinchetilla :P

EnZo

Muy bien explicado.

Pero por ponerte alguna pega. Que es en VB, no es que sea malo hacerlo en eso, pero si mas sencillo de la cuenta. Actua por eventos y es facil que haga lo que quieres. Ahora si quieres hacer un servidor en C como quiero aprender yo, ya es mas chungo, xq to el chorro de codigo va pa abajo y no actua por eventos tipo on_Load.

C

Mu bien explicado, buen tutorial _
Les servira a mux0s

pd: Asta que no vi la foto de "asi podria quedar mas o menos" no sabia que el manual era de VB XDDDD

ReMaTxEs

Mira por donde yo toy haciendo un pequeño proyecto para gesionar una biblioteca y en VB... m parece que tu tutorial me ve a ser muy pero que muy util... GRACIAS!!!!!

erdanblo

Bueno, mi duda vino cuando intente realizar un script en php mandar sms via web gracias al servidor de wowlg.

Lo que no comprendo muy bien de los sockets, es ... ¿una vez que yo realizo la conexión y la establezo... como se que comandos mandar? ¿es algo albitrario o como es eso? es básico ya, pero no he logrado a comprenderlo :s.

SeiYa

Probé a hacerlo pero bueno, en el servidor cuando le llega un mensaje hace:

Private Sub Winsock1_DataArrival(Index As Integer, ByVal bytesTotal As Long)
Dim mensaje As String
Me.Winsock1(Index).GetData mensaje
Dim i as Integer
For i = 0 To Conexiones - 1
On Error Resume Next
Me.Winsock1(i).SendData entrada
If Err.Description <> "" Then
MsgBox ("Error:" + Err.Description)
End If
Next
End Sub

Para enviarselo a todos los conectados pero solo le llega al último, sin embargo si antes del senddata pongo un msgbox, si que me salen 3 msgbox (si tengo 3 clientes conectados) y se lo envía a 3, probé usando un timer para que no los enviara tan de seguido pero nada, no se por qué falla ...

Otra duda que me ha surgido es en el data, como hacer para enviar un Type ya que no me ha sido posible, lo que quería enviar era un integer a modo de "tipo de dato a enviar" y un string que sería el texto o la información, así el servidor en el data arrival podría saber si lo que recibe es una ip para mostrarla en el log, si recibe un nombre de usuario para enviarselo a los demas clientes y que sepan que ese usuario se ha conectado o si es un mensaje ... para tratar cada envío de forma diferente... había probado en enviar un array pero tampoco tengo muy claro este punto.

Un saludo y a ver si me podéis echar una mano ;)

P.D: Espero que me saquéis de este embrollo como siempre MV ha sabido hacer xD

Soltrac

#8, si quieres programacion visual de C, hay multitud de entornos visuales con programacion con eventos.

Borland C++, Visual C, etc.

#11 No entendi bien tu duda, los comandos evidentemente dependen de lo q escuche el servidor. Imaginemos un servidor q espera un comando GET y luego una IP.

Hasta q no le envias un GET no escuchara una ip.

#12 prueba esto, solo para saber si es q hay un fallo:

Private Sub Winsock1_DataArrival(Index As Integer, ByVal bytesTotal As Long)

On Error GoTo Errores

Dim mensaje As String
Me.Winsock1(Index).GetData mensaje
Dim i as Integer
For i = 0 To Conexiones - 1
Me.Winsock1(i).SendData entrada
End If
Next

Exit Sub
Errores:
MsgBox ("Error:" + Err.Description)
End Sub

Te lo digo pq estoy pensando que si hay un error antes, al hacer un resume next puede que se salte algo del bucle y asi te aseguras de q es verdad de q solo se envia a un cliente o si realmente hay un error q no estamos viendo

SeiYa

Lo curioso es que con un MsgBox antes del SendData me saca tantos msgbox como usuarios conectados y me envía los mensajes, sino, nada ...

Lo probaré en cuanto llegue a casa ;) gracias y un saludo.

Soltrac

#14 a mi me han pasado cosas asi, no me extraño :P

Con respecto a saber lo q kieres enviar, deberías enviar un mensaje con una cabecera.

Si un usuario se conecta, envia IP 192.168.1.23 (supongamos q es la ip).

Tb envia el nombre de usuario. USUARIO pepito

Si escribe una frase, la lee. FRASE Hola chavales, q tal?

Lo q hace el servidor es ir mirando de q tipo es lo q recibe, si IP, USUARIO o FRASE y lo analiza. Tan facil como splitear la primera palabra y ver de q tipo es.

PD: No se si era esto lo q pedias jejeje

SeiYa

Pues sí, algo así haré, enviarlo con una cabecera, lo que pasa es que no ando muy puesto en VB y no se todavía más o menos como xD pero ya se de antemano que no es complicado, ya miraré a ver como separarlo y demás aunque si me lo dices ya pues mira, eso que me ahorro XD

Cuando llegue a casa probaré todo eso y ya te comento.

Soltrac

Es facil, separalo asi:

Imagina q tenemos en un string esta cabecera:

Dim Frase As String = "IP 192.168.1.1"

Ahora vamos a separarla por espacios.

'Fijate que Palabras() Es un vector
Dim Palabras() As String
Palabras=Split(Frase)

'Ahora Palabras contiene esto:
'Palabras(0)="IP"
'Palabras(1)="192.168.1.1"

Por lo tanto, lo que haremos será

If Palabras(0)="IP" Then
'Lo tratamos como IP
Else ..... 'etc

Como ves es sencillo y no pueden intentar 'hackearte' el programa intentando enviar la cabecera alguien, porque tienes que suponer q TODO lo q se le envie al servidor tendra cabecera, porque si no alguien podria enviar como frase un IP 192.168.1.1 y el servidor lo entenderia como IP. Es decir, lo unico que tienes que intentar es que todo le llegue al servidor con una cabecera.

SeiYa

Y si tengo

"FRASE hola que tal soy pepito grillo"

Me lo deja en:

(0)= "FRASE"
(1)= "hola"
(2)=...

o me hace (1)="hola que tal ..."

Por que solo quiero separar los espacios 1 vez, el primer espacio..

Un saludo ;D

Soltrac

Si, pero a ti eso no te importa, y te dire pq :P

Tu haces esto:

Tu sacas la cabecera y para sacar el contenido usas un substring

Haces
Dim Pos As IntegeR=Instr(Frase," ") y te buscará la posición con el primer espacio.

Seguidamente, usas un
Dim Contenido As String = Frase.SubString(Pos) y listo, en contenido tienes lo q quieres leer.

SeiYa

Gracias, necesito más horas de VB.

Soltrac

Aun asi, te estoy escribiendo en sintaxis VB .NET pq ya hace unos meses q no uso el VB .6 de toda la vida, asi que si hay algo que no te compila dimelo, q busco su version de VB normal :P

SeiYa

Fíjate, ni aún así ... se conectan 2 clientes, solo recibe datos el último, cierro al último cliente, el primero envía datos, pero como le cierro manualmente sin cerrar el socket con el cliente que queda envio algo y da error en el servidor por que no puede enviar al segundo cliente (algo normal) sin embargo ahora, si que recibe todos los datos que no había recibido antes pero en una misma linea, como si se hubiesen apelotonado ... todos los data en un mismo string y mientras uno recibió 4 mensajes en 4 lineas este recibe 4 mensajes en 1 linea ... no se si me entiendes XD

Vamos que sigue sin funcionar :S

Soltrac

Haz una cosa, pon antes del senddata esto:

For i = 0 To Conexiones - 1
If Winsock1(i).State=sckConnected Then <- ESTO
Me.Winsock1(i).SendData entrada
End If
Next

Tb haz otra prueba, a modo de prueba. Prueba a enviar la informacion desde el servidor cuando le des a un boton enviar, no en el mismo data arrival. Solo es para saber si no se gestiona eso correctamente.

Es decir, pon en el servidor un boton enviar y q envie a todos los clientes un string de prueba, para saber si eso lo hace bien (te lo digo por lo del msgbox)

SeiYa

No funciona ninguna de las dos :S

Sin embargo, si antes del for, pongo un msgbox (ya no dentro y que me salgan tantos como clientes conectados) si que funciona, no lo logro entender.

Soltrac

http://www.monografias.com/trabajos30/tutorial-visual-basic/tutorial-visual-basic.shtml Mira eso el apartado de multi conexiones a ver si hay algo diferente a lo tuyo

Soltrac

He arreglao tu codigo, ya funciona perfectamente. Te paso mi msn por privado para pasartelo bien :)

Usuarios habituales

  • Soltrac
  • SeiYa
  • erdanblo
  • ReMaTxEs
  • VerXeR
  • IS4kO
  • Archville