De qué vamos a hablar
Durante los cuatro últimos artículos hemos ido tratando un tema muy básico dentro de cualquier arquitectura: la memoria. Hemos profundizado en aspectos internos, sobre cómo gestionarla en esta plataforma, así como las funciones más útiles en cada una de las tareas.
En esta ocasión vamos a "poner los pies en la tierra", y durante los próximos artículos vamos a tratar un aspecto con aplicaciones mucho más inmediatas, y no tan teórico como han podido ser los anteriores artículos: el acceso a recursos en internet.
No vamos a tratar los conceptos más internos de comunicaciones en internet, ni de los protocolos de transporte TCP/IP, ni siquiera vamos a tratar la programación de sockets, sino que nuestro objetivo se centra en un API muy concreto, desarrollado por Microsoft allá cuando salió Internet Explorer 3.0: el WinInet.
Todos estamos viendo como se está desarrollando la industria del software, y podemos percibir como cada vez toma más importancia en una aplicación el acceso a internet, la recuperación de datos y contenidos de la red y la información on-line. Ya pasaron los tiempos de aplicaciones cliente-servidor normales, de bases de datos en redes locales, de aplicaciones de escritorio simples, etc. Ahora lo más "in" son las aplicaciones que están constantemente conectadas a la red para recuperar información. Para cumplir este objetivo tenemos muchas alternativas: desde los nuevos protocolos de intercambio de ficheros peer-to-peer (como Napster, eDonkey, eMule, etc.), hasta ajustarse a protocolos más estándar como pueden ser los archiconocidos HTTP y FTP.
Esta serie de artículos que comenzamos se limita a utilizar protocolos y modos de acceso estándar, a través de un API desarrollado por Microsoft para estas tareas. Posiblemente, este modo de acceso sea el más adecuado para la mayoría de nuestras necesidades, en que sólo necesitamos acceder a contenidos en modo texto o binario. Para que os hagáis una idea de la importancia de esta tecnología, Internet Explorer, el componente TWebBrowser e incluso el propio Explorador de Windows, utilizan Wininet para acceder a los contenidos en red. De hecho, los planes de Microsoft en esta línea son muy claros: integrar los accesos a internet dentro del núcleo de Win32, de modo que el acceso a un recurso local se diferencie lo más mínimo del acceso a recursos en red (todo ello, en la plataforma .NET, por supuesto).
Introducción a WinInet
Básicamente, WinInet es un conjunto de funciones de alto nivel, para al acceso a contenidos en internet.
Estas funciones, están disponibles dentro de la librería "wininet.dll", que podréis encontrar en la carpeta de sistema. Esta librería se distribuye desde Windows 95, o con cualquier instalación del Internet Explorer a partir de la versión 3.0, así que no tendremos problemas en este aspecto. De todas formas, ciertas funciones sólo están disponibles a partir de la versión 4 del explorador de Microsoft, y otras más evolucionadas a partir de la versión 5.
La principal ventaja de su uso es que se nos ocultan los detalles de implementación de los distintos protocolos que podemos utilizar. De este modo, no necesitamos conocer la estructura de peticiones y respuestas de cada protocolo, sino que será la propia librería la encargada de convertir nuestras llamadas en peticiones en el lenguaje del protocolo adecuado.
Además de esto, cuando cambien los detalles internos del protocolo (por ejemplo si se establece una nueva versión), nuestras aplicaciones seguirán funcionando, ya que será la librería la encargada de modificar sus llamadas.
Otra de las ventajas que ofrece Wininet es que nos abstrae sobre el tipo de conexión que estamos realizando. Es decir, para acceder a los contenidos actuaremos de una forma similar estemos en una LAN o en internet, ya sea a través de proxy, conexión por modem o cualquier otro tipo. Además, podemos aprovecharnos de la configuración que haya establecido el usuario en las opciones de conexión a internet (en el Panel de control - Opciones de internet - Conexiones).
Por si fuera poco, con el API Wininet se nos permite acceder a programas y scripts desarrollados en el lado del servidor, ya sean CGI, ISAPI, ASP, PHP, J2EE, etc. Por ejemplo, supongamos que ya tenemos desarrolladas una serie de funciones, en PHP, para la validación de usuarios, y estas funciones están siendo usadas desde el sistema de login de nuestra página web. Desde nuestras aplicaciones de escritorio, podremos beneficiarnos de estos desarrollos, accediendo a través de Wininet a los scripts que ya están funcionando en el servidor. En una posible modificación de estos scripts del servidor, será beneficiado tanto el acceso desde la página web como desde la aplicación de escritorio.
Además, también nos permite gestionar las cookies del sistema, añadiendo, eliminando o manipulando nuestras propias galletas.
Y por último, una característica muy interesante: Wininet nos permite acceder de forma transparente al caché de datos que gestiona Internet Explorer. De este modo, si cierta página o archivo ha sido descargada previamente, desde nuestras aplicaciones podemos beneficiarnos de este caché, y recuperar los datos del disco duro en vez de acceder a internet.
Protocolos
Wininet, en su versión actual, soporta el acceso a contenidos en internet a través de tres protocolos: HTTP, FTP y Gopher.
No vamos a profundizar en los detalles de cada uno de ellos, porque no es nuestro objetivo, aunque sí vamos a dar una pequeña descripción de su principal objetivo:
HTTP (Hyper Text Transfer Protocol): a todo el mundo le sonará este protocolo, el más utilizado de internet. Su principal uso es el envío y recepción de información en formato texto, como pueden ser páginas HTML. Además, Wininet soporta la extensión HTTPS, para conexiones seguras.
FTP (File Transfer Protocol): este protocolo también es bastante utilizado, y está orientado a la transmisión de archivos binarios de gran tamaño, como pueden ser ejecutables, archivos comprimidos, imágenes, etc.
Gopher (Go-for...): Este protocolo actualmente está en desuso, aunque en los inicios de internet tuvo un papel muy importante. Su principal función fue la de catalogar y buscar contenidos a través de la red, almacenando índices con todos los recursos disponibles. Hoy en día, gracias a motores de búsqueda basados en HTTP, como Google o Yahoo, este servicio ya no es necesario.
En esta nueva serie de artículos, vamos a profundizar en los dos primeros protocolos, HTTP y FTP, ya que no creo que el protocolo Gopher vaya a ser de mucha utilidad para alguno de vosotros (para mi no lo es).
La tecnología Wininet
Wininet nació con la idea de abstraer al programador de la implementación de los protocolos de aplicación más utilizados en internet. Gracias a esta idea, podemos acceder a internet sin saber, ni siquiera, qué es un protocolo o cómo implementarlo. Para ello, se ha creado como una capa de abstracción sobre el API Winsock.
Winsock es la tecnología utilizada por Win32 para comunicar máquinas a través de los protocolos de transporte TCP/IP y UPD. Estos protocolos sólo se encargan del nivel de transporte en el modelo OSI. Por encima de esto, se encuentran los protocolos de aplicación, que son los que nos ocupan en nuestro caso: HTTP, FTP, SMTP, NNTP, etc.
Antes de la aparición del API Wininet, era obligatorio utilizar Winsock para acceder a algún recurso en internet. Eso requería un entendimiento bastante profundo del protocolo a utilizar, además de conceptos como socket, puerto, etc. Actualmente, sólo será necesario utilizar Winsock para hacer uso de protocolos no soportados por Wininet, como puede ser envío y recepción de correos electrónicos (protocolos SMTP y POP3), acceso a servidores de noticias (protocolo NNTP), etc. Tampoco podremos utilizar Wininet cuando desarrollemos una aplicación que utilice un protocolo no estándar, como pueden ser las aplicaciones de intercambio de ficheros, mensajería instantánea, etc.
Parece que la cosa se está liando, así que vamos a zanjar el tema aquí. Dejaremos los detalles sobre el protocolo TCP/IP y el API WinSock para otra ocasión. Símplemente nos basta con tener claro que nos estamos moviendo en niveles muy altos de abstracción y que los detalles internos quedan ocultos para nosotros, siendo manejados por el propio API.
En la siguiente figura podemos ver cómo viaja la información desde nuestro API Wininet hasta el servidor destino.
Nosotros tan sólo vamos a ocuparnos del primer punto, es decir: qué llamadas debemos hacer desde nuestras aplicaciones al API Wininet.
Funciones generales
Como ya hemos dicho, Microsoft ha definido un grupo de funciones de uso general, para tratar situaciones comunes a cualquier tipo de conexión, independientemente del protocolo que utilicemos.
Estas funciones se encargan, entre otras cosas, de establecer la conexión, manipular el marcado del modem, tratar cadenas de URL, mostrar diálogos de error, manipular cookies, etc.
Por cierto, se me olvidaba comentar a los programadores en Delphi, que el API Wininet lo podemos encontrar implementado en la unidad "Wininet.pas", así que será necesario añadir esta unidad en el uses.
Bueno, como todo esto puede ser bastante extenso, vamos a ir por partes.
InternetCanonicalizeUrl
Esta función nos permite convertir una cadena con una URL a su equivalente pero con caracteres seguros. Las cadenas URL no pueden contener cualquier tipo de carácter, y existe un método para buscar qué caracteres no son seguros y sustituirlos por la secuencia equivalente. El ejemplo que todos conocemos es el carácter espacio y su secuencia "%20".
Hay que tener cuidado de no utilizar esta función con una cadena previamente codificada, porque no se realiza ningún tipo de comprobación y la re-codificará de nuevo.
function InternetCanonicalizeUrl(
lpszUrl: PChar;
lpszBuffer: PChar;
var lpdwLongitudBuffer: LongWord;
dwOpciones: LongWord
): LongBool;
lpszUrl: un puntero a una cadena con la URL a codificar.
lpszBuffer: un puntero a una cadena donde se almacenará la URL resultante. Este puntero debe tener memoria previamente reservada.
lpdwLongitudBuffer: un puntero a un valor que indica la longitud de caracteres que se han reservado para lpszBuffer. Cuando la función retorna, copiará en este valor el número de caracteres copiados en lpszBuffer si la función se ejecutó correctamente, o el número de bytes necesitados si la función retornó error.
dwOpciones: una serie de banderas que configuran el comportamiento de la función:
- ICU_BROWSER_MODE: indica que se comportará como la codificación que hace el navegador, es decir, después del carácter "?" ó "#" no codificará nada.
- ICU_NO_ENCODE: No codifica nada.
- ICU_DECODE: convierte todas las secuencias codificadas (%XX) en su correspondiente cadena decodificada. Para usar esta bandera debe hacerse en combinación con ICU_NO_ENCONDE ya que en caso contrario, no tendrá efecto.
- ICU_ENCODE_PERCENT: Codifica los signos de porcentaje, que por defecto no son codificados. Sólo está disponible a partir de IE 5.
- ICU_ENCODE_SPACES_ONLY: Sólo codifica los espacios.
La función retorna TRUE o FALSE dependiendo de su éxito.
Si utilizamos esta función a partir de IE4, la bandera ICU_BROWSER_MODE se utilizará siempre, por lo que si queremos codificar la cadena completa, debemos recurrir a otras funciones, como UrlCanonicalize de la librería shlwapi.dll
InternetCreateUrl
Esta función hace la operación inversa a la anterior, es decir: nos compone una cadena de URL a partir de sus componentes individuales.
function InternetCreateUrl(
var lpComponentesUrl: URL_COMPONENTS;
dwOpciones: LongWord;
lpszUrl: PChar;
var dwLongitudUrl: LongWord
): LongBool;
No voy a entrar en más detalles, porque los parámetros se usan del mismo modo que con InternetCrackUrl, pero de forma inversa. Sólo hay que decir, que dentro de la estructura URL_COMPONENTS, se ignorarán aquellos componentes cuyo puntero se haya establecido a nil. Hay que ser cuidadoso también con la manipulación de los punteros a carácter, ya que es muy normal equivocarse.
La función retornará FALSE en caso de error y GetLastError nos retorna ERROR_INSUFFICIENT_BUFFER si el buffer lpszUrl es demasiado pequeño, o si hemos cometido algún error manipulando los punteros de caracteres.
Funciones de estado de la conexión
Uno de los principales problemas a la hora de conectarnos a internet, suele ser los distintos tipos de configuración y la comprobación de la conexión. Hasta ahora, podíamos apoyarnos en el API RAS (Remote Access Service) para consultar la configuración del módem, aunque a partir de Wininet, se nos ofrece una serie de funciones para estas tareas.
Antes de empezar, hay que dejar claro que sólo hay una manera de comprobar al 100% la existencia una conexión: intentando acceder a un recurso remoto (por ejemplo con un ping), pero esto suele ser una tarea lenta, en la que tenemos que dejar que se agote un tiempo de espera (timeout), para asegurarnos de que no hay nadie al otro lado escuchando nuestras peticiones. Para evitar este tiempo de espera, existen otras funciones que intentan averiguar el estado de conexión o la posibilidad de acceso a internet, sin intentar realizar una petición a la red. Concretamente se considera que un equipo no tiene conexión a internet cuando no tiene ni tarjeta de red ni módem configurado. Estas son las posibilidades, según la gente de Microsoft:
IsNetworkAlive / IsDestinationReachable: estas funciones sólo están disponibles si tenemos instalada cualquier versión de Internet Explorer a partir de la 5. Nos permiten comprobar una tipo de conexión o incluso hacer un ping a una URL determinada para garantizar la conexión. El principal inconveniente es que estas funciones no permiten la existencia de firewalls en la conexión, por lo que muchas redes de empresa no permitirían su uso.
RasEnumConnections: para conexión vía módem, podemos utilizar esta función del API RAS que nos permite consultar la configuración de los módems que tengamos instalados en nuestro sistema.
InternetAttemptConnect: esta función nos permite hacer un primer intento de conexión a internet. Si el usuario configuró su sistema como con "auto-marcado" (Panel de control - Opciones de internet - Conexiones - Marcar siempre la conexión predeterminada), esta función mostrará la ventana de marcado del módem. Si este intento falla, se activará la bandera global de conexión "Trabajar sin conexión".
InternetCheckConnection: según la documentación oficial, esta función intenta acceder a una URL dada haciendo un ping. La práctica (y sus propios autores) nos dice que no funciona, y que debe evitarse su uso (un buen trabajo de Microsoft).
InetIsOffline: esta función está exportada en la librería URL.DLL y según la documentación, nos indica que estamos conectados si la conexión está activa o si todavía no ha habido ningún intento de conexión. Por si fuera poco, la función nos informa de que estamos conectados símplemente si hay una tarjeta de red configurada en el sistema, aunque el cable esté desconectado. Por todo esto, se desaconseja el uso de esta función.
InternetGetConnectedState: a partir de IE 4 se nos ofrece esta función que nos permite distinguir configuraciones típicas como conexión por módem o conexión vía LAN, aunque no gestiona bien configuraciones complejas como LAN + router con auto-marcado. Además, esta función es capaz de retornarnos el valor de la bandera "Trabajar sin conexión".
La bandera "Trabajar sin conexión": también a partir de IE 4, se gestiona una variable global (almacenada en el registro) que nos indica si debemos trabajar con o sin conexión a internet. Esta bandera suele actuar de un modo complementario con el resto de funciones y su valor puede consultarse o modificarse a través de la función InternetQueryOption. Para asegurarnos que pasamos a un estado "Trabajar con conexión", debemos llamar a la función InternetGoOnline. Esta bandera, junto con el uso de la función InternetGetConnectedState, parece ser el método más fiable de comprobar la conexión (si intentar un acceso a la red), de hecho, es el método que utiliza Microsoft para sus aplicaciones (IE, Outlook, etc.).
Ahora vamos a describir con más profundidad la sintaxis de algunas de estas funciones:
IsNetworkAlive
Esta función sólo la tendremos disponible a partir de IE 5, por lo que debemos asegurarnos de su existencia antes de usarla. Para ellos, lo mejor que podemos hacer es una carga dinámica de la librería Wininet.dll y de esta función. Para aprender más sobre la carga de librerías, podéis consultar el artículo de C++ Builder sobre "Librerías de enlace dinámico", aparecido en el número 4 de la revista Síntesis.
function IsNetworkAlive(
var dwTipoConexión: LongWord
): LongBool;
Esta función retornará TRUE o FALSE dependiendo si considera al sistema preparado para establecer una conexión a internet, según el método indicado en el parámetro. Hay que recordar que esta función no detectará una conexión correcta detrás de firewalls corporativos. Los valores permitidos para dwTipoconexión son:
- NETWORK_ALIVE_LAN: el sistema cuenta con tarjeta de red y conexión a red local.
- NETWORK_ALIVE_WAN: el sistema cuenta con una conexión remota activa.
InternetAttemptConnect
Esta función se usa para hacer el primer intento de conexión, y permitir al sistema que marque el módem si así está configurado. La función es muy sencilla:
function InternetAttemptConnect(
dwReservado: LongWord
): LongWord;
Como parámetro debemos pasar siempre un 0, y la función nos retornará el valor ERROR_SUCCESS si todo ha ido bien, o un código de error en caso contrario.
Hay que tener en cuenta que esta función nos abrirá la ventana de marcado del módem, si hemos configurado la opción de configuración del acceso telefónico a redes (en "Panel de control - Opciones de internet - Conexiones"), como "Marcar siempre la conexión predeterminada". Si en dicha ventana se pulsa el botón cancelar o "Trabajar sin conexión", se activará esta bandera global.
InternetGetConnectedState
Esta función nos consultará el estado de conexión de nuestro sistema.
function InternetGetConnectedState(
lpdwTipoConexion: PLongWord;
dwReservado: LongWord
): LongBool;
El segundo argumento es un valor de 32 bits que debe valer siempre 0, y el primer es un puntero a otro valor de 32 bits que contendrá el tipo de conexión. Los distintos valores combinados que se retornarán, son:
- INTERNET_CONNECTION_CONFIGURED: indica que hay algún enlace a internet configurado, pero no es seguro que esté activo en este momento.
- INTERNET_CONNECTION_LAN: se utiliza una red local para acceder a internet.
- INTERNET_CONNECTION_MODEM: se utiliza un módem para acceder a internet.
- INTERNET_CONNECTION_OFFLINE: la bandera de "Trabajar sin conexión" está activada, valor que puede haber adquirido desde cualquier otra aplicación que acceda a ella.
- INTERNET_CONNECTION_PROXY: se utiliza un proxy para acceder a internet.
- INTERNET_RAS_INSTALLED: El sistema tiene algún sistema de acceso remoto configurado.
La función retornará TRUE o FALSE, dependiendo del éxito en su ejecución.
Como siempre, se retornará TRUE o FALSE dependiendo de si el usuario ha pulsado en el botón "Conectar" o "Seguir desconectado" de la ventana.
El segundo parámetro será el descriptor de la ventana padre, al igual que en la función InternetGoOnline. El primer parámetro será un valor dentro de los siguientes posibles:
Esta función, como todas las demás, retorna un valor boolean indicando el éxito de la ejecución.
Funciones para comer galletas
Todos sabemos lo que son estas pequeñas galletas (cookies) que últimamente las vemos aparecer por cualquier lado. Para los olvidadizos, diremos que una cookie es un pequeño archivo almacenado por una página web en nuestro disco duro, para grabar en él datos de configuración, preferencias de usuario, contraseñas, etc. De este modo, la página en cuestión es capaz de reconocernos al entrar en la página, o saber si ha habido cambios desde nuestra última visita.
Desde el propio API Wininet también es posible gestionar estos pequeños archivos, sin tener que recurrir al navegador, Javascript, CGI o cualquier otro sistema de programación web.
Las cookies están asociadas a una URL concreta, así que por cada URL podremos tener un sólo archivo de configuración, y dentro de él, uno o varios pares de datos "variable-valor".
Existen dos tipos de cookies, las que tienen fecha de caducidad, y las que se eliminan al terminar una sesión de internet.
Las primeras (llamadas cookies persistentes) deben crearse indicando la fecha a partir de la cual dejan de ser válidas.
Las segundas (llamadas cookies de sesión) se crean sin ninguna fecha, y no se almacenarán en el disco duro, sino que permanecen en memoria hasta el final del proceso que las creó.
Las funciones para manipular las cookies son sencillas: