websockets socket nodejs http websocket spring-websocket

http - nodejs - websocket php y html5



¿Cómo el servidor WebSocket maneja múltiples solicitudes de conexión entrantes? (4)

Es un concepto bastante simple, así que déjenme intentar describirlo en términos simples en caso de que alguna otra alma perdida quiera comprenderlo sin tener que leer todas esas largas explicaciones.

Conexiones múltiples

  1. El servidor web comienza a escuchar las conexiones. Esto pasa:

    • El proceso principal del servidor web abre un socket pasivo en estado listen en el puerto 80 , por ejemplo, 9.9.9.9:80 ( 9.9.9.9 es la IP del servidor y 80 es el puerto).
  2. El navegador realiza una solicitud al puerto 80 en el servidor. Esto pasa:

    • El sistema operativo (en breve OS) asigna un puerto de salida aleatorio en el cliente, por ejemplo: 1.1.1.1:6747 ( 1.1.1.1 es la IP del cliente y 6747 es el puerto aleatorio).

    • El sistema operativo envía un paquete de datos con la dirección de origen 1.1.1.1:6747 y la dirección de destino 9.9.9.9:80 . Pasa por varios enrutadores y conmutadores y llega al servidor de destino.

  3. El servidor recibe el paquete. Esto pasa:

    • El sistema operativo del servidor ve que la dirección de destino del paquete es una de sus propias direcciones IP y, en función del puerto de destino, la pasa a la aplicación asociada con el puerto 80 .

    • El proceso principal del servidor web acepta la conexión creando un nuevo socket activo. Luego, generalmente se bifurca un nuevo proceso hijo, que se hace cargo del socket activo. El zócalo pasivo permanece abierto para aceptar nuevas conexiones entrantes.

Ahora cada paquete enviado desde el servidor al cliente tendrá esas direcciones:

  • fuente: 9.9.9.9:1553 ; destino: 1.1.1.1:80

Y cada paquete enviado desde el cliente al servidor tendrá esas direcciones:

  • fuente: 1.1.1.1:80 ; destino: 9.9.9.9:1553

HTTP -> Protocolo de enlace WebSocket

HTTP es un protocolo basado en texto. Consulte la wiki de HTTP para ver la lista de comandos disponibles. El navegador envía uno de esos comandos y el servidor web responde en consecuencia.

WebSocket no está basado en HTTP. Es un protocolo binario donde se pueden enviar múltiples flujos de mensajes en ambas direcciones al mismo tiempo (modo dúplex completo). Debido a eso, no sería posible establecer una conexión WebSocket directamente sin introducir un nuevo estándar HTTP, como por ejemplo HTTP/2 . Pero eso solo sería posible si:

  1. WebSocket admite verbos / solicitudes HTTP

  2. Había un nuevo puerto dedicado diferente al 80 para la comunicación específica de WebSocket.

El primero no estaba dentro del alcance del protocolo y el segundo rompería la infraestructura web existente. Debido a que un cliente / navegador puede establecer múltiples conexiones HTTP con el mismo servidor, cambiar algunas de HTTP a WebSocket es lo mejor de ambos mundos: mantenga el mismo puerto 80 pero permita un protocolo diferente al HTTP. El cambio ocurre a través del protocolo de enlace iniciado por el cliente.

De acuerdo a here :

El encabezado de actualización HTTP solicita que el servidor cambie el protocolo de la capa de aplicación de HTTP al protocolo WebSocket .

El protocolo de enlace del cliente estableció una conexión HTTP en TCP entre IE10 y el servidor. Después de que el servidor devuelve su respuesta 101, el protocolo de la capa de aplicación cambia de HTTP a WebSockets, que utiliza la conexión TCP establecida previamente.

HTTP está completamente fuera de la imagen en este momento. Usando el ligero protocolo de conexión WebSocket, los mensajes pueden ser enviados o recibidos por cualquier punto final en cualquier momento.

Por lo tanto, entiendo que, después de que el primer cliente haya terminado el protocolo de enlace con el servidor, el puerto 80 del servidor estará monopolizado por el protocolo WebSocket. Y el HTTP ya no funciona en el puerto 80 .

Entonces, ¿cómo podría el segundo cliente intercambiar apretón de manos con el servidor? Después de todo, el protocolo de enlace WebSocket está en formato HTTP.

AGREGAR 1

Gracias por todas las respuestas hasta el momento. Son de mucha ayuda.

Ahora entiendo que el puerto 80 del mismo servidor es compartido por múltiples conexiones TCP . Y este intercambio está totalmente bien porque las conexiones TCP se identifican mediante una tupla de 5 elementos como señaló Jan-Philip Gehrcke .

Me gustaría agregar algunos pensamientos.

Tanto WebSocket como HTTP son simplemente protocolos de nivel de aplicación. Por lo general , ambos confían en el protocolo TCP como su transporte.

¿Por qué elegir el puerto 80?

El diseño de WebSocket eligió intencionalmente el puerto 80 del servidor para el protocolo de enlace y la siguiente comunicación . Creo que el diseñador quiere hacer que la comunicación WebSocket parezca una comunicación HTTP normal desde la perspectiva del nivel de transporte (es decir, el número de puerto del servidor sigue siendo 80) . Pero según la respuesta de jfriend00 , este truco no siempre engaña a las infraestructuras de red.

¿Cómo sucede el cambio de protocolo de HTTP a WebSocket ?

Desde RFC 6455 - Protocolo WebSocket

Básicamente, se pretende que esté lo más cerca posible de exponer TCP sin procesar a un script como sea posible, dadas las restricciones de la Web. También está diseñado de tal manera que sus servidores pueden compartir un puerto con servidores HTTP, al hacer que su protocolo de enlace sea una solicitud de actualización HTTP válida. Uno podría utilizar conceptualmente otros protocolos para establecer mensajes de cliente-servidor, pero la intención de WebSockets es proporcionar un protocolo relativamente simple que pueda coexistir con HTTP e infraestructura HTTP implementada (como proxies) y que sea lo más cercano a TCP como sea seguro para usar con dicha infraestructura teniendo en cuenta las consideraciones de seguridad, con adiciones específicas para simplificar el uso y mantener las cosas simples simples (como la adición de semántica de mensajes).

Entonces creo que estoy equivocado en la siguiente declaración:

La solicitud de protocolo de enlace imita la solicitud HTTP, pero la comunicación que sigue no lo hace. La solicitud de protocolo de enlace llega al servidor en el puerto 80. Debido a que es el puerto 80, el servidor lo tratará con el protocolo HTTP. Y es por eso que la solicitud de enlace WebSocket debe estar en formato HTTP. Si es así, creo que el protocolo HTTP DEBE ser modificado / extendido para reconocer esas cosas específicas de WebSocket. De lo contrario, no se dará cuenta de que debería ceder ante el protocolo WebSocket.

Creo que debería entenderse así:

La comunicación de WebSocket comienza con una solicitud HTTP válida del cliente al servidor. Por lo tanto, es el servidor que sigue el protocolo HTTP para analizar la solicitud de protocolo de enlace e identificar la petición de cambio de protocolo. Y es el servidor el que cambia el protocolo. Entonces el protocolo HTTP no necesita cambiar. El protocolo HTTP ni siquiera necesita saber acerca de WebSocket.

WebSocket y Comet

Por lo tanto, WebSocket es diferente de las tecnologías Comet en que WebSoket no se limita dentro del dominio HTTP actual para resolver el problema de comunicación bidireccional.

AGREGAR 2

Una pregunta relacionada: ¿Cómo establece un navegador la conexión con un servidor web en el puerto 80? Detalles?


Las otras respuestas ya son útiles. Quiero señalar que su pregunta es muy buena, y quiero responderla desde el punto de vista que implica listen() y accept() . El comportamiento de estas dos llamadas al sistema debería ser suficiente para responder a su pregunta.

¡Está interesado en cómo funciona TCP / IP!

Para la parte central de la pregunta, realmente no hay diferencia dependiendo de HTTP o WebSocket: la base común es TCP sobre IP y eso es suficiente para responder a su pregunta. Aún así, usted merece una respuesta de cómo WebSocket se relaciona con TCP (he tratado de explicarlo un poco más here ): enviar una solicitud HTTP requiere una conexión TCP / IP establecida entre dos partes. En el caso de un escenario simple de navegador web / servidor web

  1. primero, se establece una conexión TCP entre ambos (iniciada por el cliente)
  2. entonces se envía una solicitud HTTP a través de esa conexión TCP (del cliente al servidor)
  3. entonces se envía una respuesta HTTP a través de la misma conexión TCP (en la otra dirección, del servidor al cliente)

Después de este intercambio, la conexión TCP subyacente ya no es necesaria y generalmente se destruye / desconecta. En el caso de una solicitud de actualización HTTP, la conexión TCP subyacente simplemente sigue viva y la comunicación WebSocket pasa por la misma conexión TCP que se creó inicialmente (paso (1) anterior).

Como puede ver, la única diferencia entre WebSocket y HTTP estándar es un cambio en un protocolo de alto nivel (de HTTP a WebSocket), sin cambiar el canal de transporte subyacente (una conexión TCP / IP).

Manejo de múltiples intentos de conexión IP a través del mismo socket, ¿cómo?

Este es un tema que una vez tuve problemas conmigo mismo y que muchos no entienden. Sin embargo, el concepto en realidad es muy simple cuando uno comprende cómo funcionan las llamadas básicas del sistema relacionadas con el socket proporcionadas por el sistema operativo.

Primero, uno debe apreciar que una conexión IP se define de manera única por cinco datos:

IP: PUERTO de la máquina A e IP: PUERTO de la máquina B y el protocolo (TCP o UDP)

Ahora, a menudo se piensa que los objetos de socket representan una conexión. Pero eso no es del todo cierto. Pueden representar cosas diferentes: pueden ser activas o pasivas. Un objeto de socket en modo pasivo / escucha hace algo muy especial, y eso es importante para responder a su pregunta. http://linux.die.net/man/2/listen dice:

listen () marca el socket referido por sockfd como un socket pasivo, es decir, como un socket que se usará para aceptar solicitudes de conexión entrantes usando accept (2).

Por lo tanto, podemos crear un socket pasivo que escuche las solicitudes de conexión entrantes. Por definición, dicho socket nunca puede representar una conexión. Solo escucha las solicitudes de conexión.

Vamos a accept() ( http://linux.die.net/man/2/accept ):

La llamada del sistema accept () se utiliza con tipos de socket basados ​​en conexión (SOCK_STREAM, SOCK_SEQPACKET). Extrae la primera solicitud de conexión en la cola de conexiones pendientes para el socket de escucha, sockfd, crea un nuevo socket conectado y devuelve un nuevo descriptor de archivo que hace referencia a ese socket. El socket recién creado no está en estado de escucha. El socket original sockfd no se ve afectado por esta llamada.

Eso es todo lo que necesitamos saber para responder a su pregunta. accept() no cambia el estado del socket pasivo creado anteriormente. Devuelve un zócalo activo (conectado) (dicho zócalo representa los cinco estados de información anteriores, simple, ¿verdad?). Por lo general, este objeto de socket activo recién creado se transfiere a otro proceso o subproceso o simplemente "entidad" que se encarga de la conexión. Después de que accept() haya devuelto este objeto de socket conectado, se puede invocar accept() nuevamente en el socket pasivo, y una y otra vez, algo que se conoce como loop de aceptación . Pero llamar a accept() lleva tiempo, ¿verdad? ¿No puede faltar las solicitudes de conexión entrantes? Hay más información esencial en el texto de ayuda recién citado: ¡hay una cola de solicitudes de conexión pendientes! Es manejado automáticamente por la pila TCP / IP de su sistema operativo. Eso significa que si bien accept() solo puede manejar las solicitudes de conexión entrantes una por una , no se perderá ninguna solicitud entrante incluso cuando estén entrando a una velocidad alta o (cuasi) simultáneamente. Se podría decir que el comportamiento de accept() limita la frecuencia de las solicitudes de conexión entrantes que su máquina puede manejar. Sin embargo, esta es una llamada rápida al sistema y, en la práctica, otras limitaciones afectan primero, generalmente aquellas relacionadas con el manejo de todas las conexiones que se han aceptado hasta ahora .


Lo relativamente sencillo que parece faltar aquí es que cada conexión a un servidor (en particular a su servidor HTTP aquí) crea su propio socket y luego se ejecuta en ese socket. Lo que sucede en un socket es completamente independiente de lo que sucede en cualquier otro socket que esté conectado actualmente. Entonces, cuando un socket se cambia al protocolo webSocket, eso no cambia lo que sucede con otras conexiones de socket actuales o entrantes. Aquellos pueden decidir por sí mismos cómo serán procesados.

Por lo tanto, los sockets abiertos pueden usar el protocolo webSocket, mientras que otras conexiones entrantes pueden ser solicitudes HTTP regulares o solicitudes para crear una nueva conexión webSocket.

Entonces, puedes tener este tipo de secuencia:

  1. El cliente A se conecta al servidor en el puerto 80 con una solicitud HTTP para iniciar una conexión webSocket. Este proceso crea un socket entre los dos.
  2. El servidor responde sí a la solicitud de actualización a webSocket y tanto el cliente como el servidor cambian el protocolo para este socket solo al protocolo webSocket.
  3. El Cliente A y el Servidor comienzan a intercambiar paquetes utilizando el protocolo webSocket y continúan haciéndolo durante las próximas horas.
  4. El cliente B se conecta al mismo servidor en el puerto 80 con una solicitud HTTP normal. Este proceso crea un nuevo socket entre los dos.
  5. El servidor ve que la solicitud entrante es una solicitud HTTP normal y envía la respuesta.
  6. Cuando el cliente B recibe la respuesta, el socket se cierra.
  7. El cliente C se conecta al mismo servidor en el puerto 80 con una solicitud HTTP para actualizar a un webSocket.
  8. El servidor responde sí a la solicitud de actualización a webSocket y tanto el cliente como el servidor cambian el protocolo para este socket solo al protocolo webSocket.
  9. En este punto, hay dos sockets abiertos que utilizan el protocolo webSocket que se pueden comunicar y el servidor aún acepta nuevas conexiones que pueden ser solicitudes HTTP regulares o solicitudes de actualización del protocolo webSocket.

Por lo tanto, en todo momento, el Servidor todavía acepta nuevas conexiones en el puerto 80 y esas nuevas conexiones pueden ser solicitudes HTTP regulares o pueden ser solicitudes HTTP que son una solicitud para actualizar al protocolo webSocket (y así iniciar una conexión webSocket). Y, mientras todo esto sucede, las conexiones webSocket que ya se han establecido se comunican a través de su propio socket utilizando el protocolo webSocket.

El esquema de conexión y comunicación webSocket fue diseñado cuidadosamente para tener estas características:

  1. No se requirió un nuevo puerto. El puerto entrante (más comúnmente el puerto 80) podría usarse tanto para solicitudes HTTP regulares como para comunicación webSocket.
  2. Debido a que no se requería un nuevo puerto, los cambios en los firewalls u otra infraestructura de red "generalmente" no eran necesarios. Como resultado, este no es siempre el caso porque algunos servidores proxy o cachés que esperan tráfico HTTP pueden tener que modificarse para manejar (o evitar) el tráfico del protocolo webSocket.
  3. El mismo proceso del servidor podría manejar fácilmente las solicitudes HTTP y las solicitudes webSocket.
  4. Las cookies HTTP y / u otros medios de autenticación basados ​​en HTTP podrían usarse durante la configuración de una conexión webSocket.

Respuestas a sus preguntas adicionales:

1) ¿Por qué elegir 80 como puerto predeterminado? ¿Quiere el diseñador hacer que la comunicación WebSocket parezca una comunicación HTTP normal desde la perspectiva del nivel de transporte? (es decir, el puerto del servidor es el viejo 80).

Sí, vea mis puntos 1-4 inmediatamente anteriores. Los webSockets se pueden establecer sobre los canales HTTP existentes, por lo que generalmente no requieren cambios en la infraestructura de red. Agregaría a esos puntos que no se requiere un nuevo servidor o proceso de servidor, ya que un servidor HTTP existente puede simplemente agregarle soporte webSocket.

2) Estoy tratando de imaginar cómo ocurre el cambio de protocolo en el servidor. Me imagino que hay diferentes módulos de software para manejar los tráficos HTTP o WebSocket. El primer servidor usa el módulo HTTP para manejar las solicitudes HTTP normales. Cuando encuentre una solicitud de actualización, cambiará para usar el módulo WebSocket.

Las diferentes arquitecturas de servidor manejarán la división entre paquetes webSocket y solicitudes HTTP de manera diferente. En algunos, las conexiones webSocket pueden incluso enviarse a un nuevo proceso. En otros, puede ser un controlador de eventos diferente en el mismo proceso que se registra para el tráfico de paquetes entrantes en el socket que ahora se ha cambiado al protocolo webSocket. Esto depende completamente de la arquitectura del servidor web y de cómo elige procesar el tráfico webSocket. Un desarrollador que implemente el extremo del servidor de una aplicación webSocket probablemente seleccionará una implementación existente de webSocket que sea compatible con su arquitectura de servidor web particular y luego escriba el código que funcione dentro de ese marco.

En mi caso, seleccioné la biblioteca socket.io que funciona con node.js (que es la arquitectura de mi servidor). Esa biblioteca me da un objeto que admite eventos para conectar WebSockets recientemente y luego un conjunto de otros eventos para leer mensajes entrantes o enviar mensajes salientes. Los detalles de la conexión webSocket inicial son manejados por la biblioteca y no tengo que preocuparme por nada de eso. Si deseo requerir autenticación antes de que se establezca la conexión, la biblioteca socket.io tiene una forma de conectarlo. Entonces puedo recibir mensajes de cualquier cliente, enviar mensajes a cualquier cliente o transmitir información a todos los clientes. Lo uso principalmente para la transmisión para mantener cierta información en una página web "en vivo" para que la visualización de la página web esté siempre actualizada. Cada vez que el valor cambia en el servidor, difundo el nuevo valor a todos los clientes conectados.


Para responder a su pregunta: se manejan conexiones Websocket y HTTP simultáneas al puerto 80 ...

¡Exactamente de la misma manera que se manejan las conexiones HTTP simultáneas al puerto 80!

Eso significa: tras un apretón de manos TCP satisfactorio, el servicio que escucha en serviceip: 80 procede a generar un nuevo proceso o subproceso y transfiere todas las comunicaciones para esa conexión (o simplemente atiende la solicitud ejecutando la devolución de llamada asociada con ese evento como asíncrono nodejs hace, como jfriend00 señaló correctamente).

Luego espera o maneja la siguiente solicitud entrante en la cola.

Si desea saber qué parte de HTTP 1.1 y la solicitud de ACTUALIZACIÓN juegan en todo esto, este here lo deja muy claro:

El protocolo WebSocket tiene dos partes: un apretón de manos para establecer la conexión actualizada, luego la transferencia de datos real. Primero, un cliente solicita una conexión websocket utilizando los encabezados "Upgrade: websocket" y "Connection: Upgrade", junto con algunos encabezados específicos del protocolo para establecer la versión que se está utilizando y configurar un protocolo de enlace. El servidor, si es compatible con el protocolo, responde con los mismos encabezados "Actualizar: websocket" y "Conexión: Actualizar" y completa el protocolo de enlace. Una vez que el apretón de manos se completa con éxito, comienza la transferencia de datos.

Solo los servicios de Websocket generalmente no están integrados en el servidor web, por lo que en realidad no están destinados a escuchar en el puerto 80, solo son accesibles a través de él gracias al reenvío transparente del servidor web. El servidor web Apache hace eso usando mod_proxy_wstunnel .

Por supuesto, también puede tener un servidor web con una implementación integrada de sockets web: Apache Tomcat , por ejemplo.

Lo principal aquí es: el protocolo Websocket no es HTTP. Sirve para un propósito diferente. Es un protocolo de comunicación de capa de aplicación independiente también construido sobre TCP (aunque TCP no es un requisito necesario, sino un protocolo de capa de transporte que se ajusta a los requisitos del protocolo de capa de aplicación Websockets).

Un servicio Websocket es un servicio PARALELO que se ejecuta junto con un servicio de servidor web.

Utiliza el protocolo Websocket, para el cual los navegadores web modernos tienen soporte, implementando la parte cliente de la interfaz.

Configura o crea un servicio Websocket para establecer conexiones persistentes, no HTTP, entre clientes Websocket (generalmente navegadores web) y ese servicio.

La principal ventaja es: el servicio Websocket puede ENVIAR un mensaje al cliente cuando lo necesite ("¡uno de ustedes amigos se ha conectado!", "Su equipo acaba de marcar un gol"), en lugar de tener que esperar la SOLICITUD explícita del cliente. para una actualización

Puede establecer una conexión persistente utilizando HTTP 1.1, pero HTTP no está destinado a otra cosa que no sea servir un conjunto de recursos A SOLICITUD y luego cerrar la conexión.

Hasta hace poco, antes de que el soporte de Websockets estuviera disponible en todos los principales navegadores, solo tenía dos opciones para implementar actualizaciones en tiempo real en una aplicación web:

  • Implementar solicitudes de sondeo largas AJAX, que es un proceso doloroso e ineficiente.

  • Usar / construir un complemento de navegador (por ejemplo, un complemento de soporte de applet de Java) para poder establecer una conexión que no sea HTTP con su servicio de actualizaciones, que es más eficiente pero incluso más doloroso que las encuestas largas.

En términos del puerto de escucha compartido del servicio (que puede ser cualquier puerto TCP, y ni siquiera tiene que estar abierto a Internet, ya que la mayoría de los servidores web admiten el reenvío transparente de conexiones de socket web), funciona exactamente como cualquier otro TCP servicio: el servicio solo lo escucha y cuando el protocolo de enlace TCP se realiza a través de un socket TCP existe para que el servicio se comunique con el cliente.

Como de costumbre, todas las conexiones a un servicio que escucha en un socket TCP particular (server_ip: service_TCP_port) se diferenciarán cuando se le asigne un par único de client_ip: client_TCP_port, el cliente_TCP_port será elegido aleatoriamente por el cliente entre sus puertos TCP disponibles).

Si todavía tiene dudas acerca de la entrega del protocolo de aplicación HTTP-> Websocket después del protocolo de enlace de conexión Websocket y de cómo no tiene nada que ver con la conexión TCP subyacente, lo remito a la respuesta de Jan-Philip Gehrcke , que es muy clara. un instructivo y probablemente lo que realmente estabas buscando.