Skip to content

Commit

Permalink
[docs] Add an example of custom parser (#2929)
Browse files Browse the repository at this point in the history
  • Loading branch information
darrachequesne authored May 8, 2017
1 parent 1980fb4 commit 6c0705f
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 0 deletions.
50 changes: 50 additions & 0 deletions examples/custom-parsers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

# Socket.IO custom parsers

Since Socket.IO version 2.0.0, you can provide your custom parser, according to the needs of your application.

Several parsers are showcased here:

- the default one: [socket.io-parser](https://github.com/socketio/socket.io-parser)
- one based on msgpack: [socket.io-msgpack-parser](https://github.com/darrachequesne/socket.io-msgpack-parser)
- one based on native JSON: [socket.io-json-parser](https://github.com/darrachequesne/socket.io-json-parser)
- a custom one based on [schemapack](https://github.com/phretaddin/schemapack)

They are tested with various payloads:

- string: `['1', '2', ... '1000']`
- numeric: `[1, 2, ... 1000]`
- binary: `new Buffer(1000), where buf[i] = i`

## How to use

```
$ npm i && npm start
```

## Results

| bytes / packet | CONNECT packet | string | numeric | binary |
|----------------|----------------|--------|---------|-----------|
| default | 1 | 5903 | 3904 | 43 + 1000 |
| msgpack | 20 | 3919 | 2646 | 1029 |
| JSON | 20 | 5930 | 3931 | 3625 |
| schemapack | 20 | 3895 | 2005 | 1005 |

## Comparison

`default parser`
- supports any serializable datastructure, including Blob and File
- **but** binary payload is encoded as 2 packets

`msgpack`
- the size of payloads containing mostly numeric values will be greatly reduced
- **but** rely on [ArrayBuffer](https://caniuse.com/#feat=typedarrays) in the browser (IE > 9)

`JSON`
- optimized
- **but** does not support binary payloads

`schemapack`
- the most efficient in both speed and size
- **but** you have to provide a schema for each packet
21 changes: 21 additions & 0 deletions examples/custom-parsers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "parsers",
"version": "1.0.0",
"description": "Various socket.io parsers",
"scripts": {
"build": "webpack --config ./support/webpack.config.js",
"start": "npm run build && node ./src/server.js"
},
"author": "Damien Arrachequesne",
"license": "MIT",
"dependencies": {
"component-emitter": "^1.2.1",
"express": "^4.15.2",
"schemapack": "^1.4.2",
"socket.io": "socketio/socket.io",
"socket.io-client": "socketio/socket.io-client",
"socket.io-json-parser": "^1.0.0",
"socket.io-msgpack-parser": "^1.0.0",
"webpack": "^2.4.1"
}
}
13 changes: 13 additions & 0 deletions examples/custom-parsers/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Socket.IO custom parsers</title>
</head>
<body>
<script src="client1.bundle.js"></script>
<script src="client2.bundle.js"></script>
<script src="client3.bundle.js"></script>
<script src="client4.bundle.js"></script>
</body>
</html>
8 changes: 8 additions & 0 deletions examples/custom-parsers/src/client1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

const socket = require('socket.io-client')('localhost:3001', {});

socket.io.engine.on('data', (data) => console.log('[default]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));

socket.on('string', (data) => console.log('[default] [string]', data));
socket.on('numeric', (data) => console.log('[default] [numeric]', data));
socket.on('binary', (data) => console.log('[default] [binary]', data));
11 changes: 11 additions & 0 deletions examples/custom-parsers/src/client2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

const customParser = require('socket.io-msgpack-parser');
const socket = require('socket.io-client')('http://localhost:3002', {
parser: customParser
});

socket.io.engine.on('data', (data) => console.log('[msgpack]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));

socket.on('string', (data) => console.log('[msgpack] [string]', data));
socket.on('numeric', (data) => console.log('[msgpack] [numeric]', data));
socket.on('binary', (data) => console.log('[msgpack] [binary]', data));
11 changes: 11 additions & 0 deletions examples/custom-parsers/src/client3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

const customParser = require('socket.io-json-parser');
const socket = require('socket.io-client')('localhost:3003', {
parser: customParser
});

socket.io.engine.on('data', (data) => console.log('[json]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));

socket.on('string', (data) => console.log('[json] [string]', data));
socket.on('numeric', (data) => console.log('[json] [numeric]', data));
socket.on('binary', (data) => console.log('[json] [binary]', data));
11 changes: 11 additions & 0 deletions examples/custom-parsers/src/client4.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

const customParser = require('./custom-parser');
const socket = require('socket.io-client')('localhost:3004', {
parser: customParser
});

socket.io.engine.on('data', (data) => console.log('[custom]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));

socket.on('string', (data) => console.log('[custom] [string]', data));
socket.on('numeric', (data) => console.log('[custom] [numeric]', data));
socket.on('binary', (data) => console.log('[custom] [binary]', data));
125 changes: 125 additions & 0 deletions examples/custom-parsers/src/custom-parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@

const Emitter = require('component-emitter');
const schemapack = require('schemapack');

/**
* Packet types (see https://github.com/socketio/socket.io-protocol)
*/

const TYPES = {
CONNECT: 0,
DISCONNECT: 1,
EVENT: 2,
ACK: 3,
ERROR: 4,
BINARY_EVENT: 5,
BINARY_ACK: 6
};

const stringSchema = schemapack.build({
_id: 'uint8',
data: [ 'string' ],
nsp: 'string'
});

const numericSchema = schemapack.build({
_id: 'uint8',
data: [ 'uint16' ],
nsp: 'string'
});

const binarySchema = schemapack.build({
_id: 'uint8',
data: 'buffer',
nsp: 'string'
});

const errorPacket = {
type: TYPES.ERROR,
data: 'parser error'
};

class Encoder {
encode (packet, callback) {
switch (packet.type) {
case TYPES.EVENT:
return callback([ this.pack(packet) ]);
default:
return callback([ JSON.stringify(packet) ]);
}
}
pack (packet) {
let eventName = packet.data[0];
let flatPacket = {
data: packet.data[1],
nsp: packet.nsp
};
switch (eventName) {
case 'string':
flatPacket._id = 1;
return stringSchema.encode(flatPacket);
case 'numeric':
flatPacket._id = 2;
return numericSchema.encode(flatPacket);
case 'binary':
flatPacket._id = 3;
return binarySchema.encode(flatPacket);
default:
throw new Error('unknown event name: ' + eventName);
}
}
}

class Decoder extends Emitter {
add (obj) {
if (typeof obj === 'string') {
this.parseJSON(obj);
} else {
this.parseBinary(obj);
}
}
parseJSON (obj) {
try {
let decoded = JSON.parse(obj);
this.emit('decoded', decoded);
} catch (e) {
this.emit('decoded', errorPacket);
}
}
parseBinary (obj) {
let view = new Uint8Array(obj);
let packetId = view[0];
try {
let packet = {
type: TYPES.EVENT
};
let decoded;
switch (packetId) {
case 1:
decoded = stringSchema.decode(obj);
packet.data = [ 'string', decoded.data ];
packet.nsp = decoded.nsp;
break;
case 2:
decoded = numericSchema.decode(obj);
packet.data = [ 'numeric', decoded.data ];
packet.nsp = decoded.nsp;
break;
case 3:
decoded = binarySchema.decode(obj);
packet.data = [ 'binary', decoded.data.buffer ];
packet.nsp = decoded.nsp;
break;
default:
throw new Error('unknown type');
}
this.emit('decoded', packet);
} catch (e) {
this.emit('decoded', errorPacket);
}
}
destroy () {}
}

exports.Encoder = Encoder;
exports.Decoder = Decoder;
55 changes: 55 additions & 0 deletions examples/custom-parsers/src/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const path = require('path');
const port = process.env.PORT || 3000;

app.use(express.static(path.join(__dirname, '../public')));

server.listen(port, () => console.log('>>> http://localhost:' + port));

const io = require('socket.io');
const msgpackParser = require('socket.io-msgpack-parser');
const jsonParser = require('socket.io-json-parser');
const customParser = require('./custom-parser');

let server1 = io(3001, {});
let server2 = io(3002, {
parser: msgpackParser
});
let server3 = io(3003, {
parser: jsonParser
});
let server4 = io(3004, {
parser: customParser
});

let string = [];
let numeric = [];
let binary = new Buffer(1e3);
for (var i = 0; i < 1e3; i++) {
string.push('' + i);
numeric.push(i);
binary[i] = i;
}

server1.on('connect', onConnect(1000));
server2.on('connect', onConnect(2000));
server3.on('connect', onConnect(3000));
server4.on('connect', onConnect(4000));

function onConnect (delay) {
return function (socket) {
console.log('connect ' + socket.id);

setTimeout(() => {
socket.emit('string', string);
socket.emit('numeric', numeric);
socket.emit('binary', binary);
}, delay);

socket.on('disconnect', () => console.log('disconnect ' + socket.id));
};
}

15 changes: 15 additions & 0 deletions examples/custom-parsers/support/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

const path = require('path');

module.exports = {
entry: {
client1: './src/client1.js',
client2: './src/client2.js',
client3: './src/client3.js',
client4: './src/client4.js'
},
output: {
path: path.resolve(__dirname, '../public'),
filename: '[name].bundle.js'
}
};

0 comments on commit 6c0705f

Please sign in to comment.