Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Web Development

HTTP Conciso: Conexiones HTTP

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called HTTP Succinctly.
HTTP Succinctly: HTTP Messages
HTTP Succinctly: HTTP Web Architecture

Spanish (Español) translation by Jonathan Samines (you can also view the original English article)

En el último artículo exploramos los mensajes HTTP y vimos ejemplos de comandos de texto y códigos que fluyen del cliente al servidor y de regreso en una transacción. Pero, ¿cómo se mueve la información en éstos mensajes a través de la red? ¿Dónde son abiertas las conexiones de red? ¿Cuándo son las conexiones cerradas? Estas son algunas de las preguntas que éste artículo responderá al observar HTTP desde una perspectiva de bajo nivel. Pero primero, necesitaremos entender algunas de las abstracciones bajo HTTP.

Un recorrido torbellino sobre Redes

Para entender las conexiones HTTP tenemos que saber solo un poco sobre que ocurre en las capas bajo HTTP. La comunicación en red, como muchas aplicaciones, se compone de capas. Cada capa en una pila de comunicación es responsable de un específico y limitado número de responsabilidades.

Por ejemplo, HTTP es lo que nosotros llamamos un protocolo de capa de aplicación porque permite a dos aplicaciones comunicarse a través de la red. Frecuentemente una de las aplicaciones es un explorador web y la otra aplicación es un servidor web como IIS (Internet Information Services) o Apache. Vimos como los mensajes HTTP permiten al explorador solicitar recursos del servidor. Pero, la especificación HTTP no indica nada sobre como los mensajes viajan realmente a través de la red y alcanzan al servidor- éste es el trabajo de los protocolos de capas inferiores. Un mensaje de un explorador web tiene que viajar hacía abajo en una serie de capas, y cuando llega al servidor web viaja hacía arriba a través de una serie de capas para alcanzar al proceso del servicio web.

Figure 4: Protocol layers
Capas de protocolos

La capa que subyace bajo HTTP es la capa del protocolo de transporte. Casi todo el trafico HTTP viaja sobre TCP (abreviatura para Protocolo de Transmisión del Control), aunque esto no es requerido por HTTP. Cuando un usuario escribe una URL en el explorador, el explorador primero extrae el nombre del host de la URL (y el número de puerto, si existe), y abre un socket TCP especificando la dirección del servidor (derivada del nombre del host) y el puerto (el cual es 80 por defecto).

Una vez una aplicación ha abierto un socket puede empezar a escribir datos al socket. La única cosa de la que el explorador necesita preocuparse es escribir los mensajes HTTP correctamente formateados al socket. La capa de TCP acepta los datos y se asegura de que el mensaje es entregado al servidor sin pérdidas o duplicados. TCP reenviará automáticamente cualquier información que podría perderse en el tránsito, y éste es el por qué TCP es conocido como un protocolo confiable. Además de la detección de errores, TCP también proporciona el control de flujo. Los algoritmos de control de flujo en TCP se asegurarán de que el emisor no envíe datos demasiado rápido para que el receptor procese los datos. El control de flujo es importante en éste mundo de redes y dispositivos variados.

En resumen, TCP proporciona servicios vitales para la entrega exitosa de los mensajes HTTP, pero lo hace de una forma transparente por lo que la mayor parte de las aplicaciones no necesitan preocuparse sobre TCP. Como muestra la figura anterior, TCP es solo la primera capa debajo de HTTP. Despues de TCP en la capa de transporte, viene IP como protocolo de la capa de red.

IP es la abreviatura para Internet Protocol (Protocolo de Internet). Mientras que TCP es responsable  de la detección de errores, control del flujo y confiabilidad general, IP es responsable de tomar las piezas de información y moverlas a través de varios switches, routers, gateways, repetidores y otros dispositivos que mueven la información de una red a la siguiente a través del mundo. IP hace su mejor esfuerzo en entregar los datos al destino (pero no garantiza la entrega- ese es un trabajo de TCP). IP requiere que las computadores tengan una dirección (la famosa dirección IP, siendo un ejemplo 208.192.32.40). IP es también responsable de dividir los datos en paquetes (frecuentemente llamados datagramas), y algunas veces fragmentar y reensamblar éstos paquetes, para así estar optimizados para un segmento de red en particular.

Todo sobre lo que hemos hablado hasta ahora ocurre dentro de una computador, pero eventualmente éstos paquetes de IP tienen que viajar a través de un fragmento de cable, un cable de fibra óptica, una red inalambrica o un enlace satelital. Ésta es responsabilidad de la capa de enlace de datos. Una elección común de tecnología en éste punto es Ethernet. En éste nivel, los paquetes de datos se convierten en frames, y protocolos de bajo nivel como Ethernet se enfocan en los 0s y 1s, y las señales eléctricas.

Eventualmente la señal alcanza al servidor e ingresa a través de la tarjeta de red dónde el proceso es revertido. La capa de enlace de datos entrega paquetes a la capa de IP, la cual maneja los datos a través de TCP, el cual puede reensamblar los datos en el mensaje HTTP original enviando por el cliente y empujarlo al proceso del servidor web. Es una bella pieza de trabajo de ingeniería, todo hecho posible gracias a los estándares.


Solicitud HTTP rápida con Sockets y C#

Si estás preguntándote como sería escribir una aplicación que haga solicitudes HTTP, entonces el siguiente código C# es un ejemplo sencillo de cómo se vería el código. Éste código no tiene ningún manejo de errores, e intenta escribir cualquier respuesta del servidor en la ventana de consola (así que necesitarás solicitar un recurso de texto), pero funciona para solicitudes simples Una copia del siguiente código de ejemplo está disponible desde https://bitbucket.org/syncfusion/http-succintly. El nombre del ejemplo es sockets-sample.

Nota como el programa necesita buscar la dirección del servidor (utilizando Dns.GetHostEntry), y formar un mensaje HTTP apropiado con un operador GET y la cabecera de Host. La parte de la red es realmente sencillo, porque la implementación del socket y TCP se ocupan de la mayor parte del trabajo. TCP entiende, por ejemplo, como manejar múltiples conexiones al mismo servidor (todas ellas reciben números de puertos diferentes localmente). Debido a esto, dos solicitudes salientes al mismo servidor no serán confundidas y recibirán los datos destinados para cada uno.


Redes y Wireshark

Si quieres algo de de visibilidad a nivel de TCP e IP puedes instalar un programa gratuito como Wireshark (disponible para OSX y Windows desde wireshark.org). Wireshark es un analizador de red que puede mostrarte cada bit de información fluyendo a través de tus interfaces de red. Utilizando Wireshark puedes examinar el intercambio TCP, los cuales son mensajes TCP requeridos para establecer una conexión entre el cliente y servidor antes de que el mensaje HTTP real empiece a fluir. También puedes ver las cabeceras TCP  e IP (20 bytes cada una) sobre cada mensaje. La siguiente figura muestra los últimos dos pasos del intercambios, seguidos por una solicitud GET y una redirección 304.

Figure 5: Using Wireshark
Utilizando Wireshark

Con Wireshark puedes ver cuando las conexiones HTTP son establecidas y cerradas. La parte importante a tomar de todo ésto no es como el intercambio y TCP funcionan a bajo nivel, sino que HTTP depende casi complemente de TCP que se encarga de todo el trabajo duro y que TCP involucra alguna sobrecarga, como los intercambios. Así, las características de rendimiento de HTTP también dependen de las características de rendimiento de TCP, y ese es el tema para la siguiente sección.


HTTP, TCP y la Evolución de la Web

En los primeros días de la web, la mayor parte de los recursos eran textuales. Podías solicitar un documento de un servidor web, salir y leer por cinco minutos, luego solicitar otro documento. El mundo era simple.

Para la web de hoy en día, la mayoría de las páginas web requieren más de un único recurso para renderizarse completamente. Cada página en una aplicación web tiene una o más imágenes, uno o más archivos javascript, y uno o más archivos CSS. No es raro que la solicitud inicial para una página principal genere 30 o 50 solicitudes adicionales para obtener todos los otros recursos asociados con una página.

En los primeros días también era simple para un explorador establecer una conexión con un servidor, enviar una solicitud, recibir una respuesta y cerrar la conexión. Si los exploradores de hoy en día abrieran las conexiones una a la vez, y esperaran a que cada recurso se descargara completamente antes de empezar la siguiente descarga, la web se sentiría muy lenta. El Internet está lleno de latencia. Las señales tienen que viajar largas distancias y serpentean a través de las diferentes piezas de hardware. También hay algo de sobrecarga en establecer una conexión TCP. Como hemos visto en la captura de pantalla de Wireshark, existe un intercambio de tres pasos a completarse antes de que una transacción HTTP pueda empezar.

La evolución de simples documentos a páginas complejas ha requerido algo de ingenio en el uso práctico de HTTP.


Conexiones Paralelas

La mayoría de agentes de usuario (también conocidos como exploradores) no harán solicitudes en una forma serial, una a una. En cambio, abren múltiples conexiones paralelas a un servidor. Por ejemplo, cuando se descarga el HTML para una página el explorador vería dos etiquetas <img> en la página, así que el explorador abrirá dos conexiones paralelas para descargar las dos imágenes simultáneamente. El número de conexiones paralelas depende del agente del usuario y la configuración del agente.

Por un largo tiempo consideramos dos, como el número máximo de conexiones paralelas que un explorador generaría. Consideramos dos como el máximo porque el explorador más popular por muchos años- Internet Explorer (IE) 6- permitía solo dos conexiones simultáneas a un único host. IE solo estaba obedeciendo las reglas dictadas en la especificación HTTP 1.1, la cual indica:

Un único cliente de usuario NO DEBE mantener más de dos conexiones con cualquier servidor o proxy.

Para incrementar el número de conexiones paralelas, muchos sitios web utilizan algunos trucos. Por ejemplo, el límite de dos conexiones es por host, lo que quiere decir que un explorador como IE 6 felizmente haría dos conexiones paralelas a www.odetocode.com, y dos conexiones paralelas a images.odetocode.com. Hospedando imágenes en un servidor diferente, los sitios web podrían incrementar el número de descargas paralelas y hacer la carga de las páginas más rápidas (aún si los registros DNS fueron configurados para apuntar las cuatro solicitudes al mismo servidor, porque el límite de dos conexiones es por nombre de host, no por dirección IP).

Las cosas son diferentes hoy en día. La mayoría de los agentes de usuario usarán diferentes conjuntos de heurísticas cuando deciden cuántas conexiones paralelas establecer. Por ejemplo, Internet Explorer 8 abrirá ahora hasta 6 conexiones concurrentes.

La verdadera pregunta a responder es: ¿cuántas conexiones son demasiadas? Las conexiones paralelas obedecerán la ley de rendimientos decrescientes. Muchas conexiones pueden saturar y congestionar la red, particularmente cuando dispositivos móviles o redes no confiables están involucradas. Así, tener muchas conexiones puede dañar el rendimiento. También, un servidor solo puede aceptar un número finito de conexiones, así que si 100,000 exploradores crean simultáneamente 100 conexiones a un único servidor web, pueden pasar cosas malas. Tranquilo, utilizar más de una conexión por agente es mejor que descargar todo en una forma serial.

Afortunadamente, las conexiones paralelas no son la única optimización de rendimiento.


Conexiones Persistentes

En los primeros días de la web, un agente de usuario abría y cerraba una conexión por cada solicitud individual que enviaba al servidor. Esta implementación estaba de acuerdo con la idea de HTTP de ser un protocolo completamente sin estado. Conforme el número de solicitudes por página creció, también lo hizo la sobrecarga generada por los intercambios de TCP y las estructuras de datos en memoria requerida para establecer cada socket TCP. Para reducir ésta sobrecarga y mejorar el rendimiento, la especificación HTTP 1.1 sugiere que el cliente y el servidor deben implementar las conexiones persistentes, y hacer las conexiones persistentes el tipo de conexión por defecto.

Una conexión persistente se mantiene abierta después de la finalización de una transacción solicitud-respuesta. Este comportamiento deja al agente de usuario con un socket ya abierto que puede continuar utilizando para continuar haciendo solicitudes al servidor sin la sobrecarga de abrir un nuevo socket. Las conexiones persistentes también evitan la estrategia de inicio lento que es parte del control de congestiones de TCP, haciendo que las conexiones persistentes tengan un mejor rendimiento en el tiempo. En resumen, las conexiones persistentes reducen el uso de memoria, reducen el uso de CPU, reducen la congestión de red, reducen la latencia, y en general mejoran el tiempo de respuesta de una página. Pero, como todo en la vida, hay un desventaja.

Como se mencionó anteriormente, un servidor solo puede soportar un número finito de conexiones entrantes. El número exacto depende de la cantidad de memoria disponible, la configuración del software del servidor, el rendimiento de la aplicación, y de muchas otras variables. Es difícil dar un número exacto, pero generalmente hablando, si hablas de soportar miles de conexiones simultáneas, tendrás que empezar a probar para ver si un servidor soportará la carga. De hecho, muchos servidores están configurados para limitar el número de conexiones simultáneas por debajo del punto dónde el servidor fallará. La configuración es una medida de seguridad para ayudarte a prevenir ataques de denegación de servicio. Es relativamente fácil para alguien crear un programa que abra miles de conexiones persistentes a un servidor y evite que el servidor responda a clientes reales. Las conexiones persistentes son una optimización de rendimiento pero también una vulnerabilidad.

Pensando en términos de una vulnerabilidad, también tenemos que preguntarnos que tanto tiempo mantener abierta una conexión persistente. En un mundo de escalabilidad infinita, las conexiones podrían mantenerse abiertas por tanto tiempo como el agente de usuario esté ejecutándose. Pero, debido a que el servidor soporta un número finito de conexiones, la mayor parte de los servidores están configurados para cerrar una conexión persistente si está sin uso por algún período de tiempo (por ejemplo, cinco segundo en Apache). Los agentes de usuario también pueden cerrar las conexiones después de un período de tiempo sin actividad. La única forma de visualizar las conexiones cerradas es a través de un analizador de red como Wireshark.

Además de cerrar agresivamente las conexiones persistentes, la mayor parte del software de servidor web puede ser configurado para deshabilitar las conexiones persistentes. Esto es común en los servidores compartidos. Los servidores compartidos sacrifican el rendimiento para permitir tantas conexiones como sean posibles. Debido a que las conexiones persistentes son el estilo de conexión por defecto en HTTP 1.1, un servidor que no permite las conexiones persistentes tiene que incluir una cabecera Connection en cada respuesta HTTP. El siguiente código es un ejemplo.

La cabecera Connection: close es una señal para el agente de usuario que la conexión no será persistente y debería ser cerrada tan pronto como sea posible. El agente no tiene permitido hacer una segunda solicitud en la misma conexión.


Conexiones Entubadas

Las conexiones paralelas y las conexiones persistentes son ampliamente utilizadas y soportadas por clientes y servidores. La especificación HTTP también permite las conexiones entubadas, las cuales no están ampliamente soportadas ni por clientes ni servidores. En una conexión entubada, un agente de usuario puede enviar múltiples solicitudes HTTP en una conexión antes de esperar la primera respuesta. El entubamiento permite un empaquetado más eficiente de solicitudes en paquetes y puede reducir la latencia, pero no está ampliamente soportado como las conexiones paralelas y persistentes.


¿Dónde estamos?

En éste capítulo hemos visto las conexiones HTTP y hemos hablado sobre algunas de las optimizaciones de rendimiento hechas por la especificación HTTP. Ahora que hemos profundizado en los detalles de los mensajes HTTP e incluso hemos examinado las conexiones y soporte de TCP bajo el protocolo, regresaremos un paso atrás y veremos Internet desde una perspectiva más amplia.

¡Sé el primero en conocer las nuevas traducciones–sigue @tutsplus_es en Twitter!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.