A complete and minimalist WebSocket under the protocol RFC-6455
It is a socket type connection (real-time, bidirectional and persistent) where both parties (server and client) can sending data (Text, Blob or ArrayBuffer) at any time.
- Supported websocket protocols: 8 and 13
- Supported HTTP2 protocol
- Supported ping and pong requests
- Supported sending and receiving types: Text, Blob and ArrayBuffer
- Supported sending and receiving encodings: utf8, ascii, base64, hex, binary, utf16le and ucs2
- Access-Control-Allow-Origin
- Limit of connections per ip
- Maximum data size
- Prevents DDOS ping and pong attack
- Fixed ID by session time
- Inheritance of socket methods
- Supported extensions:
- permessage-deflate
// Constructor
WebSocket(
server: HttpServer, // HTTP(1.x or 2) Server Object
{
allowOrigin = null, // Allowed domains
encoding = 'utf8',
limitByIP = 256, // IP access limit. if value less than 1, there will be no limit
maxPayload = 131072 * 20, // Maximum size in bytes that a message can be. if value less than 1, there will be no limit
pingDelay = 3 * 60 * 1000, // Delay in ms between sending ping's. if value less than 1, ping's will not be sent
pongTimeout = 5 * 1000, // Maximum pong waiting time in ms. if value less than 1, there will be no limit
sessionExpires = 12 * 60 * 60 * 1000, // Maximum time in ms that an ID will be associated with the same client. If the value is less than 1, every time the client reconnects, a new ID will be generated
}: {
allowOrigin?: string | string[] | null,
encoding?: 'utf8' | 'ascii' | 'base64' | 'hex' | 'binary' | 'utf16le' | 'ucs2',
limitByIP?: number,
maxPayload?: number,
pingDelay?: number,
pongTimeout?: number,
sessionExpires?: number
} = {}
)
// Getters
allowOrigin(): string | string[] | null
clients(): string[] // List of connected user ID's
encoding(): 'utf8' | 'ascii' | 'base64' | 'hex' | 'binary' | 'utf16le' | 'ucs2'
limitByIP(): number
maxPayload(): number
pingDelay(): number
pongTimeout(): number
sessionExpires(): number
// Setters
allowOrigin(arg?: (string | string[] | null) = null): void
encoding(arg?: ('utf8' | 'ascii' | 'base64' | 'hex' | 'binary' | 'utf16le' | 'ucs2') = 'utf8'): void
limitByIP(arg?: number = 256): void
maxPayload(arg?: number = 131072 * 20): void
pingDelay(arg?: number = 3 * 60 * 1000): void
pongTimeout(arg?: number = 5 * 1000): void
sessionExpires(arg?: number = 12 * 60 * 60 * 1000): void
// Methods
/* Socket Methods Begin (https://nodejs.org/docs/latest/api/net.html#net_class_net_socket) */
bytesRead(clientId: string): number
bytesWritten(clientId: string): number
isPaused(clientId: string): boolean
pause(clientId: string): boolean
readyState(clientId: string): 'opening' | 'open' | 'readOnly' | 'writeOnly'
resume(clientId: string): boolean
setEncoding(clientId: string, encoding: ('utf8' | 'ascii' | 'base64' | 'hex' | 'binary' | 'utf16le' | 'ucs2') = 'utf8'): boolean
setKeepAlive(clientId: string, enable: boolean = false, initialDelay: number = 0): boolean
setNoDelay(clientId: string, noDelay: boolean = true): boolean
/* Socket Methods End */
url(clientId: string): URL // https://developer.mozilla.org/en-US/docs/Web/API/URL
close(clientId: string): boolean
ping(clientId: string, pongTimeout?: number): boolean
send(
clientId: string,
data: string | Buffer, // Message content (if string, opcode 0x1, if not, 0x2)
encoding: ('utf8' | 'ascii' | 'base64' | 'hex' | 'binary' | 'utf16le' | 'ucs2') = 'utf8'
): boolean
// Listeners
on(name: 'close', callback: (clientId: string, event: {code: number, message: string}) => void): void
on(name: 'error', callback: (clientId: string, event: Error) => void): void
on(name: 'open', callback: (clientId: string) => void): void
on(name: string = 'message', callback: (clientId: string, data: string | Buffer) => void): void // If the pathname is instantiated in the WebSocket constructor on the front-end, it must be referenced in place of the message name
// Back-end
const HttpServer = require('http').createServer((req, res) => res.end()).listen(80); // Although this is a minimalist HTTP server, HTTPs or HTTP2 are more suitable
const WebSocket = require('@jadsonlucena/websocket'); // npm i @jadsonlucena/websocket
var webSocket = new WebSocket(HttpServer);
webSocket.on('open', clientId => {
try {
console.log('Connect', clientId, webSocket.url(clientId));
} catch (err) {
console.error(err);
}
});
webSocket.on('close', (clientId, e) => console.log('Close', clientId, e));
webSocket.on('error', (clientId, e) => console.log('Error', clientId, e));
// webSocket.on('message', (clientId, data) => {
webSocket.on('/chat', (clientId, data) => {
console.log('Data', clientId, data);
try {
// Single Client
webSocket.send(clientId, data);
// Broadcast
webSocket.clients.forEach(id => webSocket.send(id, data));
} catch (err) {
console.error(err);
}
});
// Front-end
//https://datatracker.ietf.org/doc/html/rfc6455#section-3
const path = '/chat'; // https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
const query = '?token=123'; // https://datatracker.ietf.org/doc/html/rfc3986#section-3.4
const webSocket = new WebSocket((location.protocol == 'https:' ? 'wss://' : 'ws://') + location.host + path + query);
// webSocket.binaryType = 'blob';
// webSocket.binaryType = 'arraybuffer';
webSocket.onclose = e => console.log('Close', e);
webSocket.onerror = e => console.log('Error', e);
webSocket.onopen = () => {
webSocket.send('Hello World');
webSocket.onmessage = (e) => console.log('Message', e);
};
By default, if the path in the frontend constructor is empty or "/", the listener in the backend will be "message". If you enter a path in the front-end, it must be specified in the back-end listener.
The WebSocket Protocol 13
The WebSocket Protocol 8
Extensions
Protocols
Writing WebSocket servers
Book: The Definitive Guide to HTML5 WebSocket