FoxPress – Julio-Agosto 2001

 

Usando Winsock para enviar HTTP

http://www.fpress.com/

 

Por la Redacción de FoxPress                                                   socket.zip

 

 

       Este artículo es una pequeña guía sobre como traerte páginas HTML mediante el control Winsock.OCX

 

       Quizás puedes preguntarte ¿Que interés tiene eso cuando te las puedes bajar con el magnífico IE?

 

       La razón estriba en que puedes manejar las cabeceras de los mensajes enviados para que sea interpretado de una forma u otra por el receptor y también, con muy pocos cambios, puedes llegar a tener un sistema de bajarte archivos en tu aplicación mediante el protocolo http

El proceso de comunicación mediante HTTP, entre dos ordenadores es muy simple. La aplicación cliente establece una conexión TCP/IP a través del puerto 80 de un servidor web, y envía la petición HTTP al servidor. El servidor procesa la petición del cliente, y enviá la respuesta HTTP al cliente y cierra la conexión.

De esta forma, una sesión HTTP consiste de:

·   El cliente establece la conexión

·   El cliente envía la petición HTTP

·   El servidor envía la respuesta http

·   El servidor cierra la conexión

Nota: el estado de una sesión HTTP (cerrar la conexión) es diferente según la versión del protocolo HTTP que se esté usando. Si es la verión 1.0, no necesitas hacer cambios adicionales ya que el servidor cierra automáticamente la conexión después de que ha enviado todos los datos. Pero en la versión 1.1 de HTTP, puedes controlar el estado de la conexión estableciéndolo en la cabecera de la petición. Para forzar el cierre de la conexión se deberá especificar en la cabecera de la forma siguiente:

Connection: close

             Si queremos que la conexión se quede abierta habrá que poner:

Connection: Keep-Alive

El cliente establece la conexión

Este paso lo ejecutamos con esta instrucción:

   THISFORM.oleWinsock.Object.Connect("www.microsoft.com", 80)

Fíjate que ponemos la dirección a la que queremos conectarnos sin el http::// y especificamos también a través de qué puerto vamos a ir.

Como Fox es demasiado rápido es necesario pararlo un poco después de la anterior petición para que al servidor le dé tiempo a responder satisfactoriamente a la petición. Por eso después de esa instrucción es bueno algo como:

             Wait wind ‘Conectando....’ Time 1

 

El cliente envía la petición HTTP

 

Una vez establecida la conexión podemos enviar la petición http al servidor. 

La petición HTTP es un bloque de líneas de código en ASCII. La primera línea contiene la instrucción http, el path relativo del fichero que nos queremos traer y la versión del protocolo. El resto de las líneas son otras cabeceras http con información complementaria. Una mínima petición sería esta:

GET /default.asp HTTP/1.1
Host: www.microsoft.com
Accept: */*
Connection: close

 

Si en vez de la página desfault.asp quisieramos la página de incio por defecto deberíamos poner:

 

GET / HTTP/1.1
Host: www.microsoft.com
Accept: */*
Connection: close

 

Si queremos una cabecera más elaborada podríamos poner:

 

GET / HTTP/1.1
Host: www.microsoft.com
Accept: */*

         User-Agent: miapp

         Pragma: no-cache

         Cache-Control: no-cache

         Connection: close

 

En la documentación del protoclo HTTP puedes ver todas las opciones de las cabeceras. También puedes ver las cabeceras que te envían a ti mediante un esnifador de red como el de http://www.ufasoft.com/sniffer/  que viene en los ficheros de este mes. De esa forma puedes aprender más sobre las cabeceras.

 

En nuestro caso, construiremos una cabecera sencilla y lo podemos hacer con este código:

 

local strHttpRequest

strHttpRequest = "GET / HTTP/1.1" + chr(13) + CHR(10)

strHttpRequest = strHttpRequest + "Host: www.microsoft.com" + ;

CHR(13) + CHR(10)

strHttpRequest = strHttpRequest + "Accept: */*" + CHR(13)+ CHR(10)

strHttpRequest = strHttpRequest + "Connection: close" + CHR(13)+ CHR(10)

strHttpRequest = strHttpRequest + CHR(13)+ CHR(10)

 

En nuestro código observa como la cadena de cada línea acaba con un CHR(13)+ CHR(10) y al final se añade otro CHR(13)+ CHR(10) Lo más importante a tener en cuenta es la línea en blanco que se mete al final debido a que este protocolo distingue la cabecera del cuerpo mediante la existencia de una línea en blanco. Esa línea en blanco le esta diciendo que hemos acabado de entregarle la cabecera.

 

Una vez construida la cabecera la enviamos con:

 

Thisform.oleWinsock.Object.SendData(strHttpRequest)

 

Seguramente te estará preguntando por qué pongo ese ‘Object’ dentro de las sentencias que invocan al Winsock. La razón estriba es que este control tiene métodos y propiedades que se llaman igual (algo que en Fox no se puede hacer) y para distinguir una de otra cuando se le llama necesitamos poner ese Object.

 

 

El servidor envía la respuesta HTTP

 

             Lo que el servidor envía es algo como esto:

 

                 -- HTTP/1.1 200 OK
                |   Server: Microsoft-IIS/5.0
                |   Date: Mon, 10 Jun 2001 10:00:01 GMT

Cabecera HTTP  ----     Content-Type: text/html
                     |   Accept-Ranges: bytes
                     |   Last-Modified: Fri,07 Jun 2001 21:40:36 GMT
                     |   ETag: "d061b8e8d9a0bf1:869"
                      --  Content-Length: 22796
Linea en blanco ---->
                    -- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2                 

                                                  Final//EN">
                    |   <html>
Cuerpo Mensaje --       <head>
                    |   ........
                    --  </html>

 

 

             Aquí hay que comentar varias cosas. Lo primero es conseguir que el servidor nos responda y para eso debe haber un 200 OK que quiere decir que todo ha ido bien. Cualquier otro número sobre todo la saga de los 400 indica que algo ha ido mal.

 

             Lo segundo en lo que tenemos que fijarnos es si la última línea de la cabecera es:

 

                            Content-Length: 22796

 

O por el contrario es:

 

                      Content: chunked

 

             Son dos sistemas que tienen los servidores de enviar el tamaño de la página que van a enviar. En el primer caso viene en ese parámetro y en el segundo al principio de cada uno de los paquetes TCP. De momento no nos detendremos más en este asunto.

 

             Para conseguir meter en una variable de Fox toda la página que nos viene deberemos escribir lo siguiente en el método DataArrival del Winsock:

 

LPARAMETERS bytestotal

 

LOCAL lcData

lcData = SPACE(bytestotal)

WITH THIS.Object

 .GetData(@lcData, 8, bytestotal)

 .Close

ENDWITH

 

? lcHTML = lcData

 

Este es el código que habría que poner aunque con algunos matices. En primer lugar fíjate como este método recibe en un parámetro el tamaño en bytes del paquete actual ( ten en cuenta que un paquete no coincide con la página a recepcionar sino que es un paquete TCP y normalmente la recepción de una página conlleva recepcionar varios paquetes TCP que si me apuras puede que en algún caso lleguen desordenados( el paquete correspondiente al final de la página antes que el correspondiente al del medio) esta es una de las ‘virtudes’ que en este caso se nos puede volver en contra de Internet)

 

Después fíjate como a nuestra variable lcData la hacemos igual al tamaño de los bytes que vienen y conseguimos, por fin, la cadena que nos envían por referencia mediante el @lcData

 

La cadena la recibimos mediante el método GetData del control y luego cerramos la conexión con el Close.

 

Pero aquí hay algunas incongruencias pues este método aunque lo invoquemos una sola vez con la instrucción:

 

Thisform.oleWinsock.Object.SendData(strHttpRequest)

 

Se ejecuta tantas veces como paquetes vamos a recepcionar por tanto no podemos cerrar la conexión después del primer paquete y además si inciamos la variable al principio del método vamos perdiendo la información subsiguiente. ¡Este método hay que escribirlo de otra manera!

 

Ahora lo escribimos de otra manera, pero tal y como está te servirá al principio para ver si te conectas bien y recibes un 200 OK del Servidor.

 

Si el servidor al que nos conectamos no hace un chunked de los datos podríamos escribir el siguiente código:

 

LOCAL lcData

lcData = SPACE(bytestotal)

 

WITH THIS.Object

    .GetData(@lcData, 8, bytestotal) && 8 es vbString

    Thisform.m_strHttpResponse = Thisform.m_strHttpResponse + lcData

     lnInicio = at("Content-Length:",lcData) +15

     lnFinal = at(CHR(13)+CHR(10)+CHR(13)+CHR(10),lcdata)

     lnBytes = substr(lcData,lnInicio, lnfinal -lnInicio)

                    

     IF val(lnBytes) > 0

              Thisform.lnBytes = val(lnBytes)

     ENDIF

     Thisform.lnbytestotal = Thisform.lnBytesTotal + BytesTotal

                  

     IF thisform.lnBytesTotal > INT(Thisform.lnBytes)

             thisform.Edit1.value = Thisform.m_strHttpResponse

            .Close()

   ENDIF                         

ENDWITH

 

Como se ve en este código vamos cargando en Thisform.m_strHttpResponse el contenido de cada uno de los paquetes que se reciben y en Thisform.lnbytestotal