mydog
Version:
a framework of typescript game server
327 lines (273 loc) • 11.8 kB
text/typescript
/**
* The master central server, accepts the monitor connection, is responsible for the mutual understanding between the servers, and accepts cli commands
*/
import Application from "../application";
import { MasterCli } from "./cliUtil";
import { SocketProxy, monitor_get_new_server, monitor_remove_server, monitor_reg_master, loggerLevel } from "../util/interfaceDefine";
import tcpServer from "./tcpServer";
import { runServers } from "../util/starter";
import define = require("../util/define");
import * as msgCoder from "./msgCoder";
import * as path from "path";
let meFilename = `[${path.basename(__filename, ".js")}.ts]`;
let servers: { [id: string]: Master_ServerProxy } = {};
let serversDataTmp: monitor_get_new_server = { "T": define.Master_To_Monitor.addServer, "servers": {} };
let masterCli: MasterCli;
let app: Application;
let serverToken = "";
let cliToken = "";
export function start(_app: Application, cb?: Function) {
app = _app;
masterCli = new MasterCli(_app, servers);
startServer(cb);
}
function startServer(cb?: Function) {
let tokenConfig = app.someconfig.recognizeToken || {};
serverToken = tokenConfig.serverToken || define.some_config.Server_Token;
cliToken = tokenConfig.cliToken || define.some_config.Cli_Token;
tcpServer(app.serverInfo.port, false, startCb, newClientCb);
function startCb() {
let str = `listening at [${app.serverInfo.host}:${app.serverInfo.port}] ${app.serverId}`;
console.log(str);
cb && cb();
if (app.startMode === "all") {
runServers(app);
}
}
function newClientCb(socket: SocketProxy) {
new UnregSocket_proxy(socket);
}
}
/**
* Unregistered socket proxy
*/
class UnregSocket_proxy {
private socket: SocketProxy;
private registerTimer: NodeJS.Timeout = null as any;
private onDataFunc: (data: Buffer) => void;
private onCloseFunc: () => void;
constructor(socket: SocketProxy) {
this.socket = socket;
this.onDataFunc = this.onData.bind(this);
this.onCloseFunc = this.onClose.bind(this);
socket.on("data", this.onDataFunc);
socket.on("close", this.onCloseFunc);
this.registerTimeout();
}
private registerTimeout() {
let self = this;
this.registerTimer = setTimeout(function () {
app.logger(loggerLevel.error, `${meFilename} unregistered socket, register timeout, close it, ${self.socket.remoteAddress}`);
self.socket.close();
}, 5000);
}
private onData(_data: Buffer) {
let socket = this.socket;
let data: monitor_reg_master;
try {
data = JSON.parse(_data.toString());
} catch (err) {
app.logger(loggerLevel.error, `${meFilename} unregistered socket, JSON parse error, close it, ${socket.remoteAddress}`);
socket.close();
return;
}
// The first packet must be registered
if (!data || data.T !== define.Monitor_To_Master.register) {
app.logger(loggerLevel.error, `${meFilename} unregistered socket, illegal data, close it, ${socket.remoteAddress}`);
socket.close();
return;
}
// Is it a server?
if (data.serverToken) {
if (data.serverToken !== serverToken) {
app.logger(loggerLevel.error, `${meFilename} unregistered socket, illegal serverToken, close it, ${socket.remoteAddress}`);
socket.close();
return;
}
if (!data.serverInfo || !data.serverInfo.id || !data.serverInfo.host || !data.serverInfo.port || !data.serverInfo.serverType) {
app.logger(loggerLevel.error, `${meFilename} unregistered socket, illegal serverInfo, close it, ${socket.remoteAddress}`);
socket.close();
return;
}
this.registerOk();
new Master_ServerProxy(data, socket);
return;
}
// Is it a cli?
if (data.cliToken) {
if (data.cliToken !== cliToken) {
app.logger(loggerLevel.error, `${meFilename} unregistered socket, illegal cliToken, close it, ${socket.remoteAddress}`);
socket.close();
return;
}
this.registerOk();
new Master_ClientProxy(socket);
return;
}
app.logger(loggerLevel.error, `${meFilename} unregistered socket, illegal socket, close it, ${socket.remoteAddress}`);
socket.close();
}
private onClose() {
clearTimeout(this.registerTimer);
app.logger(loggerLevel.error, `${meFilename} unregistered socket closed, ${this.socket.remoteAddress}`);
}
private registerOk() {
clearTimeout(this.registerTimer);
this.socket.removeListener("data", this.onDataFunc);
this.socket.removeListener("close", this.onCloseFunc);
this.socket = null as any;
}
}
/**
* master processing server agent
*/
export class Master_ServerProxy {
private socket: SocketProxy;
public sid: string = "";
public serverType: string = "";
private heartbeatTimeoutTimer: NodeJS.Timeout = null as any;
constructor(data: monitor_reg_master, socket: SocketProxy) {
this.socket = socket;
this.init(data);
}
private init(data: monitor_reg_master) {
let socket = this.socket;
if (!!servers[data.serverInfo.id]) {
app.logger(loggerLevel.error, `${meFilename} already has a monitor named: ${data.serverInfo.id}, close it, ${socket.remoteAddress}`);
socket.close();
return;
}
socket.maxLen = define.some_config.SocketBufferMaxLen;
this.heartbeatTimeout();
socket.on('data', this.onData.bind(this));
socket.on('close', this.onClose.bind(this));
this.sid = data.serverInfo.id;
this.serverType = data.serverInfo.serverType;
// Construct a new server message
let socketInfo: monitor_get_new_server = {
"T": define.Master_To_Monitor.addServer,
"servers": {}
};
socketInfo.servers[this.sid] = data.serverInfo;
let socketInfoBuf: Buffer = msgCoder.encodeInnerData(socketInfo);
// Notify other servers that there are new servers
for (let sid in servers) {
servers[sid].socket.send(socketInfoBuf);
}
// Notify the newly added server, which servers are currently available
let result = msgCoder.encodeInnerData(serversDataTmp);
this.socket.send(result);
servers[this.sid] = this;
serversDataTmp.servers[this.sid] = data.serverInfo;
app.logger(loggerLevel.debug, `${meFilename} get a new monitor named: ${this.sid}, ${this.socket.remoteAddress}`);
}
private heartbeatTimeout() {
this.heartbeatTimeoutTimer = setTimeout(() => {
app.logger(loggerLevel.error, `${meFilename} heartbeat timeout, close the monitor named: ${this.sid}, ${this.socket.remoteAddress}`);
this.socket.close();
}, define.some_config.Time.Monitor_Heart_Beat_Time * 1000 * 2);
}
send(msg: any) {
this.socket.send(msgCoder.encodeInnerData(msg));
}
private heartbeatResponse() {
let msg = { T: define.Master_To_Monitor.heartbeatResponse };
let buf = msgCoder.encodeInnerData(msg);
this.socket.send(buf);
}
private onData(_data: Buffer) {
let data: any;
try {
data = JSON.parse(_data.toString());
} catch (err) {
app.logger(loggerLevel.error, `${meFilename} JSON parse error,close the monitor named: ${this.sid}, ${this.socket.remoteAddress}`);
this.socket.close();
return;
}
try {
if (data.T === define.Monitor_To_Master.heartbeat) {
this.heartbeatTimeoutTimer.refresh();
this.heartbeatResponse();
} else if (data.T === define.Monitor_To_Master.cliMsg) {
masterCli.deal_monitor_msg(data);
} else {
app.logger(loggerLevel.error, `${meFilename} the monitor illegal data type close it: ${this.sid} ${this.socket.remoteAddress}`);
this.socket.close();
}
} catch (e: any) {
app.logger(loggerLevel.error, e);
this.socket.close();
}
}
private onClose() {
clearTimeout(this.heartbeatTimeoutTimer);
delete servers[this.sid];
delete serversDataTmp.servers[this.sid];
let serverInfo: monitor_remove_server = {
"T": define.Master_To_Monitor.removeServer,
"id": this.sid,
"serverType": this.serverType
};
let serverInfoBuf: Buffer = msgCoder.encodeInnerData(serverInfo);
for (let sid in servers) {
servers[sid].socket.send(serverInfoBuf);
}
app.logger(loggerLevel.error, `${meFilename} a monitor disconnected: ${this.sid}, ${this.socket.remoteAddress}`);
}
}
/**
* master handles cli agent
*/
export class Master_ClientProxy {
private socket: SocketProxy;
private heartbeatTimeoutTimer: NodeJS.Timeout = null as any;
constructor(socket: SocketProxy) {
this.socket = socket;
this.init();
}
private init() {
let socket = this.socket;
socket.maxLen = define.some_config.SocketBufferMaxLen;
this.heartbeatTimeOut();
socket.on('data', this.onData.bind(this));
socket.on('close', this.onClose.bind(this));
app.logger(loggerLevel.info, `${meFilename} get a new cli: ${socket.remoteAddress}`);
}
private heartbeatTimeOut() {
this.heartbeatTimeoutTimer = setTimeout(() => {
app.logger(loggerLevel.error, `${meFilename} heartbeat timeout, close the cli: ${this.socket.remoteAddress}`);
this.socket.close();
}, define.some_config.Time.Monitor_Heart_Beat_Time * 1000 * 2);
}
private onData(_data: Buffer) {
let data: any;
try {
data = JSON.parse(_data.toString());
} catch (err) {
app.logger(loggerLevel.error, `${meFilename} JSON parse error,close the cli: ${this.socket.remoteAddress}`);
this.socket.close();
return;
}
try {
if (data.T === define.Cli_To_Master.heartbeat) {
this.heartbeatTimeoutTimer.refresh();
} else if (data.T === define.Cli_To_Master.cliMsg) {
app.logger(loggerLevel.info, `${meFilename} get command from the cli: ${this.socket.remoteAddress} ==> ${_data.toString()}`);
masterCli.deal_cli_msg(this, data);
} else {
app.logger(loggerLevel.error, `${meFilename} the cli illegal data type close it: ${this.socket.remoteAddress}`);
this.socket.close();
}
} catch (e: any) {
app.logger(loggerLevel.error, `${meFilename} cli handle msg err, close it: ${this.socket.remoteAddress}\n ${e.stack}`);
this.socket.close();
}
}
send(msg: any) {
this.socket.send(msgCoder.encodeInnerData(msg));
}
private onClose() {
clearTimeout(this.heartbeatTimeoutTimer);
app.logger(loggerLevel.info, `${meFilename} a cli disconnected: ${this.socket.remoteAddress}`);
}
}