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

De acuerdo a aquí :

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

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

HTTP está completamente fuera de la imagen en este punto. Utilizando el protocolo de cable WebSocket liviano, los mensajes ahora pueden enviarse o recibirse por cualquiera de los puntos finales en cualquier momento.

Entonces, según entiendo, después de que el primer cliente haya finalizado el intercambio de información con el servidor, el puerto 80 del servidor estará monopolizado por el protocolo WebSocket. Y el HTTP ya no funciona en 80 puertos .

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

ADD 1

Gracias por todas las respuestas hasta el momento. Ellos son realmente útiles.

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 están identificadas por una tupla de 5 elementos como señaló Jan-Philip Gehrcke .

Me gustaría agregar algunas ideas.

Tanto WebSocket como HTTP son simplemente protocolos de nivel de aplicación. Usualmente ambos dependen del protocolo TCP como su transporte.

¿Por qué elegir el puerto 80?

El diseño de WebSocket elige intencionalmente el puerto 80 del servidor tanto para el protocolo de enlace como para la comunicación siguiente . Creo que el diseñador desea que la comunicación WebSocket se parezca a la 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 ocurre el cambio de protocolo de HTTP a WebSocket ?

De RFC 6455 – protocolo WebSocket

Básicamente, se pretende que sea lo más cercano posible a la exposición de TCP sin formato al script como sea posible dadas las limitaciones de la Web. También está diseñado de tal manera que sus servidores pueden compartir un puerto con servidores HTTP, teniendo su saludo de mano como una solicitud válida de actualización de HTTP. Uno podría usar conceptualmente otros protocolos para establecer la mensajería 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 al TCP que sea seguro para utilizar con dicha infraestructura teniendo en cuenta las consideraciones de seguridad, con adiciones específicas para simplificar el uso y simplificar las cosas simples (como la adición de la semántica de los mensajes).

Entonces creo que estoy equivocado en la siguiente statement:

La solicitud de saludo imita la solicitud de HTTP pero la comunicación que sigue no. La solicitud de saludo llega al servidor en el puerto 80. Debido a que es de 80 puertos, el servidor lo tratará con protocolo HTTP. Y es por eso que la solicitud de protocolo 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 debe ceder el paso al protocolo WebSocket.

Creo que se debe entender así:

La comunicación WebSocket comienza con una solicitud HTTP válida de cliente a 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 que cambia el protocolo. Entonces el protocolo HTTP no necesita cambiar. El protocolo HTTP ni siquiera necesita saber sobre WebSocket.

WebSocket y Comet

Así que WebSocket es diferente de las tecnologías de Comet en que WebSoket no se limita a sí mismo dentro del reino HTTP actual para resolver el problema de la comunicación bidireccional.

ADD 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 de 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: el terreno común es TCP sobre IP y eso es suficiente para responder a su pregunta. Aún así, merece una respuesta sobre cómo WebSocket se relaciona con TCP (He intentado profundizar un poco más en esto ): el envío de una solicitud HTTP requiere una conexión TCP / IP establecida entre dos partes. En el caso de un simple navegador web / escenario de servidor web

  1. primero, se establece una conexión TCP entre ambos (iniciada por el cliente)
  2. a continuación, se envía una solicitud HTTP a través de esa conexión TCP (del cliente al servidor)
  3. luego 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 de HTTP, la conexión TCP subyacente sigue funcionando, 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 interruptor 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 bashs 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 se entiende cómo funcionan las llamadas al sistema básicas relacionadas con el socket que proporciona el sistema operativo.

En primer lugar, se debe tener en cuenta que una conexión IP se define de manera única por cinco elementos de información:

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 activos o pasivos. 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 al que se refiere 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, tal socket nunca puede representar una conexión. Solo escucha las solicitudes de conexión.

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

La llamada al sistema accept () se usa 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 el estado de escucha. El socket sockfd original 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, ¿no?). 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() ha devuelto este objeto de socket conectado, accept() puede volver a llamar en el socket pasivo, y una y otra vez, algo que se conoce como accept loop . Pero llamar a accept() lleva tiempo, ¿verdad? ¿No puede perderse las solicitudes de conexión entrantes? Hay más información esencial en el texto de ayuda que se cita: ¡hay una cola de solicitudes de conexión pendientes! Se maneja automáticamente mediante la stack TCP / IP de su sistema operativo. Esto significa que, aunque accept() solo puede ocuparse de las solicitudes de conexión entrantes una a una , ninguna solicitud entrante se perderá, incluso cuando se reciban a alta velocidad 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 que falta 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é actualmente conectado. 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 normales 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 zócalo entre los dos.
  2. El servidor responde que sí, a la actualización a la solicitud webSocket y tanto el cliente como el servidor cambian el protocolo de este socket solo al protocolo webSocket.
  3. El Cliente A y el Servidor comienzan a intercambiar paquetes usando 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 regular. Este proceso crea un nuevo zócalo 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 que sí, a la actualización a la solicitud webSocket y tanto el cliente como el servidor cambian el protocolo de este socket solo al protocolo webSocket.
  9. En este punto, hay dos sockets abiertos que usan el protocolo webSocket que se puede comunicar y el servidor sigue aceptando nuevas conexiones que pueden ser solicitudes HTTP regulares o pueden ser solicitudes de un updrade al protocolo webSocket.

Por lo tanto, en todo momento, el servidor sigue aceptando nuevas conexiones en el puerto 80 y esas nuevas conexiones pueden ser solicitudes HTTP normales 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 mediante el protocolo webSocket.

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

  1. No se requirió un nuevo puerto. El puerto de entrada (más comúnmente el puerto 80) podría usarse tanto para solicitudes HTTP normales como para comunicación webSocket.
  2. Debido a que no se requería ningún puerto nuevo, los cambios en los firewalls u otras infraestructuras de red “generalmente” no eran necesarios. Resulta que este no es siempre el caso debido a que algunos proxies o cachés que esperan tráfico HTTP pueden tener que ser modificados para manejar (o evitar) el tráfico del protocolo webSocket.
  3. El mismo proceso de 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 utilizarse durante la configuración de una conexión webSocket.

Respuestas a sus preguntas adicionales:

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

Sí, mira mis puntos 1-4 inmediatamente arriba. webSockets se puede establecer a través de canales HTTP existentes, por lo que generalmente no requieren cambios en la infraestructura de red. Agregaría a esos puntos que no se requiere ningún nuevo servidor o proceso de servidor, ya que un servidor HTTP existente simplemente puede tener compatibilidad con webSocket.

2) Estoy tratando de imaginar cómo ocurre el cambio de protocolo en el servidor. Creo 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 architectures de servidor manejarán la división entre los paquetes webSocket y las solicitudes HTTP de forma diferente. En algunas, las conexiones webSocket incluso pueden reenviarse a un nuevo proceso. En otros, puede ser simplemente un manejador de eventos diferente en el mismo proceso que se registra para el tráfico entrante de paquetes en el socket que ahora se ha cambiado al protocolo webSocket. Esto depende completamente de la architecture del servidor web y de cómo elige procesar el tráfico webSocket. Un desarrollador que implementa el extremo del servidor de una aplicación webSocket probablemente seleccionará una implementación webSocket existente que sea compatible con su architecture particular de servidor web 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 architecture de mi servidor). Esa biblioteca me proporciona un objeto que admite eventos para conectar nuevamente WebSockets 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 de socket.io tiene una manera de conectarla. Puedo recibir mensajes de cualquier cliente, enviar mensajes a cualquier cliente o transmitir información a todos los clientes. Lo uso principalmente para transmitir 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, transmito el nuevo valor a todos los clientes conectados.

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

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

Esto significa: Tras un satisfactorio intercambio de TCP, el servicio que escucha en serviceip: 80 procede a engendrar un nuevo proceso o hilo y entrega todas las comunicaciones para esa conexión (o simplemente atiende la solicitud ejecutando la callback asociada a ese evento como el asincrónico nodejs lo 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 se reproducen en todo esto, este artículo de MSDN lo deja muy claro:

El protocolo WebSocket tiene dos partes: un apretón de manos para establecer la conexión mejorada, luego la transferencia de datos real. En primer lugar, un cliente solicita una conexión websocket utilizando los encabezados “Actualizar: websocket” y “Connection: Upgrade”, junto con algunos encabezados específicos del protocolo para establecer la versión que se utiliza y configurar un saludo. El servidor, si es compatible con el protocolo, responde con los mismos encabezados “Upgrade: websocket” y “Connection: Upgrade” 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 realmente no se pretende escuchar en el puerto 80, al que solo se puede acceder gracias al reenvío transparente del servidor web. El servidor web Apache lo hace usando mod_proxy_wstunnel .

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

Lo principal aquí es: el protocolo Websocket no es HTTP. Sirve 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 necesario el requisito, 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 del cliente de la interfaz.

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

La principal ventaja es: el servicio Websocket puede ENVIAR un mensaje al cliente siempre que lo necesite (“¡uno de ustedes se ha conectado!” “¡Su equipo acaba de marcar un gol!”), En lugar de tener que esperar la PETICIÓN EXPLÍCITA del cliente para una actualización

Puede establecer una conexión persistente utilizando HTTP 1.1, pero HTTP no está destinado para nada más que para servir un conjunto de recursos A PETICIÓN y luego cerrar la conexión.

Hasta hace poco, antes de que el soporte de Websocket 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 encuesta larga 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 el servicio de actualizaciones, que es más eficiente pero aún más doloroso que un largo sondeo.

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 finaliza el protocolo de enlace TCP existe un socket TCP para que el servicio se comunique con el cliente.

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

Si aún tiene dudas sobre el traspaso del protocolo de aplicación HTTP-Websocket al momento del intercambio de conexión con Websocket y sobre cómo no tiene nada que ver con la conexión TCP subyacente, remito a la respuesta de Jan-Philip Gehrcke , que es muy clara una instructiva y probablemente lo que estabas buscando en realidad.

Es un concepto bastante simple, así que permítanme tratar de describirlo en términos simples en caso de que algún otro alma perdida quiera comprenderlo sin tener que leer todas esas largas explicaciones.

Múltiples conexiones

  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 el servidor IP y 80 es el puerto).
  2. El navegador realiza una solicitud al puerto 80 en el servidor. Esto pasa:

    • El sistema operativo (en resumen OS) asigna un puerto de salida aleatorio en el cliente, digamos: 1.1.1.1:6747 ( 1.1.1.1 es el 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 e interruptores 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 secundario, que se hace cargo del socket activo. El socket 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 -> WebSocket apretón de manos

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 se basa en HTTP. Es un protocolo binario en el que 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 soportó verbos / solicitudes HTTP

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

El primero no estaba en el scope 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 algunos de ellos de HTTP a WebSocket es lo mejor de ambos mundos: mantener el mismo puerto 80 pero permitir un protocolo diferente que HTTP. El cambio ocurre a través del protocolo de enlace iniciado por el cliente.