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