Tipos of Sockets
Hay dos tipos de sockets: sockets de flujo [stream sockets] sockets de datagrama.
Un socket de flujo hace uso de TCP de manera que su transmisión requiera
transmisión y sea realizable. Usas sockets de flujo [stream] cuando quieres
enviar grandes archivos por la red o cuando quieres paquetes de datos secuenciados
(es decir, el orden de los paquetes de datos es importante). Un socket de datagrama
suministra transferencia simple de datos. Nada garantiza que sea realizable
(los datos pueden no alcanzar su destino), secuenciado (los paquetes de datos
pueden llegar en orden diferente al que se ha intentado), o no duplicado(el
socket de destino puede recibir dos o más copias distintas de los mismos
paquetes). HTTP y FTP y algunos otros protocolos usan sockets de flujo mientras
que algunos protocolos de disfusión ('broadcast') usan sockets de datagrama.
Puedes pensar el socket de flujo como una conección de charla telefónica.
Primero tienes que hacer una llamada, y si la llamada tiene éxito, se
establece una conección. Luego puedes intercambiar información
con otros terminales de una manera realizable. Si ocurre algún problema
de comunicación lo sabes de inmediato y puedes tomar medidas para rectificarlo.
El socket de conección de datagrama es como enviar varios correos electrónicos
a alguien. No puedes saber de antemano si llegarán a la persona a la
que intentas enviarle el paquete de datos. Incluso si la persona los recibe,
nada garantiza que el envío le llegue en el orden que se los enviaste.
Y los correo pueden no llegar a su destino.
El modelo de sockets a veces usa el concepto cliente-servidor,
es decir, un socket es pensado como un servidor ya que tiene servicios
que puede rendir a otros sockets, el lado opuesto es el cliente que requiere
algunos servicios del servidor. HTTP y FTP hacen uso de este concepto cliente-servidor.
Ordenamiento de Byte
Como tenemos que
tratar con direcciones IP en la programación de la winsock, deberíamos
conocer primero los diversos ordenamientos de los bytes. Hay dos tipos de ordenamiento
de bytes: big Endian y little Endian. En el esquema de big Endian, el byte del
extremo izquierdo es almacenado en el byte más significante. El esquema
Little Endian es inverso al de big Endian. Por ejemplo, si la dirección
IP es 205.34.67.24, en el esquema big Endian será 205 34 67 24. Pero en
little Endian, será 24 67 34 205.
Los CPUs Intel usan little endian mientras que otros CPUs como los Motorola
usan big endian. La palabra final es: la internet usa el esquema big endian
así que si quieres usar un CPU con ordenamiento de byte por little endian,
debes convertir las dirceciones IP antes de usarlas en la red.
Modos de bloqueo y de no-bloqueo
Las funciones del socket pueden funcionar en dos modos: bloqueo y no bloqueo.
Originalmente, en la implementación de Berkeley Unix, los sockets operan
en modo de bloqueo, es decir, una función de socket no regresará
hasta que la operación haya sido completada. Podemos llamar "modo
de bloqueo" a la operación síncrona y "modo de no bloqueo"
a la operación asíncrona. Una operación de bloqueo puede
durar un tiempo arbitrariamente largo para completarse así como esperando
datos que lleguen desde el sócket remoto. Durante ese tiempo, el programa
parecerá congelado. Esto usualmente es inaceptable en el entorno Windows.
Así que la implementación de Windows de la API del socket incluye
versiones asíncronas (no-bloqueantes) de las funciones de bloqueo orginales.
Deberías usar versiones no bloqueantes cada vez que sea posible ya que
es conforme al paradigma de Windows y suministra mejor resultado que las funciones
de bloqueo.
Puertos
Cuando creas un socket y lo conectas a un socket remoto, debes especificar el
puerto a través del cual se comunicarán los sockets. Los puertos
en este caso no son los puertos de hardware COM1 o COM2. Los puertos en la programación
de winsock son puertos virtuales para propósitos de comunicación
solamente. Quizás un ejemplo pueda clarificar.Supongamos que el servidor crea
un socket y le ordena "escuchar" [to listen] a una conección
que entra por el puerto 21, aunque pueden haber muchos paquetes de red entrado
al servidor, esos paquetes que son destinados al puerto 21 serán dirigidos
a ese socket. Algunos protocolos de Internet tienen sus propios puertos por
defecto. HTTP usa el puerto 80 (decimal) y FTP usa el puerto 21 (decimal). Estos
sólo son puertos por defecto. Significa que el cliente y el servidor
están de acuerdo en comunicarse a través de diferentes puertos,
y pueden hacerlo así sin ninguna repercusión.
Panorama de la Programación de la Winsock
La programación de la winsock normalmente
incluye estos pasos:
Valor
que Regresa
WSAStartup regresa NULL si la
llamada es exitosa. Si no lo es regresa un código de error. Puedes revisar
los códigos de eroor en winsock.hlp. Es la única función
de la winsock que regresa el actual código de error porque no puedes llamar
a WSAGetLastError si la dll del socket np se inicializa primero.
Comentario
Si esta función no tiene éxito, no podrás
usar ninguna función de la winsock. En realidad esta función también
sirve como una rutina de negociación para la mejor versión del
servicio de la api de winsock para aplicación. Después de que
la llamada es exitosa, la estructura WSADATA será llenada con las capacidades
de la implementación actual de la winsock. Uno de los miembros de la
versión más alta de winsock que soporta la librería. Es
decir, si la aplicación requiere una versión de winsock menor
que la soportada por la 1.1 y la actual biblioteca de winsock puede sopotar
la versión 2, la llamada a WSAStartup será exitosa y la biblioteca
winsock regresará 200h en un miembro de WSADATA. Si la aplicación
puede usar el soporte de la winsock 2, puede llamar a WSACleanup para cerrar
la inicialización previa y llamar de nuevo a WSAStartup, esta vez con
200h en su parámetro wVersionRequired. Si tu aplicación sólo
soporta winsock 1.1, no tienes que examinar la estructura WSADATA.
Recorte de Código:
Valor
que Regresa
Si la llmada es exitosa, el
valor que regresa es INVALID_SOCKET y puedes lamar a WSAGetLastError para recuperar
el código de error actual. si la llamada es exitosa, regresa el descriptor
de socket que debes usar en subsecuentes llamadas a la winsock.
Comentario:
Debes crear al menos un socket a ser usado como el dispositivo
de comunicación desde tu terminal. Después de una llamada exitosa,
debes salvar el descriptor del socket regresado para usarlo con las subsecuentes
llamadas a la winsock.
Recorte de Código:
Comentario
Esta función es un instrumento del paradigma de no bloqueo. Permite que
una aplicación especifique en cuáles eventos de winsock está
interesada y cuando los eventos de winsock registrados ocurran, la biblioteca
winsock enviará el mensaje especificado a la ventana. Contrasta con el
escenario de bloqueo, donde debes elegir los eventos de winsock. Esta función
también cambia el socket al modo de no bloqueo. Recomiendo que uses WSAAsyncSelect
cada vez que sea aplicable ya que se conforma al paradigma de Windows.
Si no quieres que la biblioteca winsock envíe más notificaciones
a tu ventana, debes llamar a WSAAsyncSelect con
el valor 0 en el parámetro lEvent. Sin embargo, ten encuenta que pueden
haber mensajes de winsock messages en la cola antes de que canceles la notificación
de winsock. Debes estar preparado para manejarlos. Normalmente deberías
extraerlos de la cola de mensajes llamamndo s PeekMessage
con la bandera PM_REMOVE.
Cuando ov¡curre un evento de winsock en el cual está interesada
la aplicación, el mensaje especificado opr el parámetro msg
será enviado al procedimiento de ventana de la ventana especificada
por el parámetro hwnd. Con el mensaje,
wParam contiene el descriptor de socket, la palabra [word] alta de lParam contiene
el código de error (si hay alguno) y la palabra [word] baja de lParam
contiene el eventot.
Recorte de Código
.data?
hwnd dd ? ; handle de la ventana que recibirá el mensaje winsocke.
socket dd ? ; descriptor de socket
....
.const
WM_SOCKET equ WM_USER+100 ; crear un mensaje personalizado de Win. Puedes usar cualquier
; nombre
; limitado a WM_SOCKET y puedes usar cualquier numero
; que no esté limitado a 100.
......
.code
...........
invoke WSAAsyncSelect, socket, hwnd,WM_SOCKET, FD_CONNECT+FD_READ+FD_CLOSE
; Registrar el interés en eventos de conección,lectura y clausura.
.if eax==SOCKET_ERROR
<pones aquí la rutina de manejo de errores>
.else
........
.endif
WndProc PROC hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
.......
.if uMsg==WM_SOCKET
; el mensjae especificado en WSAAsyncSelect
mov eax,lParam
.if ax==FD_CONNECT
; la palabra baja de lParam contiene el código del evento.
shr
eax,16
; el código de error (si lo hay) setá en la palabra [word] alta
de lParam
.if
ax==NULL
<no hay error así que se procede>
.else
<ocurre un error. Pones aquí la rutina del manejo de errores>
.endif
.elseif ax==FD_READ
shr
eax,16
.if
ax==NULL
<no ocurre ningún error así que se procede>
.else
<ocurre un error. Pones aquí la rutina del manejo de errores>
.endif
.elseif ax==FD_CLOSE
shr
eax,16
.if
ax==NULL
<no ocurre ningún error así que se procede>
.else
<aocurre un error. Pones aquí la rutina del manejo de errores>
.endif
.endif
.else
..........
.endif
WndProc ENDP
Debes llamar esta estructura y pasar su dirección
a connect.
Valor de Regreso
Depende del modo del socket.
.data?
socket dd ?
.code
........
mov sin.sin_family, AF_INET
invoke htons, Port
; convierte el número del puerto en byte de red de primer orden
mov sin.sin_port,ax
; nota que este miembro es un parámetro que tiene tamaño de
palabra [word].
invoke inet_addr, addr IPAddress
; convierte la dirección IP en
byte de red de primer orden
mov sin_addr,eax
invoke connect,socket,addr sin,sizeof sin
.if eax==SOCKET_ERROR
; asumimos que usamos el modo de no-bloqueo, connectarse
; siempre regresará SOCKET_ERROR
invoke WSAGetLastError
; recuperar el código de error actual
.if eax!=WSAEWOULDBLOCK
; si no es WSAEWOULDBLOCK
<Pones aquí la rutina
del manejo de errores>
; entonces ocurrió un verdadero error
.endif
.endif
Valor Regresado
Si tiene éxito, regresa un puntero a la estructura
hostent que
es localizada por la implementación de los sockets de Windows. Si la
llamada falla, regresa NULL y puedes lamar a WSAGetLastError
para recuperar el código de error.
Recorte de Código
.data
sin SOCKADDR_IN <>
hostname db "members.xoom.com",0
Port dd 80
; Usamos el puerto 80, el puerto HTTP para propósitos de demostración
.data?
socket dd ?
.code
........
mov sin.sin_family, AF_INET
invoke htons, Port
; convierte el número del puerto
en byte de red de primer orden
mov sin.sin_port,ax
; nota que este miembro es un parámetro que tiene tamaño de palabra
[word].
invoke gethostbyname, addr hostname
mov eax,[eax+12]
; mover el valor del miembro h_list a eax
mov eax,[eax]
; copiar el puntero de la dirección IP actual en eax
mov eax,[eax]
; copiar la dirección IP eneax
mov sin.sin_addr,eax
invoke connect,socket,addr sin,sizeof sin
.if eax==SOCKET_ERROR
; asumimos que usamos el modo de no-bloqueo, conectarse
; siempre regresará SOCKET_ERROR
invoke WSAGetLastError
; recuperar el código de error actual
.if eax!=WSAEWOULDBLOCK
; si no es WSAEWOULDBLOCK
<Pones aquí la rutina
del manejo de errores>
; entonces ocurrió un verdadero error
.endif
.endif
Normalment no usamos ests dos banderas, el parámetro flags debería en este caso estar en 0.
Comentario
Esta función envía datos a un socket remoto
conectado. No toma en cuenta ni sabe nada acerca de los datos enviados.
Recorte de Código
Comentario
Esta función lee los datos desde el socket y los
almacena en un buffer específico. Aparece una pregunta: ¿cómo
sabes cuántos bytes están disponibles para leer desde el socket?
La respuesta es la función ioctlsocket.
Tiene la siguiente sintaxis:
Valor que Regresa
Si tiene éxito, the valor que regresa en eax es
NULL. Si no regresa SOCKET_ERROR
y debes llamar a WSAGetLastError
para recuperar el código de error
Comentario
Esta función es usada para controlar el modo de
un socket. Con la orden FIONBIO, puede conmutar el socket hacia el modo de bloqueo
o de no bloqueo. Con la orden FIONREAD, regresa la cantidad de datos disponibles
en el socket. Con la orden SIOCATMARK, chequea si han sido leídos los
datos fuera del ancho de banda.
Recorte de Código
Valor de Regreso
Si es fructífera, regresa NULL. Si no regresa SOCKET_ERROR
y debes llamar a WSAGetLastError
para recuperar el código de error.
Comentario
Esta función cierra un socket. Todos los recursos
del socket serán liberados.
Recorte de Código
Valor que Regresa
Si es fructífera, regresa NULL. Si no regersa SOCKET_ERROR.
Comentario
Deben coincidir el número de llamadas [calls] a
WSAStartup
y a WSACleanup.
Es decir, si llamas a WSAStartup tres veces, debes llamar tres veces a WSACleanup
sino la biblioteca winsock no será capaz de liberar ninguno de los recursos
no usados. Ésta es la última función que llamas cuando
vas a través de la biblioteca de la winsock.
Recorte de Código
[Iczelion's Win32 Assembly Homepage]
n u M I T_o r's Programming Page
Este tutorial, original de Iczelion, ha sido traducido por: n u M I T_o r