【bug反馈】pomelo 2.2.5 使用sioconnector报错,已经替官方找到原因了,求改正

运行node app 报错:

[2017-04-20 09:45:47.089] [INFO] pomelo-admin - 83: [ConsoleService] try to connect master: "gate", "127.0.0.1", 3005
[2017-04-20 09:45:47.281] [INFO] pomelo - 377: [/root/WebstormProjects/treasures/game-server/node_modules/pomelo/lib/application.js] "gate-server-1" enter after start...
[2017-04-20 09:45:47.368] [ERROR] console - Option path is not valid. Please refer to the README.
[2017-04-20 09:45:47.368] [ERROR] console - Option close timeout is not valid. Please refer to the README.
[2017-04-20 09:45:47.369] [ERROR] console - Option heartbeats is not valid. Please refer to the README.
[2017-04-20 09:45:47.369] [ERROR] console - Option log level is not valid. Please refer to the README.
[2017-04-20 09:45:47.369] [INFO] console - sio Server listening at port 3015
[2017-04-20 09:45:47.369] [INFO] pomelo - 409: [/root/WebstormProjects/treasures/game-server/node_modules/pomelo/lib/application.js] "gate-server-1" finish start

============================================
使用socket.io-client连接后 game-server端报错:

[2017-04-20 10:30:31.338] [ERROR] console - Caught exception: TypeError: Cannot read property 'indexOf' of undefined
    at Server.verify (/root/WebstormProjects/treasures/game-server/node_modules/engine.io/lib/server.js:132:24)
    at Server.handleRequest (/root/WebstormProjects/treasures/game-server/node_modules/engine.io/lib/server.js:205:8)
    at Server.<anonymous> (/root/WebstormProjects/treasures/game-server/node_modules/engine.io/lib/server.js:432:12)
    at Server.<anonymous> (/root/WebstormProjects/treasures/game-server/node_modules/socket.io/lib/index.js:275:16)
    at emitTwo (events.js:106:13)
    at Server.emit (events.js:192:7)
    at parserOnIncoming (_http_server.js:565:12)
    at HTTPParser.parserOnHeadersComplete (_http_common.js:99:23)

错误在于两点:
1 pomelo2.2.5以后 @fantasyni 又提交了commit修正了sioconnector错误,如下,但没有提交到npm包里
,于是导致了前半段错误
https://github.com/NetEase/pomelo/commit/c808640c443fe50e214c55d52d05f28f92ff102d
2 这个commit修正并不成功,this.opts.transports还是没有设置,是undefined

着急的可以像我这么修改,npm install pomelo后,进入node_modules的pomelo中,先按照@fantasyni 的commit的改掉,然后进入lib/connectors/sioConnector.js中,把原来的:

if(this.opts) {
    opts = this.opts;
  } else {
    opts = {
      transports: [
      'websocket', 'polling-xhr', 'polling-jsonp', 'polling'
      ]
    };
  }

改成这么写:

if(this.opts) {
    opts = this.opts;
  }
  if(!this.opts.transports) {
      opts.transports = [
          'websocket', 'polling-xhr', 'polling-jsonp', 'polling'
      ];
  }

这样就能够正常使用sioconnector
我估计网易大神没想到空对象{}也是返回true的,导致一直走if语句,不走else语句

标签: bug 反馈
MissLee 在 4-20 11:45发布
MissLee 在 4-21 10:35重新编辑 分享到 weibo
8 回复
#1 {1} MissLee 4-24 14:38 回复

sioconnector的wss协议也有问题。。。唉。。。真是醉了

MissLee 4-24 16:35 回复

找到原因了,hybridconnector中有下面这样的代码,所以支持ws和wss

if(!this.ssl) {
    this.listeningServer = net.createServer();
  } else {
    this.listeningServer = tls.createServer(this.ssl);
  }

而sioconnector没有对应的选择用http或https来createServer的代码。
很简单的bug,有空提pr

#2 MissLee 4-24 16:42 回复

提多少个pr能进贡献者名单啊,我看nextzeus大神都没进

#3 {2} MissLee 4-24 17:25 回复

sioconnector的stop函数也有bug,不知道意图是什么,没法改

/**
 * Stop connector
 */
Connector.prototype.stop = function(force, cb) {
  this.wsocket.server.close();
  process.nextTick(cb);
};
deloduan 5-8 16:46 回复

我解决了,修改代码:

/**
 * Stop connector
 */
Connector.prototype.stop = function(force, cb) {
  httpServer.close();
  process.nextTick(cb);
};
MissLee 5-12 17:13 回复

@deloduan 应该是可能httpServer.close();也可能httpsServer.close();
根据你start()方法里创建了哪个

#4 MissLee 4-24 17:40 回复

sioconnector改成了class,结果pomelo不是通过new来调用的。。。。

#5 {3} MissLee 4-24 17:45 回复

wss搞定了,哭了要

williamjie 5-4 02:14 回复

wss是如何搞定的? 我按照你的方法改了源代码,服务器启动时没问题了,但浏览器发送xhr请求时,client一直重连server,根本不会发送ws or wss请求 郁闷死了

williamjie 5-4 02:18 回复

发一份修改方案到我邮箱呗,williamjie@foxmail.com

MissLee 5-5 16:55 回复

服务端可以自己建一个SioConnector类和SioSocket类,不使用自带的
SioSocket类不变,SioConnector代码如下:

const http = require('http');
const https = require('https');
const io = require('socket.io');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
//var httpServer = require('http').createServer();
var SioSocket = require('./SioSocket');

var PKG_ID_BYTES = 4;
var PKG_ROUTE_LENGTH_BYTES = 1;
var PKG_HEAD_BYTES = PKG_ID_BYTES + PKG_ROUTE_LENGTH_BYTES;

var curId = 1;

/**
 * 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.
 */
var Connector = function(port, host, opts) {
    if (!(this instanceof Connector)) {
        return new Connector(port, host, opts);
    }

    EventEmitter.call(this);
    this.port = port;
    this.host = host;
    this.opts = opts;
    this.heartbeats = opts.heartbeats || true;
    this.closeTimeout = opts.closeTimeout || 60;
    this.heartbeatTimeout = opts.heartbeatTimeout || 60;
    this.heartbeatInterval = opts.heartbeatInterval || 25;
};

util.inherits(Connector, EventEmitter);

module.exports = Connector;

/**
 * Start connector to listen the specified port
 */
Connector.prototype.start = function(cb) {
    var self = this;
    // issue https://github.com/NetEase/pomelo-cn/issues/174
    var opts = {};

    if(this.opts) {
        opts = this.opts;
    }
    if (!opts.transports || opts.transports.length === 0) {
        opts.transports = [
            'websocket', 'polling-xhr', 'polling-jsonp', 'polling'
        ];
    }
    if (!opts.key || !opts.cert) {
        this.listeningServer = http.createServer();
    } else {
        this.listeningServer = https.createServer(opts);
    }
    const sio = io(this.listeningServer, opts);
    const port = this.port;
    this.listeningServer.listen(this.port, function () {
        console.log('sio Server listening at port %d', port);
    });
    sio.set('transports', this.opts.transports);
    sio.set('resource', '/socket.io');
    sio.set('heartbeat timeout', this.heartbeatTimeout);
    sio.set('heartbeat interval', this.heartbeatInterval);

    sio.on('connection', function (socket) {
        console.debug('sio on connection');
        // this.wsocket.sockets.on('connection', function (socket) {
        var 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
 */
Connector.prototype.stop = function(force, cb) {
    this.listeningServer.close();
    process.nextTick(cb);
};

Connector.encode = Connector.prototype.encode = function(reqId, route, msg) {
    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
 */
Connector.decode = Connector.prototype.decode = function(msg) {
    var index = 0;

    var id = parseIntField(msg, index, PKG_ID_BYTES);
    index += PKG_ID_BYTES;

    var routeLen = parseIntField(msg, index, PKG_ROUTE_LENGTH_BYTES);

    var route = msg.substr(PKG_HEAD_BYTES, routeLen);
    var body = msg.substr(PKG_HEAD_BYTES + routeLen);

    return {
        id: id,
        route: route,
        body: JSON.parse(body)
    };
};

var composeResponse = function(msgId, route, msgBody) {
    return {
        id: msgId,
        body: msgBody
    };
};

var composePush = function(route, msgBody) {
    return JSON.stringify({route: route, body: msgBody});
};

var parseIntField = function(str, offset, len) {
    var res = 0;
    for(var i=0; i<len; i++) {
        if(i > 0) {
            res <<= 8;
        }
        res |= str.charCodeAt(offset + i) & 0xff;
    }

    return res;
};

客户端就很简单了

var socket = require('socket.io-client')('wss://192.168.1.249:3015');
socket.on('connect', function(){console.log('Socket connected');});
socket.on('event', function(data){});
socket.on('disconnect', function(){});
#6 {2} williamjie 5-4 02:17 回复

还有个问题 pomelo提供的pomeloclient.js是基于socket.io 0.9.16版本,pomelo2.2.5版本中的socket.io是1.7.2,感觉兼容性有问题,你注意到这个问题了吗?

MissLee 5-5 17:03 回复

我客户端环境也是nodejs,所以用的这个
https://www.npmjs.com/package/pomelo-sio-client
它的依赖是">=0.0.1,所以会安装最新的1.7.3
"dependencies": {
"socket.io-client": ">=0.0.1"
},

MissLee 5-5 17:23 回复

上边那个客户端代码发错了,我是用socket.io-client测了一下可以连,然后就没有在做了,刚才看了下,客户端应该是基于pomelo-sio-client

服务端app.js:

app.configure('production|development', 'gate', function () {
    app.set('connectorConfig', {
        connector: SioConnector,
        key: fs.readFileSync('./ssl/private.pem'),
        cert: fs.readFileSync('./ssl/20180303.crt')
    });
});

我的客户端代码app.js:

const Pomelo = require('pomelo-sio-client');
const pomelo = new Pomelo({"debug":true});

async function main() {
    pomelo.init({host: 'localhost', port: 3015, log: true}, function () {
        console.log(`pomelo.request`);
        pomelo.request('gate.gateHandler.queryEntry', {uid: '58e58d64723cf89394806936'}, function (data) {
            console.info(data);
        });
    });
}

main().catch(err => {
    console.error(err);
});

然后呢,这个pomelo-sio-client是用ws协议连接的host,我到里面改成wss,就可以连接了。
这是输出

[pomeloclient.init] websocket connected!
pomelo.request
[pomeloclient.init] websocket disconnect
[pomeloclient.init] websocket reconnect_attempt:1
[pomeloclient.init] websocket reconnect
[pomeloclient.init] websocket connected!
pomelo.request
{ code: 200, host: '127.0.0.1', port: 3010 }

查到了connector的ip的port,但是呢socket.io经常连两次才行,不知道为什么,我现在也不用了,用那个hybridconnector的了

#7 MissLee 5-12 17:19 回复

这个encode和decode也挺逗的。
client的encode和server的decode使用了二进制格式。
server的encode和client的decode使用了json。
虽然这样没有错。而且我知道你们后面那样做是为了能够封装sendBatch。
但是这样任性真的好吗

#8 MissLee 5-12 17:39 回复

这个地方也任性,到底哪个是有用的

Connector.decode = Connector.prototype.decode = coder.decode;
Connector.encode = Connector.prototype.encode = coder.encode;

还有这个地方,你让es6强迫症怎么活

var Connector = function(port, host, opts) {
  if (!(this instanceof Connector)) {
    return new Connector(port, host, opts);
  }
回到顶部