occaecatidicta
Version:
145 lines (121 loc) • 3.92 kB
text/typescript
import * as util from 'util';
import { EventEmitter } from 'events';
import { createServer } from 'http';
let httpServer = createServer();
import { SioSocket } from './siosocket';
import { IConnector } from '../interfaces/IConnector';
import * as socket_io from 'socket.io';
let PKG_ID_BYTES = 4;
let PKG_ROUTE_LENGTH_BYTES = 1;
let PKG_HEAD_BYTES = PKG_ID_BYTES + PKG_ROUTE_LENGTH_BYTES;
let curId = 1;
export type SIOConnectorOptions = socket_io.ServerOptions;
/**
* Connector that manager low level connection and protocol bewteen server and client.
* Develper can provide their own connector to switch the low level prototol, such as tcp or probuf.
*/
export class SIOConnector extends EventEmitter implements IConnector {
port: number;
host: string;
opts: SIOConnectorOptions;
private server: SocketIO.Server;
constructor(port: number, host: string, opts: SIOConnectorOptions) {
super();
this.port = port;
this.host = host;
this.opts = opts;
opts.pingTimeout = opts.pingTimeout || 60;
opts.pingInterval = opts.pingInterval || 25;
}
/**
* Start connector to listen the specified port
*/
start(cb: () => void) {
let self = this;
// issue https://github.com/NetEase/omelox-cn/issues/174
let opts: SIOConnectorOptions;
if (!!this.opts) {
opts = this.opts;
}
else {
opts = {
transports: [
'websocket', 'polling-xhr', 'polling-jsonp', 'polling'
]
} as any;
}
opts.path = '/socket.io';
let sio = socket_io(httpServer, opts);
let port = this.port;
httpServer.listen(port, function () {
console.log('sio Server listening at port %d', port);
});
sio.on('connection', (socket) => {
// this.wsocket.sockets.on('connection', function (socket) {
let siosocket = new SioSocket(curId++, socket);
self.emit('connection', siosocket);
siosocket.on('closing', function (reason) {
siosocket.send({ route: 'onKick', reason: reason });
});
});
process.nextTick(cb);
}
/**
* Stop connector
*/
stop(force: boolean, cb: () => void) {
this.server.close();
process.nextTick(cb);
}
encode(reqId: number, route: string, msg: any) {
if (reqId) {
return composeResponse(reqId, route, msg);
} else {
return composePush(route, msg);
}
}
/**
* Decode client message package.
*
* Package format:
* message id: 4bytes big-endian integer
* route length: 1byte
* route: route length bytes
* body: the rest bytes
*
* @param {String} data socket.io package from client
* @return {Object} message object
*/
decode(msg: any) {
let index = 0;
let id = parseIntField(msg, index, PKG_ID_BYTES);
index += PKG_ID_BYTES;
let routeLen = parseIntField(msg, index, PKG_ROUTE_LENGTH_BYTES);
let route = msg.substr(PKG_HEAD_BYTES, routeLen);
let body = msg.substr(PKG_HEAD_BYTES + routeLen);
return {
id: id,
route: route,
body: JSON.parse(body)
};
}
}
let composeResponse = function (msgId: number, route: string, msgBody: any) {
return {
id: msgId,
body: msgBody
};
};
let composePush = function (route: string, msgBody: any) {
return JSON.stringify({ route: route, body: msgBody });
};
let parseIntField = function (str: string, offset: number, len: number) {
let res = 0;
for (let i = 0; i < len; i++) {
if (i > 0) {
res <<= 8;
}
res |= str.charCodeAt(offset + i) & 0xff;
}
return res;
};