Projekt

Obecné

Profil

Stáhnout (12.6 KB) Statistiky
| Větev: | Revize:
1
# ws: a Node.js WebSocket library
2

    
3
[![Version npm](https://img.shields.io/npm/v/ws.svg?logo=npm)](https://www.npmjs.com/package/ws)
4
[![Linux Build](https://img.shields.io/travis/websockets/ws/master.svg?logo=travis)](https://travis-ci.org/websockets/ws)
5
[![Windows Build](https://img.shields.io/appveyor/ci/lpinca/ws/master.svg?logo=appveyor)](https://ci.appveyor.com/project/lpinca/ws)
6
[![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg)](https://coveralls.io/github/websockets/ws)
7

    
8
ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
9
server implementation.
10

    
11
Passes the quite extensive Autobahn test suite: [server][server-report],
12
[client][client-report].
13

    
14
**Note**: This module does not work in the browser. The client in the docs is a
15
reference to a back end with the role of a client in the WebSocket
16
communication. Browser clients must use the native
17
[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
18
object. To make the same code work seamlessly on Node.js and the browser, you
19
can use one of the many wrappers available on npm, like
20
[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).
21

    
22
## Table of Contents
23

    
24
- [Protocol support](#protocol-support)
25
- [Installing](#installing)
26
  - [Opt-in for performance and spec compliance](#opt-in-for-performance-and-spec-compliance)
27
- [API docs](#api-docs)
28
- [WebSocket compression](#websocket-compression)
29
- [Usage examples](#usage-examples)
30
  - [Sending and receiving text data](#sending-and-receiving-text-data)
31
  - [Sending binary data](#sending-binary-data)
32
  - [Simple server](#simple-server)
33
  - [External HTTP/S server](#external-https-server)
34
  - [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server)
35
  - [Server broadcast](#server-broadcast)
36
  - [echo.websocket.org demo](#echowebsocketorg-demo)
37
  - [Other examples](#other-examples)
38
- [Error handling best practices](#error-handling-best-practices)
39
- [FAQ](#faq)
40
  - [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)
41
  - [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)
42
  - [How to connect via a proxy?](#how-to-connect-via-a-proxy)
43
- [Changelog](#changelog)
44
- [License](#license)
45

    
46
## Protocol support
47

    
48
- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
49
- **HyBi drafts 13-17** (Current default, alternatively option
50
  `protocolVersion: 13`)
51

    
52
## Installing
53

    
54
```
55
npm install ws
56
```
57

    
58
### Opt-in for performance and spec compliance
59

    
60
There are 2 optional modules that can be installed along side with the ws
61
module. These modules are binary addons which improve certain operations.
62
Prebuilt binaries are available for the most popular platforms so you don't
63
necessarily need to have a C++ compiler installed on your machine.
64

    
65
- `npm install --save-optional bufferutil`: Allows to efficiently perform
66
  operations such as masking and unmasking the data payload of the WebSocket
67
  frames.
68
- `npm install --save-optional utf-8-validate`: Allows to efficiently check if a
69
  message contains valid UTF-8 as required by the spec.
70

    
71
## API docs
72

    
73
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like docs for the ws classes.
74

    
75
## WebSocket compression
76

    
77
ws supports the [permessage-deflate extension][permessage-deflate] which enables
78
the client and server to negotiate a compression algorithm and its parameters,
79
and then selectively apply it to the data payloads of each WebSocket message.
80

    
81
The extension is disabled by default on the server and enabled by default on the
82
client. It adds a significant overhead in terms of performance and memory
83
consumption so we suggest to enable it only if it is really needed.
84

    
85
Note that Node.js has a variety of issues with high-performance compression,
86
where increased concurrency, especially on Linux, can lead to [catastrophic
87
memory fragmentation][node-zlib-bug] and slow performance. If you intend to use
88
permessage-deflate in production, it is worthwhile to set up a test
89
representative of your workload and ensure Node.js/zlib will handle it with
90
acceptable performance and memory usage.
91

    
92
Tuning of permessage-deflate can be done via the options defined below. You can
93
also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly
94
into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].
95

    
96
See [the docs][ws-server-options] for more options.
97

    
98
```js
99
const WebSocket = require('ws');
100

    
101
const wss = new WebSocket.Server({
102
  port: 8080,
103
  perMessageDeflate: {
104
    zlibDeflateOptions: {
105
      // See zlib defaults.
106
      chunkSize: 1024,
107
      memLevel: 7,
108
      level: 3
109
    },
110
    zlibInflateOptions: {
111
      chunkSize: 10 * 1024
112
    },
113
    // Other options settable:
114
    clientNoContextTakeover: true, // Defaults to negotiated value.
115
    serverNoContextTakeover: true, // Defaults to negotiated value.
116
    serverMaxWindowBits: 10, // Defaults to negotiated value.
117
    // Below options specified as default values.
118
    concurrencyLimit: 10, // Limits zlib concurrency for perf.
119
    threshold: 1024 // Size (in bytes) below which messages
120
    // should not be compressed.
121
  }
122
});
123
```
124

    
125
The client will only use the extension if it is supported and enabled on the
126
server. To always disable the extension on the client set the
127
`perMessageDeflate` option to `false`.
128

    
129
```js
130
const WebSocket = require('ws');
131

    
132
const ws = new WebSocket('ws://www.host.com/path', {
133
  perMessageDeflate: false
134
});
135
```
136

    
137
## Usage examples
138

    
139
### Sending and receiving text data
140

    
141
```js
142
const WebSocket = require('ws');
143

    
144
const ws = new WebSocket('ws://www.host.com/path');
145

    
146
ws.on('open', function open() {
147
  ws.send('something');
148
});
149

    
150
ws.on('message', function incoming(data) {
151
  console.log(data);
152
});
153
```
154

    
155
### Sending binary data
156

    
157
```js
158
const WebSocket = require('ws');
159

    
160
const ws = new WebSocket('ws://www.host.com/path');
161

    
162
ws.on('open', function open() {
163
  const array = new Float32Array(5);
164

    
165
  for (var i = 0; i < array.length; ++i) {
166
    array[i] = i / 2;
167
  }
168

    
169
  ws.send(array);
170
});
171
```
172

    
173
### Simple server
174

    
175
```js
176
const WebSocket = require('ws');
177

    
178
const wss = new WebSocket.Server({ port: 8080 });
179

    
180
wss.on('connection', function connection(ws) {
181
  ws.on('message', function incoming(message) {
182
    console.log('received: %s', message);
183
  });
184

    
185
  ws.send('something');
186
});
187
```
188

    
189
### External HTTP/S server
190

    
191
```js
192
const fs = require('fs');
193
const https = require('https');
194
const WebSocket = require('ws');
195

    
196
const server = new https.createServer({
197
  cert: fs.readFileSync('/path/to/cert.pem'),
198
  key: fs.readFileSync('/path/to/key.pem')
199
});
200
const wss = new WebSocket.Server({ server });
201

    
202
wss.on('connection', function connection(ws) {
203
  ws.on('message', function incoming(message) {
204
    console.log('received: %s', message);
205
  });
206

    
207
  ws.send('something');
208
});
209

    
210
server.listen(8080);
211
```
212

    
213
### Multiple servers sharing a single HTTP/S server
214

    
215
```js
216
const http = require('http');
217
const WebSocket = require('ws');
218

    
219
const server = http.createServer();
220
const wss1 = new WebSocket.Server({ noServer: true });
221
const wss2 = new WebSocket.Server({ noServer: true });
222

    
223
wss1.on('connection', function connection(ws) {
224
  // ...
225
});
226

    
227
wss2.on('connection', function connection(ws) {
228
  // ...
229
});
230

    
231
server.on('upgrade', function upgrade(request, socket, head) {
232
  const pathname = url.parse(request.url).pathname;
233

    
234
  if (pathname === '/foo') {
235
    wss1.handleUpgrade(request, socket, head, function done(ws) {
236
      wss1.emit('connection', ws, request);
237
    });
238
  } else if (pathname === '/bar') {
239
    wss2.handleUpgrade(request, socket, head, function done(ws) {
240
      wss2.emit('connection', ws, request);
241
    });
242
  } else {
243
    socket.destroy();
244
  }
245
});
246

    
247
server.listen(8080);
248
```
249

    
250
### Server broadcast
251

    
252
```js
253
const WebSocket = require('ws');
254

    
255
const wss = new WebSocket.Server({ port: 8080 });
256

    
257
// Broadcast to all.
258
wss.broadcast = function broadcast(data) {
259
  wss.clients.forEach(function each(client) {
260
    if (client.readyState === WebSocket.OPEN) {
261
      client.send(data);
262
    }
263
  });
264
};
265

    
266
wss.on('connection', function connection(ws) {
267
  ws.on('message', function incoming(data) {
268
    // Broadcast to everyone else.
269
    wss.clients.forEach(function each(client) {
270
      if (client !== ws && client.readyState === WebSocket.OPEN) {
271
        client.send(data);
272
      }
273
    });
274
  });
275
});
276
```
277

    
278
### echo.websocket.org demo
279

    
280
```js
281
const WebSocket = require('ws');
282

    
283
const ws = new WebSocket('wss://echo.websocket.org/', {
284
  origin: 'https://websocket.org'
285
});
286

    
287
ws.on('open', function open() {
288
  console.log('connected');
289
  ws.send(Date.now());
290
});
291

    
292
ws.on('close', function close() {
293
  console.log('disconnected');
294
});
295

    
296
ws.on('message', function incoming(data) {
297
  console.log(`Roundtrip time: ${Date.now() - data} ms`);
298

    
299
  setTimeout(function timeout() {
300
    ws.send(Date.now());
301
  }, 500);
302
});
303
```
304

    
305
### Other examples
306

    
307
For a full example with a browser client communicating with a ws server, see the
308
examples folder.
309

    
310
Otherwise, see the test cases.
311

    
312
## Error handling best practices
313

    
314
```js
315
// If the WebSocket is closed before the following send is attempted
316
ws.send('something');
317

    
318
// Errors (both immediate and async write errors) can be detected in an optional
319
// callback. The callback is also the only way of being notified that data has
320
// actually been sent.
321
ws.send('something', function ack(error) {
322
  // If error is not defined, the send has been completed, otherwise the error
323
  // object will indicate what failed.
324
});
325

    
326
// Immediate errors can also be handled with `try...catch`, but **note** that
327
// since sends are inherently asynchronous, socket write failures will *not* be
328
// captured when this technique is used.
329
try {
330
  ws.send('something');
331
} catch (e) {
332
  /* handle error */
333
}
334
```
335

    
336
## FAQ
337

    
338
### How to get the IP address of the client?
339

    
340
The remote IP address can be obtained from the raw socket.
341

    
342
```js
343
const WebSocket = require('ws');
344

    
345
const wss = new WebSocket.Server({ port: 8080 });
346

    
347
wss.on('connection', function connection(ws, req) {
348
  const ip = req.connection.remoteAddress;
349
});
350
```
351

    
352
When the server runs behind a proxy like NGINX, the de-facto standard is to use
353
the `X-Forwarded-For` header.
354

    
355
```js
356
wss.on('connection', function connection(ws, req) {
357
  const ip = req.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
358
});
359
```
360

    
361
### How to detect and close broken connections?
362

    
363
Sometimes the link between the server and the client can be interrupted in a way
364
that keeps both the server and the client unaware of the broken state of the
365
connection (e.g. when pulling the cord).
366

    
367
In these cases ping messages can be used as a means to verify that the remote
368
endpoint is still responsive.
369

    
370
```js
371
const WebSocket = require('ws');
372

    
373
function noop() {}
374

    
375
function heartbeat() {
376
  this.isAlive = true;
377
}
378

    
379
const wss = new WebSocket.Server({ port: 8080 });
380

    
381
wss.on('connection', function connection(ws) {
382
  ws.isAlive = true;
383
  ws.on('pong', heartbeat);
384
});
385

    
386
const interval = setInterval(function ping() {
387
  wss.clients.forEach(function each(ws) {
388
    if (ws.isAlive === false) return ws.terminate();
389

    
390
    ws.isAlive = false;
391
    ws.ping(noop);
392
  });
393
}, 30000);
394
```
395

    
396
Pong messages are automatically sent in response to ping messages as required by
397
the spec.
398

    
399
Just like the server example above your clients might as well lose connection
400
without knowing it. You might want to add a ping listener on your clients to
401
prevent that. A simple implementation would be:
402

    
403
```js
404
const WebSocket = require('ws');
405

    
406
function heartbeat() {
407
  clearTimeout(this.pingTimeout);
408

    
409
  // Use `WebSocket#terminate()` and not `WebSocket#close()`. Delay should be
410
  // equal to the interval at which your server sends out pings plus a
411
  // conservative assumption of the latency.
412
  this.pingTimeout = setTimeout(() => {
413
    this.terminate();
414
  }, 30000 + 1000);
415
}
416

    
417
const client = new WebSocket('wss://echo.websocket.org/');
418

    
419
client.on('open', heartbeat);
420
client.on('ping', heartbeat);
421
client.on('close', function clear() {
422
  clearTimeout(this.pingTimeout);
423
});
424
```
425

    
426
### How to connect via a proxy?
427

    
428
Use a custom `http.Agent` implementation like [https-proxy-agent][] or
429
[socks-proxy-agent][].
430

    
431
## Changelog
432

    
433
We're using the GitHub [releases][changelog] for changelog entries.
434

    
435
## License
436

    
437
[MIT](LICENSE)
438

    
439
[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
440
[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
441
[client-report]: http://websockets.github.io/ws/autobahn/clients/
442
[server-report]: http://websockets.github.io/ws/autobahn/servers/
443
[permessage-deflate]: https://tools.ietf.org/html/rfc7692
444
[changelog]: https://github.com/websockets/ws/releases
445
[node-zlib-bug]: https://github.com/nodejs/node/issues/8871
446
[node-zlib-deflaterawdocs]:
447
  https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
448
[ws-server-options]:
449
  https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback
(2-2/5)