web-dev-qa-db-fra.com

Chunking WebSocket Transmission

depuis que j'utilise des connexions WebSocket sur des bases plus régulières, j'étais intéressé par la façon dont les choses fonctionnent sous le capot. J'ai donc fouillé dans les documents de spécifications sans fin pendant un certain temps, mais jusqu'à présent, je n'ai vraiment rien trouvé sur la segmentation du flux de transmission lui-même .

Le protocole WebSocket l'appelle trames de données (qui décrit le flux de données pur, donc il est aussi appelé trames non contrôlées ). Pour autant que je comprenne la spécification, il n'y a pas de longueur maximale définie et pas de valeur MTU (unité de transfert maximale) définie, ce qui signifie qu'une seule trame de données WebSocket peut contenir, par spécification (!), Une quantité infinie de données (veuillez me corriger si je me trompe ici, je suis encore étudiant à ce sujet).

Après avoir lu cela, j'ai instantanément configuré mon petit nœud serveur WebSocket. Comme j'ai une forte histoire Ajax (également sur le streaming et Comet), mes attentes étaient à l'origine comme " là-bas doit être une sorte de mode interactif pour lire les données pendant leur transfert ". Mais je me trompe, n'est-ce pas?

J'ai commencé petit, avec 4kb de données.

serveur

testSocket.emit( 'data', new Array( 4096 ).join( 'X' ) );

et comme prévu, cela arrive sur le client comme un bloc de données

client

wsInstance.onmessage = function( data ) {
    console.log( data.length ); // 4095
};

j'ai donc augmenté la charge utile et je m'attendais à nouveau à ce qu'à un moment donné, le gestionnaire onmessage côté client se déclenche de manière répétée, ce qui réduit effectivement la transmission. Mais à mon grand choc, cela ne s'est jamais produit ( node-server , testé sur firefox , chrome et safari côté client). Ma plus grande charge utile était 80 Mo

testSocket.emit( 'data', new Array( 1024*1024*80 ).join( 'X' ) );

et il est toujours arrivé en un seul gros morceau de données sur le client. Bien sûr, cela prend un certain temps même si vous avez une assez bonne connexion. Les questions ici sont

  • existe-t-il une possibilité de segmenter ces flux, similaire au mode XHR readyState3 ?
  • Existe-t-il une limite de taille pour une seule trame de données WS ?
  • les websockets ne sont-ils pas censés transférer de telles charges utiles? (ce qui me ferait me demander à nouveau pourquoi il n'y a pas de taille max définie)

Je pourrais toujours regarder du mauvais point de vue sur les WebSockets, probablement le besoin d'envoyer de grandes quantités de données n'est tout simplement pas là et vous devriez morceler/diviser logiquement toutes les données vous-même avant de les envoyer?

43
jAndy

Tout d'abord, vous devez différencier le protocole WebSocket et le WebSocket [~ # ~] api [~ # ~ ] dans les navigateurs .

Le protocole WebSocket a une limite de taille de trame de 2 à 63 octets, mais un message WebSocket peut être composé d'un nombre illimité de trames.

L'API WebSocket dans les navigateurs n'expose pas une API basée sur des trames ou en streaming, mais uniquement une API basée sur des messages. La charge utile d'un message entrant est toujours complètement mise en mémoire tampon (dans l'implémentation WebSocket du navigateur) avant de le fournir à JavaScript.

Les API d'autres implémentations WebSocket peuvent fournir un accès basé sur des trames ou en continu à la charge utile transférée via le protocole WebSocket. Par exemple, AutobahnPython le fait. Vous pouvez en savoir plus dans les exemples ici https://github.com/tavendo/AutobahnPython/tree/master/examples/twisted/websocket/streaming .

Divulgation: Je suis l'auteur original d'Autobahn et je travaille pour Tavendo.

Plus de considérations:

Tant qu'il n'y a pas d'API frame/streaming dans l'API JS WebSocket du navigateur, vous ne pouvez recevoir/envoyer que des messages WS complets.

Une seule connexion WebSocket (simple) ne peut pas entrelacer la charge utile de plusieurs messages. Ainsi, si vous utilisez des messages volumineux, ceux-ci sont livrés dans l'ordre et vous ne pourrez pas envoyer de petits messages entre les deux alors qu'un gros message est toujours à la volée.

Il y a une extension WebSocket à venir (les extensions sont un mécanisme intégré pour étendre le protocole): le multiplexage WebSocket. Cela permet d'avoir plusieurs connexions WebSocket (logiques) sur une seule connexion TCP TCP sous-jacente, ce qui présente de multiples avantages.

Remarque également: vous pouvez ouvrir plusieurs connexions WS (sur différents TCP sous-jacents) à un seul serveur cible à partir d'une seule page JS/HTML aujourd'hui .

Remarquez aussi: vous pouvez vous "découper" vous-même dans la couche application: envoyez vos trucs dans des messages WS plus petits et réassemblez-vous.

Je suis d'accord, dans un monde idéal, vous auriez un API message/frame/streaming dans le navigateur plus le multiplexage WebSocket. Cela donnerait tout le pouvoir et la commodité.

80
oberstet

RFC 6455 section 1.1 :

Voici ce que le protocole WebSocket fournit: [...] une alternative à l'interrogation HTTP pour la communication bidirectionnelle d'une page Web à un serveur distant .

Comme indiqué, les WebSockets sont destinées aux communications entre une page Web et un serveur. Veuillez noter la différence entre un site Web page et un site Web navigateur. Des exemples utilisés sont les jeux par navigateur et les applications de chat, qui échangent de nombreux petits messages.

Si vous souhaitez envoyer de nombreux Mo dans un seul message, je pense que vous n'utilisez pas les WebSockets comme ils étaient destinés. Si vous souhaitez transférer des fichiers, faites-le en utilisant une Plain Old Http Request, répondez par Content-Disposition pour permettre au navigateur de télécharger un fichier.

Donc, si vous expliquez pourquoi vous souhaitez envoyer de si grandes quantités de données, quelqu'un peut peut-être vous aider à trouver une solution plus élégante que l'utilisation de WebSockets.

En outre, un client ou un serveur peut refuser des messages trop volumineux (bien que cela ne soit pas explicitement indiqué comment il refusera):

RFC 6455 section 10.4 :

Les implémentations qui ont des limitations spécifiques à l'implémentation et/ou à la plate-forme concernant la taille de trame ou la taille totale des messages après le réassemblage à partir de plusieurs trames DOIVENT se protéger contre le dépassement de ces limites. (Par exemple, un point de terminaison malveillant peut essayer d'épuiser la mémoire de son homologue ou monter une attaque par déni de service en envoyant soit une seule grande trame (par exemple, de taille 2 ** 60), soit en envoyant un long flux de petites trames qui font partie d'un message fragmenté.) Une telle implémentation DEVRAIT imposer une limite sur les tailles de trame et la taille totale du message après réassemblage à partir de plusieurs trames.

10
CodeCaster