-
Notifications
You must be signed in to change notification settings - Fork 10.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[docs] Add an example of custom parser (#2929)
- Loading branch information
1 parent
1980fb4
commit 6c0705f
Showing
10 changed files
with
320 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
}; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' | ||
} | ||
}; |