web-dev-qa-db-fra.com

Comment le serveur WebSocket traite-t-il plusieurs demandes de connexion entrantes?

Selon ici :

L'en-tête de mise à niveau HTTP demande au serveur de basculer le protocole de couche d'application de HTTP vers le protocole WebSocket.

La prise de contact du client a établi une connexion HTTP sur TCP entre IE10 et le serveur. Une fois que le serveur a renvoyé sa réponse 101, le protocole de couche d'application bascule de HTTP vers WebSockets, qui utilise la connexion précédemment établie TCP.

HTTP est complètement absent à ce stade. Grâce au protocole WebSocket léger, les messages peuvent désormais être envoyés ou reçus par les deux extrémités à tout moment.

Donc, si j'ai bien compris, une fois que le premier client a terminé la négociation avec le serveur, le port 80 du serveur sera monopolisé par le protocole WebSocket. Et le HTTP ne fonctionne plus sur le port 80 .

Alors, comment le deuxième client pourrait-il échanger une poignée de main avec le serveur? Après que toutes les négociations entre WebSocket soient au format HTTP.

AJOUTER 1

Merci pour toutes les réponses jusqu'à présent. Ils sont vraiment utiles.

Maintenant, je comprends que le port 80 du même serveur est partagé par plusieurs connexions TCP. Et ce partage est tout à fait correct car TCP les connexions sont identifiées par un tuple à 5 éléments comme indiqué par Jan-Philip Gehrcke.

J'aimerais ajouter quelques réflexions.

WebSocket et HTTP ne sont que des protocoles au niveau de l'application. Habituellement ils utilisent tous deux le protocole TCP comme transport.

Pourquoi choisir le port 80?

La conception WebSocket choisit intentionnellement le port 80 du serveur pour à la fois la poignée de main et la communication suivante. Je pense que le concepteur veut établir une communication WebSocket ressemblez une communication HTTP normale du point de vue du niveau de transport (le numéro de port du serveur est toujours 80 ). Mais selon la réponse de jfriend00, Cette astuce ne trompe pas toujours les infrastructures réseau.

Comment le protocole passe-t-il de [~ # ~] http [~ # ~] à WebSocket?

A partir de RFC 6455 - protocole WebSocket

Fondamentalement, il est destiné à être aussi proche que possible d'exposer raw TCP au script, étant donné les contraintes du Web. Il est également conçu pour que ses serveurs puissent partager un port avec des serveurs HTTP. En établissant sa poignée de main comme une demande de mise à niveau HTTP valide, on pourrait conceptuellement utiliser d’autres protocoles pour établir une messagerie client-serveur, mais l’intention de WebSockets est de fournir un protocole relativement simple pouvant coexister avec les infrastructures HTTP et déployées (telles que les serveurs proxy). ) et cela est aussi proche de TCP que l’utilisation sans danger avec une telle infrastructure tient compte de considérations de sécurité, avec des ajouts ciblés pour simplifier l’utilisation et simplifier les choses simples (comme l’ajout de sémantique de message).

Donc, je pense que je me trompe sur la déclaration suivante:

La demande de prise de contact mimique demande HTTP mais la communication qui suit ne le fait pas. La demande d'établissement de liaison arrive au serveur sur le port 80. Comme c'est un port 80, le serveur la traitera avec le protocole HTTP. Et c’est pourquoi la demande de prise de contact WebSocket doit être au format HTTP. Si tel est le cas, le protocole HTTP [~ # ~] doit être rempli par [~ # ~]. peut être modifié/étendu pour reconnaître ces éléments spécifiques à WebSocket. Sinon, il ne réalisera pas qu'il devrait céder à protocole WebSocket.

Je pense que cela devrait être compris comme ceci:

La communication WebSocket commence par une requête HTTP valide valide du client au serveur. C'est donc le serveur qui suit le protocole HTTP pour analyser la demande d'établissement de liaison et identifier la demande de modification du protocole. Et c'est le serveur qui bascule le protocole. Donc, le protocole HTTP n'a pas besoin de changer. Le protocole HTTP n'a même pas besoin de connaître WebSocket.

WebSocket et Comet

WebSocket est donc différent des technologies Comet en ce sens que WebSoket ne se limite pas au domaine HTTP actuel pour résoudre le problème de la communication bidirectionnelle.

AJOUTER 2

Une question connexe: Comment un navigateur établit-il une connexion avec un serveur Web sur un port 80? Détails?

52
smwikipedia

Les autres réponses sont déjà utiles. Je tiens à souligner que votre question est très bonne et je souhaite y répondre du point de vue impliquant listen() et accept(). Le comportement de ces deux appels système devrait suffire à répondre à votre question.

Vous êtes intéressé par le fonctionnement de TCP/IP!

Pour la partie principale de la question, il n'y a vraiment aucune différence selon HTTP ou WebSocket: le point commun est TCP sur IP et cela suffit pour répondre à votre question. Néanmoins, vous méritez une réponse de comment WebSocket se rapporte à TCP (j'ai essayé de développer cela un peu plus ici ): l'envoi d'une requête HTTP nécessite une connexion TCP/IP établie entre deux parties. Dans le cas d'un navigateur Web simple/scénario de serveur Web

  1. en premier lieu, une connexion TCP est établie entre les deux (initiée par le client)
  2. puis une requête HTTP est envoyée via ça TCP connexion (du client au serveur)
  3. puis une réponse HTTP est envoyée via la connexion même TCP (dans l'autre sens, du serveur au client)

Après cet échange, la connexion sous-jacente TCP n'est plus nécessaire et est généralement détruite/déconnectée. Dans le cas d'une demande de mise à niveau HTTP, la connexion sous-jacente TCP juste continue à vivre et la communication WebSocket passe par la même connexion TCP qui a été créée initialement (étape (1) ci-dessus)).

Comme vous pouvez le constater, la seule différence entre WebSocket et HTTP standard est un commutateur dans un protocole de haut niveau (de HTTP à WebSocket), sans changer le canal de transport sous-jacent (une connexion TCP/IP). ).

Comment gérer plusieurs tentatives de connexion IP via le même socket? Comment?

C’est un sujet que jadis me débattais et que beaucoup ne comprenaient pas. Cependant, le concept est en réalité très simple lorsque l’on comprend le fonctionnement des appels système de base liés aux sockets fournis par le système d’exploitation.

Premièrement, il faut comprendre qu'une connexion IP est uniquement définie par cinq informations:

IP: PORT de la machine A et IP: PORT de la machine B et le protocole (TCP ou UDP)

Maintenant, socket On pense souvent que les objets représentent une connexion. Mais ce n'est pas tout à fait vrai. Ils peuvent représenter différentes choses: ils peuvent être actifs ou passifs. Un objet socket en mode passif/listen fait quelque chose de très spécial, et il est important de répondre à votre question. http://linux.die.net/man/2/listen dit:

listen () marque le socket désigné par sockfd comme un socket passif, c'est-à-dire comme un socket qui sera utilisé pour accepter les demandes de connexion entrantes à l'aide de accept (2).

Nous pouvons donc créer un socket passif qui écoute les demandes de connexion entrantes. Par définition, une telle socket ne peut jamais représenter une connexion. Il n'écoute que les demandes de connexion.

Passons à accept() ( http://linux.die.net/man/2/accept ):

L'appel système accept () est utilisé avec les types de socket basés sur la connexion (SOCK_STREAM, SOCK_SEQPACKET). Il extrait la première demande de connexion sur la file d'attente des connexions en attente pour le socket d'écoute, sockfd, crée un nouveau socket connecté et renvoie un nouveau descripteur de fichier faisant référence à ce socket. Le socket nouvellement créé n'est pas à l'état d'écoute. Le socket d'origine sockfd n'est pas affecté par cet appel.

C'est tout ce que nous avons besoin de savoir pour répondre à votre question. accept() ne modifie pas l'état de la socket passive créée auparavant. Il renvoie une socket active (connectée) (une telle socket représente alors les cinq éléments d'information ci-dessus - simple, non?). Habituellement, cet objet socket actif nouvellement créé est ensuite transféré à un autre processus ou thread ou simplement à une "entité" qui s'occupe de la connexion. Après que accept() a renvoyé cet objet socket connecté, accept() peut être appelé encore sur le socket passif, et encore et encore - quelque chose de connu comme accepte la boucle . Mais appeler accept() prend du temps, n'est-ce pas? Ne peut-il pas manquer les demandes de connexion entrantes? Le texte d'aide que vous venez de citer contient d'autres informations essentielles: une file d'attente de demandes de connexion en attente! Il est géré automatiquement par la pile TCP/IP de votre système d'exploitation. Cela signifie que même si accept() ne peut traiter que les demandes de connexion entrantes une par une , aucune demande entrante ne sera manquée, même si ils arrivent à un débit élevé ou (presque) simultanément. On pourrait dire que le comportement de accept() limite la fréquence des demandes de connexion entrantes que votre machine peut gérer. Cependant, il s'agit d'un appel système rapide et, en pratique, d'autres limitations apparaissent en premier - généralement celles liées à la gestion de toutes les connexions acceptées jusqu'à présent.

66
Jan-Philip Gehrcke

La chose relativement simple que vous semblez manquer ici est que chaque connexion à un serveur (en particulier à votre serveur HTTP ici) crée sa propre socket et s'exécute ensuite sur ce socket. Ce qui se passe sur une socket est complètement indépendant de ce qui se passe sur toute autre socket actuellement connectée. Ainsi, lorsqu'un socket est commuté sur le protocole webSocket, cela ne change pas ce qu'il advient des autres connexions de socket actuelles ou entrantes. Ceux-ci décident eux-mêmes comment ils seront traités.

Ainsi, les sockets ouverts peuvent utiliser le protocole webSocket, alors que les autres connexions entrantes peuvent être des requêtes HTTP normales ou des requêtes pour créer une nouvelle connexion webSocket.

Donc, vous pouvez avoir ce type de séquence:

  1. Le client A se connecte au serveur sur le port 80 avec une requête HTTP pour établir une connexion webSocket. Ce processus crée un socket entre les deux.
  2. Le serveur répond oui à la demande de mise à niveau vers webSocket et le client et le serveur commutent le protocole pour ce socket uniquement vers le webSocket protocole.
  3. Le client A et le serveur commencent à échanger des paquets en utilisant le protocole webSocket et continuent ainsi pendant plusieurs heures.
  4. Le client B se connecte au même serveur sur le port 80 avec une requête HTTP normale. Ce processus crée un nouveau socket entre les deux.
  5. Le serveur voit que la requête entrante est une requête HTTP normale et envoie la réponse.
  6. Lorsque le client B reçoit la réponse, le socket est fermé.
  7. Le client C se connecte au même serveur sur le port 80 avec une requête HTTP pour effectuer une mise à niveau vers un webSocket.
  8. Le serveur répond oui à la demande de mise à niveau vers webSocket et le client et le serveur commutent le protocole pour ce socket uniquement vers le webSocket protocole.
  9. À ce stade, deux sockets ouverts utilisant le protocole webSocket peuvent communiquer et le serveur accepte toujours de nouvelles connexions pouvant être des demandes HTTP normales ou des demandes de mise à jour du protocole webSocket.

Ainsi, le serveur accepte toujours de nouvelles connexions sur le port 80. Ces nouvelles connexions peuvent être des demandes HTTP normales ou des demandes HTTP constituant une demande de mise à niveau vers le protocole webSocket (et donc de démarrer une connexion webSocket). Et, pendant tout ce temps, les connexions webSocket déjà établies communiquent via leur propre socket en utilisant le protocole webSocket.

Le schéma de connexion et de communication webSocket a été conçu avec soin pour présenter les caractéristiques suivantes:

  1. Aucun nouveau port n'était requis. Le port entrant (le plus souvent le port 80) pourrait être utilisé à la fois pour les requêtes HTTP normales et pour la communication webSocket.
  2. Comme aucun nouveau port n'était requis, les modifications de pare-feu ou autre infrastructure de réseau n'étaient "généralement" pas nécessaires. Il se trouve que ce n’est pas toujours le cas, car certains serveurs mandataires ou caches qui attendent du trafic HTTP doivent parfois être modifiés pour gérer (ou éviter) le trafic du protocole webSocket.
  3. Le même processus serveur pourrait facilement gérer les requêtes HTTP et les requêtes webSocket.
  4. Les cookies HTTP et/ou d'autres moyens d'authentification basés sur HTTP pourraient être utilisés lors de la configuration d'une connexion webSocket.

Réponses à vos autres questions:

1) Pourquoi choisir 80 comme port par défaut? Le concepteur souhaite-t-il que la communication WebSocket ressemble à une communication HTTP normale du point de vue du niveau de transport? (c’est-à-dire que le port du serveur est le bon vieux 80).

Oui, voir mes points 1-4 immédiatement ci-dessus. Les webSockets peuvent être établis sur des canaux HTTP existants, ils ne nécessitent donc généralement aucune modification de l'infrastructure réseau. J'ajouterais à ces points qu'aucun nouveau serveur ou processus serveur n'est requis, car un serveur HTTP existant peut simplement bénéficier de la prise en charge de webSocket.

2) J'essaie d'imaginer comment le transfert de protocole se produit sur le serveur. J'imagine qu'il existe différents modules logiciels pour la gestion du trafic HTTP ou WebSocket. Le premier serveur utilise le module HTTP pour gérer les requêtes HTTP normales. Lorsqu'il trouvera une demande de mise à niveau, il utilisera le module WebSocket.

Différentes architectures de serveur gèrent différemment la division entre les paquets webSocket et les requêtes HTTP. Dans certains cas, les connexions webSocket peuvent même être transférées vers un nouveau processus. Dans d'autres cas, il peut s'agir simplement d'un gestionnaire d'événements différent dans le même processus qui est enregistré pour le trafic de paquets entrant sur le socket qui est maintenant basculé sur le protocole webSocket. Cela dépend entièrement de l'architecture du serveur Web et de la manière dont il choisit de traiter le trafic webSocket. Un développeur qui implémente l'extrémité serveur d'une application webSocket sélectionnera probablement une implémentation webSocket existante compatible avec l'architecture de son serveur Web, puis écrira un code fonctionnant dans ce cadre.

Dans mon cas, j'ai sélectionné la bibliothèque socket.io qui fonctionne avec node.js (qui est l'architecture de mon serveur). Cette bibliothèque me fournit un objet qui prend en charge les événements pour la connexion récente de WebSockets, puis un ensemble d'autres événements pour lire les messages entrants ou envoyer des messages sortants. Les détails de la connexion webSocket initiale sont tous gérés par la bibliothèque et je n'ai pas à m'en soucier. Si je veux exiger une authentification avant que la connexion ne soit établie, la bibliothèque socket.io me permet de la connecter. Je peux alors recevoir des messages de n’importe quel client, envoyer des messages à un client unique ou diffuser des informations à tous les clients. Je l'utilise principalement pour la diffusion afin de garder certaines informations dans une page Web "en direct" afin que l'affichage de la page Web soit toujours à jour. Chaque fois que la valeur change sur le serveur, je diffuse la nouvelle valeur à tous les clients connectés.

19
jfriend00

C'est un concept assez simple, alors laissez-moi essayer de le décrire en termes simples au cas où une autre âme perdue voudrait le saisir sans avoir à lire toutes ces longues explications.

Connexions multiples

  1. Le serveur Web commence à écouter les connexions. Ça arrive:

    • Le processus principal du serveur Web ouvre une socket passive dans l'état listen sur le port 80, par exemple. 9.9.9.9:80 (9.9.9.9 est l’adresse IP du serveur et 80 est le port).
  2. Le navigateur demande au port 80 sur le serveur. Ça arrive:

    • Le système d'exploitation (bref système d'exploitation) alloue un port sortant aléatoire au client, par exemple: 1.1.1.1:6747 (1.1.1.1 est l'IP du client et 6747 est le port aléatoire).

    • Le système d'exploitation envoie un paquet de données avec l'adresse source 1.1.1.1:6747 et adresse de destination 9.9.9.9:80. Il passe par divers routeurs et commutateurs et arrive au serveur de destination.

  3. Le serveur reçoit le paquet. Ça arrive:

    • Le système d'exploitation du serveur voit que l'adresse de destination du paquet est l'une de ses propres adresses IP et, en fonction du port de destination, la transmet à l'application associée au port 80.

    • Le processus principal du serveur Web accepte la connexion en créant un nouveau socket actif. Ensuite, il lance généralement un nouveau processus enfant qui reprend le socket actif. Le socket passif reste ouvert pour accepter les nouvelles connexions entrantes.

Maintenant, chaque paquet envoyé du serveur au client aura ces adresses:

  • la source: 9.9.9.9:1553; destination: 1.1.1.1:80

Et chaque paquet envoyé du client au serveur aura ces adresses:

  • la source: 1.1.1.1:80; destination: 9.9.9.9:1553

HTTP -> Poignée de main WebSocket

HTTP est un protocole textuel. Voir le HTTP wiki pour la liste des commandes disponibles. Le navigateur envoie l'une de ces commandes et le serveur Web répond en conséquence.

WebSocket n'est pas basé sur HTTP. Il s'agit d'un protocole binaire dans lequel plusieurs flux de messages peuvent être envoyés simultanément dans les deux sens (mode duplex intégral). De ce fait, il ne serait pas possible d'établir une connexion WebSocket directement sans introduire un nouveau standard HTTP, comme par exemple HTTP/2 . Mais cela ne serait possible que si:

  1. WebSocket supporté requêtes/verbes HTTP

  2. Il y avait un nouveau port dédié différent de 80 pour la communication spécifique à WebSocket.

Le premier n’était pas visé par le protocole et le second casserait l’infrastructure Web existante. Etant donné qu'un client/navigateur peut établir plusieurs connexions HTTP avec le même serveur, basculer certaines d'entre elles de HTTP à WebSocket est le meilleur des deux mondes - conservez le même port 80 mais autorise un protocole différent de HTTP. Le changement se fait par protocole de communication initié par le client.

1
Greg