ws est une application simple à utiliser, rapide comme l’éclair, et testée de manière approfondie WebSocket client et serveur implémentation.
Passe la suite de tests Autobahn assez étendue : serveur,client.
Note : Ce module ne fonctionne pas dans le navigateur. Le client dans les docs est une référence à un back end ayant le rôle de client dans la communication WebSocket. Les clients des navigateurs doivent utiliser l’objet natifWebSocket
. Pour que le même code fonctionne de manière transparente sur Node.js et le navigateur, vous pouvez utiliser l’un des nombreux wrappers disponibles sur npm, commeisomorphic-ws.
Table des matières
- Protocole supporté
- Installation
- Opt.in pour les performances et la conformité aux spécifications
- Documents de l’API
- Compression de la WebSocket
- Exemples d’utilisation
- Envoi et réception de données textuelles
- Envoi de données binaires données binaires
- Serveur simple
- Serveur HTTP/S externe
- Multiples serveurs partageant un seul serveur HTTP/S
- Authentification du client
- Diffusion du serveur
- echo.websocket.org demo
- Utiliser l’API de flux Node.js streams API
- Autres exemples
- Foire aux questions
- Comment obtenir l’adresse IP du client ?
- Comment détecter et fermer les connexions interrompues ?
- Comment se connecter via un proxy ?
- Changelog
- License
Support du protocole
- HyBi drafts 07-12 (Utilisez l’option
protocolVersion: 8
) - HyBi drafts 13-17 (Défaut actuel, alternativement l’option
protocolVersion: 13
)
Installation
npm install ws
Opt-in pour la performance et la conformité aux spécifications
Il existe 2 modules optionnels qui peuvent être installés en parallèle du wsmodule. Ces modules sont des compléments binaires qui améliorent certaines opérations.Des binaires préconstruits sont disponibles pour les plateformes les plus populaires, de sorte que vous n’avez pas nécessairement besoin d’avoir un compilateur C++ installé sur votre machine.
-
npm install --save-optional bufferutil
: Permet d’effectuer efficacement des opérations telles que le masquage et le démasquage de la charge utile de données des WebSocketframes. -
npm install --save-optional utf-8-validate
: Permet de vérifier efficacement si un message contient un UTF-8 valide, comme l’exige la spécification.
Docs API
Voir /doc/ws.md
pour une documentation de type Node.js des classes ws et des fonctions utilitaires.
Compression WebSocket
Ws supporte l’extension permessage-deflate qui permet au client et au serveur de négocier un algorithme de compression et ses paramètres,puis de l’appliquer sélectivement aux charges utiles de données de chaque message WebSocket.
L’extension est désactivée par défaut sur le serveur et activée par défaut sur leclient. Elle ajoute une surcharge significative en termes de performance et de consommation de mémoire, nous suggérons donc de ne l’activer que si elle est vraiment nécessaire.
Notez que Node.js a une variété de problèmes avec la compression haute performance,où l’augmentation de la concurrence, en particulier sous Linux, peut entraîner une fragmentation catastrophique de la mémoire et un ralentissement des performances. Si vous avez l’intention d’utiliserpermessage-deflate en production, il vaut la peine de mettre en place un testreprésentatif de votre charge de travail et de vous assurer que Node.js/zlib le gérera avec des performances et une utilisation de la mémoire inacceptables.
Le réglage de permessage-deflate peut être effectué via les options définies ci-dessous. Vous pouvez également utiliser zlibDeflateOptions
et zlibInflateOptions
, qui est passé directement dans la création des flux bruts deflate/inflate.
Voir les docs pour plus d’options.
const WebSocket = require('ws');const wss = new WebSocket.Server({ port: 8080, perMessageDeflate: { zlibDeflateOptions: { // See zlib defaults. chunkSize: 1024, memLevel: 7, level: 3 }, zlibInflateOptions: { chunkSize: 10 * 1024 }, // Other options settable: clientNoContextTakeover: true, // Defaults to negotiated value. serverNoContextTakeover: true, // Defaults to negotiated value. serverMaxWindowBits: 10, // Defaults to negotiated value. // Below options specified as default values. concurrencyLimit: 10, // Limits zlib concurrency for perf. threshold: 1024 // Size (in bytes) below which messages // should not be compressed. }});
Le client n’utilisera l’extension que si elle est supportée et activée sur le serveur. Pour toujours désactiver l’extension sur le client, définissez l’optionperMessageDeflate
false
.
const WebSocket = require('ws');const ws = new WebSocket('ws://www.host.com/path', { perMessageDeflate: false});
Exemples d’utilisation
Envoi et réception de données textuelles
const WebSocket = require('ws');const ws = new WebSocket('ws://www.host.com/path');ws.on('open', function open() { ws.send('something');});ws.on('message', function incoming(data) { console.log(data);});
.
Émission de données binaires
const WebSocket = require('ws');const ws = new WebSocket('ws://www.host.com/path');ws.on('open', function open() { const array = new Float32Array(5); for (var i = 0; i < array.length; ++i) { array = i / 2; } ws.send(array);});
Serveur simple
const WebSocket = require('ws');const wss = new WebSocket.Server({ port: 8080 });wss.on('connection', function connection(ws) { ws.on('message', function incoming(message) { console.log('received: %s', message); }); ws.send('something');});
Serveur externe HTTP/S
const fs = require('fs');const https = require('https');const WebSocket = require('ws');const server = https.createServer({ cert: fs.readFileSync('/path/to/cert.pem'), key: fs.readFileSync('/path/to/key.pem')});const wss = new WebSocket.Server({ server });wss.on('connection', function connection(ws) { ws.on('message', function incoming(message) { console.log('received: %s', message); }); ws.send('something');});server.listen(8080);
Serveur multiple partageant un seul serveur HTTP/S
const http = require('http');const WebSocket = require('ws');const url = require('url');const server = http.createServer();const wss1 = new WebSocket.Server({ noServer: true });const wss2 = new WebSocket.Server({ noServer: true });wss1.on('connection', function connection(ws) { // ...});wss2.on('connection', function connection(ws) { // ...});server.on('upgrade', function upgrade(request, socket, head) { const pathname = url.parse(request.url).pathname; if (pathname === '/foo') { wss1.handleUpgrade(request, socket, head, function done(ws) { wss1.emit('connection', ws, request); }); } else if (pathname === '/bar') { wss2.handleUpgrade(request, socket, head, function done(ws) { wss2.emit('connection', ws, request); }); } else { socket.destroy(); }});server.listen(8080);
.
Authentification du client
const http = require('http');const WebSocket = require('ws');const server = http.createServer();const wss = new WebSocket.Server({ noServer: true });wss.on('connection', function connection(ws, request, client) { ws.on('message', function message(msg) { console.log(`Received message ${msg} from user ${client}`); });});server.on('upgrade', function upgrade(request, socket, head) { // This function is not defined on purpose. Implement it with your own logic. authenticate(request, (err, client) => { if (err || !client) { socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n'); socket.destroy(); return; } wss.handleUpgrade(request, socket, head, function done(ws) { wss.emit('connection', ws, request, client); }); });});server.listen(8080);
Voir aussi l’exemple fourni utilisant express-session
.
Diffusion du serveur
Un client WebSocket diffusant à tous les clients WebSocket connectés, y compris lui-même.
const WebSocket = require('ws');const wss = new WebSocket.Server({ port: 8080 });wss.on('connection', function connection(ws) { ws.on('message', function incoming(data) { wss.clients.forEach(function each(client) { if (client.readyState === WebSocket.OPEN) { client.send(data); } }); });});
Un client WebSocket diffusant à tous les autres clients WebSocket connectés, à l’exclusion de lui-même.
const WebSocket = require('ws');const wss = new WebSocket.Server({ port: 8080 });wss.on('connection', function connection(ws) { ws.on('message', function incoming(data) { wss.clients.forEach(function each(client) { if (client !== ws && client.readyState === WebSocket.OPEN) { client.send(data); } }); });});
Démo de echo.websocket.org
const WebSocket = require('ws');const ws = new WebSocket('wss://echo.websocket.org/', { origin: 'https://websocket.org'});ws.on('open', function open() { console.log('connected'); ws.send(Date.now());});ws.on('close', function close() { console.log('disconnected');});ws.on('message', function incoming(data) { console.log(`Roundtrip time: ${Date.now() - data} ms`); setTimeout(function timeout() { ws.send(Date.now()); }, 500);});
Utiliser l’API de flux Node.js streams API
const WebSocket = require('ws');const ws = new WebSocket('wss://echo.websocket.org/', { origin: 'https://websocket.org'});const duplex = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' });duplex.pipe(process.stdout);process.stdin.pipe(duplex);
Autres exemples
Pour un exemple complet avec un client navigateur communiquant avec un serveur ws, voir le dossierexamples.
Autrement, voir les cas de test.
FAQ
Comment obtenir l’adresse IP du client ?
L’adresse IP distante peut être obtenue à partir du socket brut.
const WebSocket = require('ws');const wss = new WebSocket.Server({ port: 8080 });wss.on('connection', function connection(ws, req) { const ip = req.socket.remoteAddress;});
Lorsque le serveur s’exécute derrière un proxy comme NGINX, la norme de facto consiste à utiliser l’en-tête X-Forwarded-For
.
wss.on('connection', function connection(ws, req) { const ip = req.headers.split(/\s*,\s*/);});
Comment détecter et fermer les connexions rompues ?
Parfois, le lien entre le serveur et le client peut être interrompu d’une manière qui maintient le serveur et le client dans l’ignorance de l’état rompu de laconnexion (par exemple, en tirant sur le cordon).
Dans ces cas, les messages ping peuvent être utilisés comme un moyen de vérifier que le point de terminaison distant est toujours réactif.
const WebSocket = require('ws');function noop() {}function heartbeat() { this.isAlive = true;}const wss = new WebSocket.Server({ port: 8080 });wss.on('connection', function connection(ws) { ws.isAlive = true; ws.on('pong', heartbeat);});const interval = setInterval(function ping() { wss.clients.forEach(function each(ws) { if (ws.isAlive === false) return ws.terminate(); ws.isAlive = false; ws.ping(noop); });}, 30000);wss.on('close', function close() { clearInterval(interval);});
Les messages ping sont automatiquement envoyés en réponse aux messages ping, comme l’exige la spécification.
Tout comme l’exemple du serveur ci-dessus, vos clients pourraient aussi bien perdre la connexionsans le savoir. Vous pourriez vouloir ajouter un écouteur de ping sur vos clients pour empêcher cela. Une implémentation simple serait:
const WebSocket = require('ws');function heartbeat() { clearTimeout(this.pingTimeout); // Use `WebSocket#terminate()`, which immediately destroys the connection, // instead of `WebSocket#close()`, which waits for the close timer. // Delay should be equal to the interval at which your server // sends out pings plus a conservative assumption of the latency. this.pingTimeout = setTimeout(() => { this.terminate(); }, 30000 + 1000);}const client = new WebSocket('wss://echo.websocket.org/');client.on('open', heartbeat);client.on('ping', heartbeat);client.on('close', function clear() { clearTimeout(this.pingTimeout);});
Comment se connecter via un proxy?
Utiliser une implémentation personnalisée http.Agent
comme https-proxy-agent ousocks-proxy-agent.
Changelog
Nous utilisons les versions GitHub pour les entrées du changelog.
Licence
MIT