melo-example-robot 1.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +6 -0
- package/app/script/ByteArray.ts +1188 -0
- package/app/script/Client.ts +93 -0
- package/app/script/MeloForEgret.ts +911 -0
- package/app.ts +34 -0
- package/dist/app/config/dev/config.json +3 -0
- package/dist/app/config/env.json +4 -0
- package/dist/app/config/prod/config.json +3 -0
- package/dist/app/script/ByteArray.js +1103 -0
- package/dist/app/script/Client.js +83 -0
- package/dist/app/script/MeloForEgret.js +741 -0
- package/dist/app.js +33 -0
- package/dist/http.js +66 -0
- package/dist/nodejs-ts-client/cacheClass.js +19 -0
- package/dist/nodejs-ts-client/client.js +396 -0
- package/dist/nodejs-ts-client/logger.service.js +59 -0
- package/dist/nodejs-ts-client/my.logger.js +49 -0
- package/http.ts +64 -0
- package/nodejs-ts-client/README.md +3 -0
- package/nodejs-ts-client/cacheClass.ts +21 -0
- package/nodejs-ts-client/client.ts +475 -0
- package/nodejs-ts-client/logger.service.ts +107 -0
- package/nodejs-ts-client/my.logger.ts +61 -0
- package/package.json +35 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MyLogger = void 0;
|
|
4
|
+
const util = require("util");
|
|
5
|
+
const logger_service_1 = require("./logger.service");
|
|
6
|
+
class MyLogger extends logger_service_1.Logger {
|
|
7
|
+
constructor(context, isTimeDiffEnabled = false) {
|
|
8
|
+
super(MyLogger.LogPrefix + context, isTimeDiffEnabled);
|
|
9
|
+
}
|
|
10
|
+
log(message, ...args) {
|
|
11
|
+
if (MyLogger.level >= 3 /* MyLogLevel.INFO */) {
|
|
12
|
+
message = 'LOG<><>' + message;
|
|
13
|
+
message = util.format(message, ...args);
|
|
14
|
+
super.log(message);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
info(message, ...args) {
|
|
18
|
+
if (MyLogger.level >= 3 /* MyLogLevel.INFO */) {
|
|
19
|
+
message = 'INFO<><>' + message;
|
|
20
|
+
message = util.format(message, ...args);
|
|
21
|
+
super.log(message);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
debug(message, ...args) {
|
|
25
|
+
if (MyLogger.level >= 4 /* MyLogLevel.DEBUG */) {
|
|
26
|
+
message = 'DEBUG<><>' + message;
|
|
27
|
+
message = util.format(message, ...args);
|
|
28
|
+
super.warn(message);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
error(message, ...args) {
|
|
32
|
+
if (MyLogger.level >= 1 /* MyLogLevel.ERROR */) {
|
|
33
|
+
message = 'ERROR<><>' + message;
|
|
34
|
+
message = util.format(message, ...args);
|
|
35
|
+
super.error(message);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
warn(message, ...args) {
|
|
39
|
+
if (MyLogger.level >= 2 /* MyLogLevel.WARN */) {
|
|
40
|
+
message = 'WARN<><>' + message;
|
|
41
|
+
message = util.format(message, ...args);
|
|
42
|
+
super.warn(message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.MyLogger = MyLogger;
|
|
47
|
+
MyLogger.LogPrefix = '';
|
|
48
|
+
MyLogger.level = 4 /* MyLogLevel.DEBUG */;
|
|
49
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXkubG9nZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vbm9kZWpzLXRzLWNsaWVudC9teS5sb2dnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsNkJBQTZCO0FBQzdCLHFEQUEwQztBQVUxQyxNQUFhLFFBQVMsU0FBUSx1QkFBTTtJQUNoQyxZQUFZLE9BQWUsRUFDZixpQkFBaUIsR0FBRyxLQUFLO1FBRWpDLEtBQUssQ0FBQyxRQUFRLENBQUMsU0FBUyxHQUFHLE9BQU8sRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO0lBQzNELENBQUM7SUFLRCxHQUFHLENBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSTtRQUNoQixJQUFJLFFBQVEsQ0FBQyxLQUFLLDJCQUFtQixFQUFFO1lBQ25DLE9BQU8sR0FBRyxTQUFTLEdBQUcsT0FBTyxDQUFDO1lBQzlCLE9BQU8sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDO1lBQ3hDLEtBQUssQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7U0FDdEI7SUFDTCxDQUFDO0lBRUQsSUFBSSxDQUFDLE9BQU8sRUFBRSxHQUFHLElBQUk7UUFDakIsSUFBSSxRQUFRLENBQUMsS0FBSywyQkFBbUIsRUFBRTtZQUNuQyxPQUFPLEdBQUcsVUFBVSxHQUFHLE9BQU8sQ0FBQztZQUMvQixPQUFPLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQztZQUN4QyxLQUFLLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1NBQ3RCO0lBQ0wsQ0FBQztJQUVELEtBQUssQ0FBQyxPQUFPLEVBQUUsR0FBRyxJQUFJO1FBQ2xCLElBQUksUUFBUSxDQUFDLEtBQUssNEJBQW9CLEVBQUU7WUFDcEMsT0FBTyxHQUFHLFdBQVcsR0FBRyxPQUFPLENBQUM7WUFDaEMsT0FBTyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUM7WUFDeEMsS0FBSyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztTQUN2QjtJQUNMLENBQUM7SUFFRCxLQUFLLENBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSTtRQUNsQixJQUFJLFFBQVEsQ0FBQyxLQUFLLDRCQUFvQixFQUFFO1lBQ3BDLE9BQU8sR0FBRyxXQUFXLEdBQUcsT0FBTyxDQUFDO1lBQ2hDLE9BQU8sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDO1lBQ3hDLEtBQUssQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7U0FDeEI7SUFDTCxDQUFDO0lBRUQsSUFBSSxDQUFDLE9BQU8sRUFBRSxHQUFHLElBQUk7UUFDakIsSUFBSSxRQUFRLENBQUMsS0FBSywyQkFBbUIsRUFBRTtZQUNuQyxPQUFPLEdBQUcsVUFBVSxHQUFHLE9BQU8sQ0FBQztZQUMvQixPQUFPLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQztZQUN4QyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1NBQ3ZCO0lBQ0wsQ0FBQzs7QUFoREwsNEJBaURDO0FBMUNpQixrQkFBUyxHQUFXLEVBQUUsQ0FBQztBQUN2QixjQUFLLDRCQUFvQiJ9
|
package/http.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import cluster from 'cluster';
|
|
2
|
+
import * as http from 'http';
|
|
3
|
+
let envConfig = require('./app/config/env.json');
|
|
4
|
+
let config = require('./app/config/' + envConfig.env + '/config');
|
|
5
|
+
let path = __filename.substring(0, __filename.lastIndexOf('/'));
|
|
6
|
+
import { Robot } from '@bigtyphoon/melo-robot';
|
|
7
|
+
|
|
8
|
+
let robot = new Robot(config);
|
|
9
|
+
|
|
10
|
+
function run(num) {
|
|
11
|
+
for (let i = 0; i < num; i++) {
|
|
12
|
+
cluster.fork();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function stop() {
|
|
17
|
+
for (let id in cluster.workers) {
|
|
18
|
+
cluster.workers[id].process.kill();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function startHttp() {
|
|
23
|
+
http.createServer(function (req, res) {
|
|
24
|
+
if (req.method === 'GET') {
|
|
25
|
+
let url = require('url').parse(req.url, true);
|
|
26
|
+
if (url.pathname === '/') {
|
|
27
|
+
return res.end(JSON.stringify(config) + '\n');
|
|
28
|
+
} else if (url.pathname === '/set') {
|
|
29
|
+
for (let key in url.query) {
|
|
30
|
+
config['apps'][key] = (typeof config[key] === 'number') ? +url.query[key] : url.query[key];
|
|
31
|
+
}
|
|
32
|
+
return res.end(JSON.stringify(config) + '\n');
|
|
33
|
+
} else if (url.pathname === '/restart') {
|
|
34
|
+
require('child_process').exec('sudo restart client', function () { });
|
|
35
|
+
return res.end('OK\n');
|
|
36
|
+
} else if (url.pathname === '/pull') {
|
|
37
|
+
require('child_process').exec('cd /home/ubuntu/hello && git pull ', function () { });
|
|
38
|
+
return res.end('OK\n');
|
|
39
|
+
} else if (url.pathname === '/stop') {
|
|
40
|
+
setTimeout(function () { stop(); }, 1000);
|
|
41
|
+
return res.end('HTTP SERVER CLOSE OK\n');
|
|
42
|
+
} else if (url.pathname === '/start') {
|
|
43
|
+
let num = url.query['num'] || 1;
|
|
44
|
+
run(num);
|
|
45
|
+
return res.end('OK\n' + num);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
res.writeHead(404);
|
|
49
|
+
res.end('<h1>404<h1>\n');
|
|
50
|
+
}).listen(config.master.cwebport);
|
|
51
|
+
console.log(' http server start at http://127.0.0.1:' + config.master.cwebport);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
process.on('uncaughtException', function (err) {
|
|
55
|
+
console.error(' Caught exception: ' + err.stack);
|
|
56
|
+
require('fs').writeFileSync('/tmp/log', err.stack, 'utf8');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (cluster.isMaster) {
|
|
60
|
+
startHttp();
|
|
61
|
+
} else {
|
|
62
|
+
robot.runAgent(path + envConfig.script);
|
|
63
|
+
}
|
|
64
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CacheClass {
|
|
7
|
+
getCache():{dictVersion:string,protoVersion:string,dict:any,protos:any}{
|
|
8
|
+
try{
|
|
9
|
+
return JSON.parse(fs.readFileSync(__dirname+'/cache.tmp').toString())as any;
|
|
10
|
+
}catch (err){
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
saveCache(cache){
|
|
16
|
+
fs.writeFileSync(__dirname+'/cache.tmp',JSON.stringify(cache,null,4));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
export const cacheClass = new CacheClass();
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import * as WebSocket from 'ws';
|
|
2
|
+
import {Package, Message, Protocol} from '@bigtyphoon/melo-protocol';
|
|
3
|
+
import {Protobuf} from '@bigtyphoon/melo-protobuf';
|
|
4
|
+
import {EventEmitter} from 'events';
|
|
5
|
+
import {cacheClass} from "./cacheClass";
|
|
6
|
+
import { MyLogger } from "./my.logger";
|
|
7
|
+
|
|
8
|
+
const JS_WS_CLIENT_TYPE = 'js-ws';
|
|
9
|
+
const JS_WS_CLIENT_VERSION = '0.0.1';
|
|
10
|
+
|
|
11
|
+
const RES_OK = 200;
|
|
12
|
+
const RES_OLD_CLIENT = 501;
|
|
13
|
+
const CODE_DICT_ERROR = 502;
|
|
14
|
+
const CODE_PROTOS_ERROR = 503;
|
|
15
|
+
|
|
16
|
+
export interface IPomeloInterface {
|
|
17
|
+
on(event: 'close', cb): any;
|
|
18
|
+
|
|
19
|
+
on(event: 'io-error', cb): any;
|
|
20
|
+
|
|
21
|
+
on(event: 'error', cb): any;
|
|
22
|
+
|
|
23
|
+
on(event: 'heartbeat timeout', cb): any;
|
|
24
|
+
|
|
25
|
+
on(event: 'onKick', cb): any;
|
|
26
|
+
|
|
27
|
+
initAsync(params): Promise<any>;
|
|
28
|
+
|
|
29
|
+
init(params, cb);
|
|
30
|
+
|
|
31
|
+
disconnect();
|
|
32
|
+
|
|
33
|
+
request(route, msg): Promise<any>;
|
|
34
|
+
|
|
35
|
+
notify(route, msg);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class Pomelo extends EventEmitter implements IPomeloInterface {
|
|
39
|
+
socket = null;
|
|
40
|
+
reqId = 0;
|
|
41
|
+
callbacks = {};
|
|
42
|
+
handlers = {};
|
|
43
|
+
routeMap = {};
|
|
44
|
+
protobuf: Protobuf = null;
|
|
45
|
+
heartbeatInterval = 5000;
|
|
46
|
+
heartbeatTimeout = this.heartbeatInterval * 2;
|
|
47
|
+
nextHeartbeatTimeout = 0;
|
|
48
|
+
gapThreshold = 100; // heartbeat gap threshold
|
|
49
|
+
heartbeatId = null;
|
|
50
|
+
heartbeatTimeoutId = null;
|
|
51
|
+
|
|
52
|
+
handshakeCallback = null;
|
|
53
|
+
logger: MyLogger;
|
|
54
|
+
|
|
55
|
+
handshakeBuffer = {
|
|
56
|
+
'sys': {
|
|
57
|
+
type: JS_WS_CLIENT_TYPE,
|
|
58
|
+
version: JS_WS_CLIENT_VERSION,
|
|
59
|
+
dictVersion: '' as any,
|
|
60
|
+
protoVersion: '' as any
|
|
61
|
+
},
|
|
62
|
+
'user': {}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
initCallback = null;
|
|
66
|
+
|
|
67
|
+
params = null;
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
data: { dict: any, abbrs: any, protos: any } = {} as any;
|
|
71
|
+
|
|
72
|
+
sysCache: { dictVersion: string, protoVersion: string, dict: any, protos: any } = null;
|
|
73
|
+
static ClientId = 0;
|
|
74
|
+
|
|
75
|
+
constructor(useNestLogger = true, private readonly showPackageLog: boolean = true) {
|
|
76
|
+
super();
|
|
77
|
+
this.handlers[Package.TYPE_HANDSHAKE] = this.handshake.bind(this);
|
|
78
|
+
this.handlers[Package.TYPE_HEARTBEAT] = this.heartbeat.bind(this);
|
|
79
|
+
this.handlers[Package.TYPE_DATA] = this.onData.bind(this);
|
|
80
|
+
this.handlers[Package.TYPE_KICK] = this.onKick.bind(this);
|
|
81
|
+
if (useNestLogger) {
|
|
82
|
+
this.logger = new MyLogger('wsclient-' + ++Pomelo.ClientId);
|
|
83
|
+
} else {
|
|
84
|
+
this.logger = console as any;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
initAsync(params): Promise<any> {
|
|
90
|
+
return new Promise(resolve => {
|
|
91
|
+
this.params = params;
|
|
92
|
+
params.debug = true;
|
|
93
|
+
this.initCallback = resolve;
|
|
94
|
+
const host = params.host;
|
|
95
|
+
const port = params.port;
|
|
96
|
+
|
|
97
|
+
let url = host;
|
|
98
|
+
if (port) {
|
|
99
|
+
url += ':' + port;
|
|
100
|
+
}
|
|
101
|
+
this.sysCache = cacheClass.getCache() || {} as any;
|
|
102
|
+
this.handshakeBuffer.sys.dictVersion = this.sysCache.dictVersion || 0;
|
|
103
|
+
this.handshakeBuffer.sys.protoVersion = this.sysCache.protoVersion || 0;
|
|
104
|
+
|
|
105
|
+
if (!params.type) {
|
|
106
|
+
this.logger.log('init websocket');
|
|
107
|
+
this.handshakeBuffer.user = params.user;
|
|
108
|
+
this.handshakeCallback = params.handshakeCallback;
|
|
109
|
+
this.initWebSocket(url, resolve);
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
init(params, cb) {
|
|
115
|
+
this.params = params;
|
|
116
|
+
params.debug = true;
|
|
117
|
+
this.initCallback = cb;
|
|
118
|
+
const host = params.host;
|
|
119
|
+
const port = params.port;
|
|
120
|
+
|
|
121
|
+
let url = 'ws://' + host;
|
|
122
|
+
if (port) {
|
|
123
|
+
url += ':' + port;
|
|
124
|
+
}
|
|
125
|
+
this.sysCache = cacheClass.getCache() || {} as any;
|
|
126
|
+
this.handshakeBuffer.sys.dictVersion = this.sysCache.dictVersion || 0;
|
|
127
|
+
this.handshakeBuffer.sys.protoVersion = this.sysCache.protoVersion || 0;
|
|
128
|
+
|
|
129
|
+
if (!params.type) {
|
|
130
|
+
this.logger.log('init websocket');
|
|
131
|
+
this.handshakeBuffer.user = params.user;
|
|
132
|
+
this.handshakeCallback = params.handshakeCallback;
|
|
133
|
+
this.initWebSocket(url, cb);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
private initWebSocket(url, cb) {
|
|
139
|
+
this.logger.log(url);
|
|
140
|
+
const onopen = (event) => {
|
|
141
|
+
this.logger.log('[pomeloclient.init] websocket connected!');
|
|
142
|
+
const obj = Package.encode(Package.TYPE_HANDSHAKE, Protocol.strencode(JSON.stringify(this.handshakeBuffer)));
|
|
143
|
+
this.send(obj);
|
|
144
|
+
};
|
|
145
|
+
const onmessage = (event) => {
|
|
146
|
+
if (this.showPackageLog && event.data.byteLength != 4) {
|
|
147
|
+
this.logger.log('recv orgdata', event.data.byteLength, Buffer.from(event.data).toString('hex'));
|
|
148
|
+
}
|
|
149
|
+
this.processPackage(Package.decode(event.data));//, cb);
|
|
150
|
+
// new package arrived, update the heartbeat timeout
|
|
151
|
+
if (this.heartbeatTimeout) {
|
|
152
|
+
this.nextHeartbeatTimeout = Date.now() + this.heartbeatTimeout;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const onerror = (event) => {
|
|
156
|
+
this.emit('io-error', event);
|
|
157
|
+
this.logger.log('socket error %j ', event);
|
|
158
|
+
};
|
|
159
|
+
const onclose = (event) => {
|
|
160
|
+
this.emit('close', event);
|
|
161
|
+
this.logger.log('socket close %j ', event);
|
|
162
|
+
};
|
|
163
|
+
this.socket = new WebSocket(url);
|
|
164
|
+
this.socket.binaryType = 'arraybuffer';
|
|
165
|
+
this.socket.onopen = onopen;
|
|
166
|
+
this.socket.onmessage = onmessage;
|
|
167
|
+
this.socket.onerror = onerror;
|
|
168
|
+
this.socket.onclose = onclose;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
disconnect() {
|
|
172
|
+
if (this.socket) {
|
|
173
|
+
if (this.socket.disconnect) this.socket.disconnect();
|
|
174
|
+
if (this.socket.close) this.socket.close();
|
|
175
|
+
this.logger.log('disconnect');
|
|
176
|
+
this.socket = null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (this.heartbeatId) {
|
|
180
|
+
clearTimeout(this.heartbeatId);
|
|
181
|
+
this.heartbeatId = null;
|
|
182
|
+
}
|
|
183
|
+
if (this.heartbeatTimeoutId) {
|
|
184
|
+
clearTimeout(this.heartbeatTimeoutId);
|
|
185
|
+
this.heartbeatTimeoutId = null;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
request(route, msg): Promise<any> {
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
msg = msg || {};
|
|
192
|
+
route = route || msg.route;
|
|
193
|
+
if (!route) {
|
|
194
|
+
this.logger.log('fail to send request without route.');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.reqId++;
|
|
199
|
+
this.sendMessage(this.reqId, route, msg);
|
|
200
|
+
|
|
201
|
+
this.callbacks[this.reqId] = resolve;
|
|
202
|
+
this.routeMap[this.reqId] = route;
|
|
203
|
+
})
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
notify(route, msg) {
|
|
207
|
+
msg = msg || {};
|
|
208
|
+
this.sendMessage(0, route, msg);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
private sendMessage(reqId, route, msg) {
|
|
212
|
+
const type = reqId ? Message.TYPE_REQUEST : Message.TYPE_NOTIFY;
|
|
213
|
+
if (this.showPackageLog) {
|
|
214
|
+
this.logger.log('send', reqId, route, msg);
|
|
215
|
+
}
|
|
216
|
+
//compress message by protobuf
|
|
217
|
+
const protos = !!this.data.protos ? this.data.protos.client : {};
|
|
218
|
+
if (!!protos[route]) {
|
|
219
|
+
msg = this.protobuf.encode(route, msg);
|
|
220
|
+
} else {
|
|
221
|
+
msg = Protocol.strencode(JSON.stringify(msg));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let compressRoute = false;
|
|
225
|
+
if (this.data.dict && this.data.dict[route]) {
|
|
226
|
+
route = this.data.dict[route];
|
|
227
|
+
compressRoute = true;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
msg = Message.encode(reqId, type, compressRoute, route, msg);
|
|
231
|
+
const packet = Package.encode(Package.TYPE_DATA, msg);
|
|
232
|
+
if (this.showPackageLog) {
|
|
233
|
+
this.logger.log('send', "packet", packet.length, packet.toString('hex'));
|
|
234
|
+
}
|
|
235
|
+
this.send(packet);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
private send(packet) {
|
|
239
|
+
if (!!this.socket) {
|
|
240
|
+
this.socket.send(packet.buffer || packet, {binary: true, mask: true});
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
private heartbeat(data) {
|
|
245
|
+
const obj = Package.encode(Package.TYPE_HEARTBEAT);
|
|
246
|
+
if (this.heartbeatTimeoutId) {
|
|
247
|
+
clearTimeout(this.heartbeatTimeoutId);
|
|
248
|
+
this.heartbeatTimeoutId = null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (this.heartbeatId) {
|
|
252
|
+
// already in a heartbeat interval
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
this.heartbeatId = setTimeout(() => {
|
|
257
|
+
this.heartbeatId = null;
|
|
258
|
+
this.send(obj);
|
|
259
|
+
|
|
260
|
+
this.nextHeartbeatTimeout = Date.now() + this.heartbeatTimeout;
|
|
261
|
+
this.heartbeatTimeoutId = setTimeout(this.heartbeatTimeoutCb.bind(this), this.heartbeatTimeout);
|
|
262
|
+
}, this.heartbeatInterval);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
private heartbeatTimeoutCb() {
|
|
266
|
+
const gap = this.nextHeartbeatTimeout - Date.now();
|
|
267
|
+
if (gap > this.gapThreshold) {
|
|
268
|
+
this.heartbeatTimeoutId = setTimeout(this.heartbeatTimeoutCb.bind(this), gap);
|
|
269
|
+
} else {
|
|
270
|
+
this.logger.error('server heartbeat timeout');
|
|
271
|
+
this.emit('heartbeat timeout');
|
|
272
|
+
this.disconnect();
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
private handshake(data) {
|
|
277
|
+
data = JSON.parse(Protocol.strdecode(data));
|
|
278
|
+
if (data.code === RES_OLD_CLIENT) {
|
|
279
|
+
this.emit('error', 'client version not fullfill');
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (data.code !== RES_OK) {
|
|
284
|
+
this.emit('error', 'handshake fail');
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
this.handshakeInit(data);
|
|
289
|
+
|
|
290
|
+
const obj = Package.encode(Package.TYPE_HANDSHAKE_ACK);
|
|
291
|
+
this.send(obj);
|
|
292
|
+
if (this.initCallback) {
|
|
293
|
+
this.initCallback(this.socket);
|
|
294
|
+
this.initCallback = null;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
private onData(data) {
|
|
299
|
+
//probuff decode
|
|
300
|
+
const msg = Message.decode(data);
|
|
301
|
+
|
|
302
|
+
if (msg.id > 0) {
|
|
303
|
+
msg.route = this.routeMap[msg.id];
|
|
304
|
+
delete this.routeMap[msg.id];
|
|
305
|
+
if (!msg.route) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
msg.body = this.deCompose(msg);
|
|
311
|
+
if (this.showPackageLog) {
|
|
312
|
+
this.logger.log('recv', JSON.stringify(msg), "\n\tpacket", data.length, data.toString('hex'));
|
|
313
|
+
}
|
|
314
|
+
this.processMessage(msg);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
private onKick(data) {
|
|
319
|
+
this.emit('onKick', data.toString());
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
private processPackage(msg) {
|
|
323
|
+
if (Array.isArray(msg)) {
|
|
324
|
+
for (let m of msg) {
|
|
325
|
+
this.handlers[m.type](m.body);
|
|
326
|
+
}
|
|
327
|
+
} else {
|
|
328
|
+
this.handlers[msg.type](msg.body);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
private processMessage(msg) {
|
|
334
|
+
if (!msg || !msg.id) {
|
|
335
|
+
// server push message
|
|
336
|
+
// this.logger.error('processMessage error!!!');
|
|
337
|
+
this.emit(msg.route, msg.body);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
//if have a id then find the callback function with the request
|
|
342
|
+
const cb = this.callbacks[msg.id];
|
|
343
|
+
|
|
344
|
+
delete this.callbacks[msg.id];
|
|
345
|
+
if (typeof cb !== 'function') {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
cb(msg.body);
|
|
350
|
+
return;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
private processMessageBatch(pomelo, msgs) {
|
|
354
|
+
for (let i = 0, l = msgs.length; i < l; i++) {
|
|
355
|
+
this.processMessage(msgs[i]);
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
private deCompose(msg) {
|
|
360
|
+
const protos = !!this.data.protos ? this.data.protos.server : {};
|
|
361
|
+
const abbrs = this.data.abbrs;
|
|
362
|
+
let route = msg.route;
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
//Decompose route from dict
|
|
366
|
+
if (msg.compressRoute) {
|
|
367
|
+
if (!abbrs[route]) {
|
|
368
|
+
this.logger.error('illegal msg!');
|
|
369
|
+
return {};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
route = msg.route = abbrs[route];
|
|
373
|
+
}
|
|
374
|
+
if (!!protos[route]) {
|
|
375
|
+
return this.protobuf.decode(route, msg.body);
|
|
376
|
+
} else {
|
|
377
|
+
return JSON.parse(Protocol.strdecode(msg.body));
|
|
378
|
+
}
|
|
379
|
+
} catch (ex) {
|
|
380
|
+
this.logger.error('route, body = ' + route + ", " + msg.body);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return msg;
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
private handshakeInit(data) {
|
|
387
|
+
if (data.sys && data.sys.heartbeat) {
|
|
388
|
+
this.heartbeatInterval = data.sys.heartbeat * 1000; // heartbeat interval
|
|
389
|
+
this.heartbeatTimeout = this.heartbeatInterval * 2; // max heartbeat timeout
|
|
390
|
+
} else {
|
|
391
|
+
this.heartbeatInterval = 0;
|
|
392
|
+
this.heartbeatTimeout = 0;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
this.initData(data);
|
|
396
|
+
|
|
397
|
+
if (typeof this.handshakeCallback === 'function') {
|
|
398
|
+
this.handshakeCallback(data.user);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
//Initilize data used in pomelo client
|
|
403
|
+
private initData(data) {
|
|
404
|
+
if (!data || !data.sys) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const dictVersion = data.sys.dictVersion;
|
|
409
|
+
const protoVersion = data.sys.protos ? data.sys.protos.version : null;
|
|
410
|
+
|
|
411
|
+
let changed = false;
|
|
412
|
+
const dict = data.sys.dict || this.sysCache.dict;
|
|
413
|
+
const protos = data.sys.protos || this.sysCache.protos;
|
|
414
|
+
|
|
415
|
+
if (dictVersion) {
|
|
416
|
+
this.sysCache.dict = dict;
|
|
417
|
+
this.sysCache.dictVersion = dictVersion;
|
|
418
|
+
changed = true;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (protoVersion) {
|
|
422
|
+
this.sysCache.protos = protos;
|
|
423
|
+
this.sysCache.protoVersion = protoVersion;
|
|
424
|
+
changed = true;
|
|
425
|
+
}
|
|
426
|
+
if (changed) {
|
|
427
|
+
cacheClass.saveCache(this.sysCache);
|
|
428
|
+
}
|
|
429
|
+
//Init compress dict
|
|
430
|
+
if (!!dict) {
|
|
431
|
+
this.data.dict = dict;
|
|
432
|
+
this.data.abbrs = {};
|
|
433
|
+
|
|
434
|
+
for (const route in dict) {
|
|
435
|
+
this.data.abbrs[dict[route]] = route;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
//Init protobuf protos
|
|
440
|
+
if (!!protos) {
|
|
441
|
+
this.data.protos = {
|
|
442
|
+
server: protos.server || {},
|
|
443
|
+
client: protos.client || {}
|
|
444
|
+
};
|
|
445
|
+
if (!this.protobuf) {
|
|
446
|
+
// 要改WEB JS客户端的话 这里可能需要改一下。
|
|
447
|
+
this.protobuf = new Protobuf({encoderProtos: protos.client, decoderProtos: protos.server});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
|