Site hosted by Angelfire.com: Build your free website today!

Guía de Iczelion para la Programación de la Winsock

Winsock o el socket de windows toma su concepto de Unix. La idea detrás de los sockets es usarlos como dispositivos de comunicación entre diferentes máquinas en la red. Cada máquina crea uno o más sockets para conectarse con una o más máquinas. Los sockets hacen uso del protocolo TCP/IP, como tales, operan en las capas más altas de la red. Usamos sockets en cualquier protocolo alto que quiera emplear TCP/IP como medio de transporte, tales como HTTP, FTP, etx. El socket mismo no entiende ni se cuida del contenido que pasa a través de él. Realiza la tarea de enviar a y de recibir de su contraparte remota.

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:

  1. Initializar la dll winsock.
  2. Crear el socket.
  3. Specificar el modo de operación: bloqueo o no bloqueo.
  4. Conectar el socket.
  5. Realizar algunas tareas con el socket tal como enviar o recibir datos.
  6. Cerrar elsocket.
  7. Liberar dll winsock.
Exploraremos estos pasos con cierto detalle.

Inicialización de la Biblioteca de la Socket
Debes llamar a WSAStartup para inicializar la dll winsock antes de usar cualquier función de la winsock. Esta función tiene la siguiente sintaxis: Parámetros
wVersionRequried  == Versión de la winsock que quiere usar tu aplicación. En este momento, la versión 1.1 y 2 están disponibles. La version 1.1 viene con Windows 95 y la versión 2.x viene con Windows NT 4.0. Si quires especificar la versión 1.1, usa 101h si quieres usar la versión 2.0, usa 200h
lpWSADATA == Puntero a la estructura WSADATA que la winsock.dll llenará.. Esta estructura contiene la implementación de la actual dll winsock. Normalmente creas una instancia no inicializada de WSADATA y pasas su dirección a WSAStartup.

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:


Creación del Socket
Después de una llamada exitosa a WSAStartup, puedes proceder a crear el socket. Parámetros
af == Formato de la dirección que en los actuales momentos es sólo uno: PF_INET
type == tipo de socket que quieres crear, stream o datagrama. Si quieres un socket de stream, usas SOCK_STREAM sino usas SOCK_DGRAM
protocol == si el parámetro af es AF_UNSPEC (no especificado), debes especificar aquí el protocolo. Sin embargo, ya que siempre usamos PF_INET, puedes usar 0.

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:


Especificando las Opciones del Socket
Después de que la socket es creada, por defecto se pone en modo de bloqueo. Si quieres operar en modo de no-bloqueo, tienes que establecerlo con setsockopt o WSAAsyncSelect. Normalmente, deberías usar WSAAsyncSelect para volverse hacia un modo de no bloqueo para el socket. Así que aquí demostraremos el uso de WSAAsyncSelect. setsockopt no es difícil de usar, puedes imaginártelo. setsockopt puede alterar muchas características de un socket de manera que puedas querer observarlas luego. Parámetros
socket == descriptor del socket que es regresado desde una llamada a socket.
hwnd == handle a la ventana que recibirá la notificación de los eventos de winsock.
msg == mensajes personalizados de windows que creas y que quieres que la biblioteca winsock envíe a tu ventana en caso de que ocurran los eventos en los que estás interesado.
Event == eventos winsock en los cuales está interesada tu aplicación. Puedes especificar una de las constantes de abajo o agregarlas juntas si tu aplicación está interesada en más de un evento de winsock. Valor que Regresa
Si la llamada es exitosal, el valor regeresado es NULL. De otra manera regersa SOCKET_ERROR y podrás llamara aWSAGetLastError para ecuperar el código de error actual.

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


Conección a un Socket Remoto
Después de que el socket es creado, tienes dos posibilidades: esperas por una conección de entrada o te conectas a un socket remoto. Si quieres esperar por una conección que está entrando, debes llamar a listen para que 'escuches' [listen] por una conección que esté entrando y llamara a accept para establecer unaconección con el socket remoto. Si quieres conectarte a un socket remoto, debes llamar a connect que tiene la siguiente sintaxis: Parámetros
socket == descriptor de socket del socket localt. Puedes pasar el descriptor del socket regersado por lallamada a socket como este parámetro.
lpSockAddr_in == un puntero a una esrtuctura SOCKADDR_IN la cual está declarada como: namelen == tamaño de la estructura SOCKADDR_IN

Valor de Regreso
Depende del modo del socket.

Recorte de Código Si sólo obtienes una cadena URL tal como "http://members.xoom.com/Iczel", debes pasarla en vez del nombre del huesped [host], en este caso "members.xoom.com". Y pasas la dirección del nombre del huesped como un parámetro a la fnción gethostbyname que tiene la siguiente sintaxis: Parámetros
lphostname == puntero a lacadena del nombre del huesped [host].

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.

Nota que windows.inc es suministrada por hutch, y la estructura hostent recibe el nombre hostentStru. Comentario
Esta función es usada para recuperar la(s) dirección(es) IP del huesped especificado en una cadena. Nota que lo que quieres setá principalmente en el miembro h_list.

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


Operaciones sobre el Socket
Hay varias operaciones que puedes ejecutar sobre el socket tales como enviar o recibir datos. Aquí examinaremos ambas en detalle.
Enviar Datos a un Socket Remoto
Usamos send para 'enviar' [send] datos sobre un socket de stream y sendto para enviar datos a un socket de datagrama. Aquí examinaré send ya que muchos protocolos populares como HTTP y FTP usan sockets de stream. Parámetros
socket == descriptor del socket
buffer == dirección de los datos que quieres enviar. Los datos no necesitan terminar en NULL ya que su tamaño está especificado en el parámetro len.
len == tamaño de los datos a enviar
flags == banderas [flags] que especifican la conducta de la función. Hay dos banderas que puedes usar: Valor Regresado
Si la lamada es infructuosa, el valor regresado en eax es SOCKET_ERROR. Si tiene éxito, el número de bytes enviados es regresado en eax. Nota que este número puede no ser igual al parámetro len.

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

Leyendo Datos desde el Socket
Hay dos variantes de llamadas a winsock para leer datos del socket: recv y recvfrom. recv es para ser usada con un socket de stream mientras que recvfrom es para un socket de datagrama. Parámetros
socket == descriptor del socket
buffer == dirección del bloque de memoria dónde vas a almacenar los datos que llegan.
len == tamaño del bloque de memoria
flags == banderas [flags] que especifican la conducta de la función. Hay dos banderas que puedes usar: Valor Regresado
Si la llamada es fructífera, regresa el número de bytes leídos desde el socket. Si es infructuosa regresa el valor SOCKET_ERROR. Si el valor que regresa eis 0, el socket remoto ha sido cerrado.

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:

Parámetros
socket == descriptor del socket
cmd == orden a ser ejecutada sobre un socket. Hay tres ordenes: FIONBIO, FIONREAD, y SIOCATMARK. Ya que queremos obtener el tamaño de los datos disponibles en el socket, nos concentraremos en la orden FIONREAD. FIONREAD que determina la catidad de datos que pueden ser leídos desde el socket. La cantidad de datos es almacenada en la dirección apuntada por lpArgument.
lpArgument == dirección de un parámetro adicional para cmd.

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


Cerrando el Socket
Después que haz terminado de usar el socket, tienes que cerrarlo llamando a closesocket. Parámetro
socket == descriptor del socket

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


Liberar le Biblioteca Winsock
Cuando la aplicación ya no necesita usar más la biblioteca winsock, se llama a WSACleanup. Parámetro
Esta función no tiene parámetros.

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 Winsock Section]

[Iczelion's Win32 Assembly Homepage]

Índice

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