Pomelo Client

使用pomelo做服务器开发时,无论什么客户端,只要遵循与服务器的线上协议就能够与服务器建立通信。pomelo内置sioconnector、hybirdconnector都定义了自己的协议格式。

服务器通信协议

配置服务器通信协议

$ vim game-server/app.js
const pomelo = require('pomelo');

/**
 * 初始化应用
 */
const app = pomelo.createApp();
app.set('name', 'test');

//前端服务器配置
app.configure('production|development',  'connector', function(){
    //连接配置
    app.set('connectorConfig',
    {
      connector : pomelo.connectors.hybridconnector,
      heartbeat : 3,
      useDict : true,
      useProtobuf : true
    });
});

//开启应用
app.start();

process.on('uncaughtException', function (err) {
  console.error(' Caught exception: ' + err.stack);
});

Pomelo支持的通信协议

在与客户端通信时pomelo提供了hybirdconnector和sioconnector,hybirdconnector支持TCP、WebSocket,sioconnector支持socket.io。实际编程中,可使用pomelo提供的接口自定义connector。

前端服务器 协议 通信 适用范围
hybirdconnector pomelo.connectors.hybirdconnector TCP、WebSocket 使用二进制通信 移动端
sioconnector pomelo.connectors.sioconnector socket.io 使用JSON通信 PC端
udpconnector pomelo.connectors.udpconnector UDP 二进制协议 网路环境差数据包小的环境
mqttconnector pomelo.connectors.mqttconnector MQTT 二进制物联网协议 嵌入式设备

pomelo内部有各种协议的实现,典型的有protobuf、mqtt。mqtt物联网协议的特点是体积小、效率高、省电,pomelo+mqtt能实现单机30w在线的推送。

Web端API

Web端JavaScript开发库

对于浏览器来说,HTML5中已经支持了WebSocket,因此使用支持WebSocket的浏览器可以直接与服务器的hybirdconnector建立通信。对于比较旧的浏览器,还没有支持websocket的可使用基于socket.io的方式与服务器建立连接。因此,对于Web端,Pomelo提供了两套开发库,分别适用于支持WebSocket的浏览器和不支持WebSocket的浏览器。

开发库 描述
pomelo-jsclient-socket.io 适用于socket.io
pomelo-jsclient-websocket 适用于websocket

无论是socket.io还是websocket都提供了统一的API

初始化

pomelo.init(params, cb);

客户端pomelo初始化,在客户端第一次调用时使用。参数params中需要指定连接的服务器的主机地址和端口,回调函数cb在连接成功后会进行回调。

pomelo.init = function(params, cb){
  pomelo.params = params;
  params.debug = true;
  var host = params.host;
  var port = params.port;

  var url = 'ws://' + host;
  if(port) {
    url +=  ':' + port;
  }

  socket = io(url, {'force new connection': true, reconnect: false});

  socket.on('connect', function(){
    console.log('[pomeloclient.init] websocket connected!');
    if (cb) {
      cb(socket);
    }
  });

  socket.on('reconnect', function() {
    console.log('reconnect');
  });

  socket.on('message', function(data){
    if(typeof data === 'string') {
      data = JSON.parse(data);
    }
    if(data instanceof Array) {
      processMessageBatch(pomelo, data);
    } else {
      processMessage(pomelo, data);
    }
  });

  socket.on('error', function(err) {
    console.log(err);
  });

  socket.on('disconnect', function(reason) {
    pomelo.emit('disconnect', reason);
  });
};

请求服务

pomelo.request(route, msg, cb);

request用于请求服务,route是服务端的路由,格式为"xxx.xxx.xxx"。msg为请求内容,cb响应回来后的回调函数。

参数 描述
route 服务端的路由,格式为"xxx.xxx.xxx"。
msg 请求内容
cb 响应成功后的回调函数
pomelo.request = function(route) {
  if(!route) {
    return;
  }
  var msg = {};
  var cb;
  arguments = Array.prototype.slice.apply(arguments);
  if(arguments.length === 2){
    if(typeof arguments[1] === 'function'){
      cb = arguments[1];
    }else if(typeof arguments[1] === 'object'){
      msg = arguments[1];
    }
  }else if(arguments.length === 3){
    msg = arguments[1];
    cb = arguments[2];
  }
  msg = filter(msg,route);
  id++; 
  callbacks[id] = cb;
  var sg = Protocol.encode(id,route,msg);
  socket.send(sg);
};

例如:同网关服务器建立连接后 ,向其发送查询前端服务器入口的请求。

/**
 * 连接gate服务器
 * 客户端首先要给gate服务器查询一个connector服务器,gate给其回复一个connector的地址及端口号
 * */
function queryEntry(data, callback){
    const config = {host:"127.0.0.1", port:3014, log:true};
    pomelo.init(config, function(socket){
        const route = "gate.gateHandler.queryEntry";
        pomelo.request(route, data, function(msg){
            pomelo.disconnect();
            if(!msg){
                msg = {code:500, msg:"error"};
            }
            callback(msg);
        });
    });
}

断开连接

客户端与服务器断开连接有两种可能,一种是心跳超时,一种是直接断开。

pomelo.disconnect();

disconnect()方法用于Pomelo主动断开连接

pomelo.disconnect = function() {
  if(socket) {
    socket.disconnect();
    socket = null;
  }
};

客户端检测主动断开

pomelo.on("disconnect", function(){
  console.log("主动断开");
});

客户端检测心跳超时

pomelo.on("heartbeat timeout", function(){
  console.log("心跳超时");
});

事件监听

on()方法用于从EventEmmiter继承过来,用来对服务端的推送做出响应。route用户可以自定义,格式一般为onXXX

pomelo.on(route, cb);
EventEmitter.prototype.on = EventEmitter.prototype.addListener;

例如:系统内置事件监听

pomelo.on("connect", function(){
    console.log("pomelo client connect");
});
pomelo.on("disconnect", function(){
   console.log("pomelo client disconnect");
});
pomelo.on("error", function(){
   console.log("pomelo client error");
});
pomelo.on("heartbeat timeout", function(){
   console.log("pomelo client heartbeat timeout");
});

例如:自定义事件监听

pomelo.on("onJoin", function(data){
   console.log("onJoin", data);
});
pomelo.on("onKick", function(data){
   console.log("onKick", data);
});
pomelo.on("onChat", function(data){
   console.log("onChat", data);
});

封装

使用ES7的async/await封装pomelo客户端的request方法

function pomelo_init_request(host, port, route, param){
    return new Promise((resolve, reject)=>{
       pomelo.init({host:host, port:port, log:true}, socket=>{
           pomelo.request(route, param, res=>{
               console.log(res);
                if(res.code === 200){
                    resolve({error:false, data:res.data, message:res.message});
                }else{
                    reject({error:true, message:res.message});
                }
               //pomelo.disconnect();
           });
       });
    });
}
function pomelo_request(route, param){
    return new Promise((resolve, reject)=>{
       pomelo.request(route, param, res=>{
           console.log(res);
            if(res.code === 200){
                resolve({error:false, data:res.data, message:res.message});
            }else{
                reject({error:true, message:res.message});
            }
           //pomelo.disconnect();
       });
    });
}

使用封装后的方法

function id(min=100000, max=1000000){
    return Math.round(Math.random()*(max - min)) + min;
}

const aid = id();
const rid = 222222;
async function main(){
    let result;
    result = await pomelo_init_request("127.0.0.1", 3014, "gate.gateHandler.queryEntry", {aid:aid});
    if(!result.error){
        await pomelo_init_request(result.data.host, result.data.port, "connector.chatHandler.join", {aid:aid, rid:rid});
    }
    await pomelo_request("chat.chatHandler.send", {target:"*", content:"hello world"});
}
main();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351