使用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();