eazy-pomelo
Version:
NetEase Pomelo Of EazyGame OEM
149 lines (123 loc) • 4.14 kB
JavaScript
const util = require('util');
const EventEmitter = require('events').EventEmitter;
const kcp = require('node-kcp');
const handler = require('./common/handler');
const Protocol = require('pomelo-protocol');
const Package = Protocol.Package;
const Message = Protocol.Message;
const ST_INITED = 0;
const ST_WAIT_ACK = 1;
const ST_WORKING = 2;
const ST_CLOSED = 3;
var output = function(data, size, context) {
context.socket.send(data, 0, size, context.port, context.host);
}
var Socket = function(id, socket, address, port, opts) {
EventEmitter.call(this);
let self = this;
this.id = id;
this.socket = socket;
this.host = address;
this.port = port;
this.remoteAddress = {
ip: this.host,
port: this.port
};
this.opts = opts;
let conv = opts.conv || 123;
this.kcpobj = new kcp.KCP(conv, self);
if (!!opts) {
let nodelay = opts.nodelay || 0; //是否启用 nodelay模式,0不启用;1启用。
let interval = opts.interval || 100; //协议内部工作的 interval,单位毫秒,比如 10ms或者 20ms
let resend = opts.resend || 0; //快速重传模式,默认0关闭,可以设置2(2次ACK跨越将会直接重传)
let nc = opts.nc || 0; //是否关闭流控,默认是0代表不关闭,1代表关闭。
this.kcpobj.nodelay(nodelay, interval, resend, nc); //普通模式: nodelay(kcp, 0, 40, 0, 0); 极速模式: nodelay(kcp, 1, 10, 2, 1);
let sndwnd = opts.sndwnd || 32; //该调用将会设置协议的最大发送窗口和最大接收窗口大小, 单位是包
let rcvwnd = opts.rcvwnd || sndwnd;
this.kcpobj.wndsize(sndwnd, rcvwnd);
let mtu = opts.mtu || 1400; //降低 mtu 到 470,同样数据虽然会发更多的包,但是小包在路由层优先级更高
this.kcpobj.setmtu(mtu);
}
this.kcpobj.output(output);
this.on('input', function(msg){
this.kcpobj.input(msg);
let data = this.kcpobj.recv();
if (!!data) {
data = Package.decode(data);
if (Array.isArray(data)) {
for (let p in data) {
handler(self, data[p]);
}
} else {
handler(self, data);
}
}
});
this.on('end', function(msg){
if (!!msg) {
self.socket.write(msg);
self.state = ST_CLOSED;
self.emit('end');
}
});
this.check();
this.state = ST_INITED;
}
util.inherits(Socket, EventEmitter);
module.exports = Socket;
Socket.prototype.check = function() {
let self = this;
if(this.state == ST_CLOSED){
return;
}
let now = Date.now();
this.kcpobj.update(now);
setTimeout(function(){
self.check();
}, this.kcpobj.check(now));
}
Socket.prototype.send = function(msg) {
if (this.state != ST_WORKING) {
return;
}
if (msg instanceof String) {
msg = new Buffer(msg);
} else if (!(msg instanceof Buffer)) {
msg = new Buffer(JSON.stringify(msg));
}
this.sendRaw(Package.encode(Package.TYPE_DATA, msg));
}
Socket.prototype.sendRaw = function(msg) {
this.kcpobj.send(msg);
this.kcpobj.flush();
}
Socket.prototype.sendForce = function(msg) {
if (this.state == ST_CLOSED) {
return;
}
this.sendRaw(msg);
}
Socket.prototype.sendBatch = function(msgs) {
if (this.state != ST_WORKING) {
return;
}
var rs = [];
for (var i = 0; i < msgs.length; i++) {
rs.push(Package.encode(Package.TYPE_DATA, msgs[i]));
}
this.sendRaw(Buffer.concat(rs));
}
Socket.prototype.handshakeResponse = function(resp) {
if(this.state !== ST_INITED) {
return;
}
this.sendRaw(resp);
this.state = ST_WAIT_ACK;
}
Socket.prototype.disconnect = function(msg) {
if (this.state == ST_CLOSED) {
return;
}
this.state = ST_CLOSED;
this.emit('disconnect', 'connection disconnected');
}