使用pomelo做服務(wù)器開(kāi)發(fā)時(shí)等曼,無(wú)論什么客戶端,只要遵循與服務(wù)器的線上協(xié)議就能夠與服務(wù)器建立通信禁谦。pomelo內(nèi)置sioconnector、hybirdconnector都定義了自己的協(xié)議格式丧蘸。
服務(wù)器通信協(xié)議
配置服務(wù)器通信協(xié)議
$ vim game-server/app.js
const pomelo = require('pomelo');
/**
* 初始化應(yīng)用
*/
const app = pomelo.createApp();
app.set('name', 'test');
//前端服務(wù)器配置
app.configure('production|development', 'connector', function(){
//連接配置
app.set('connectorConfig',
{
connector : pomelo.connectors.hybridconnector,
heartbeat : 3,
useDict : true,
useProtobuf : true
});
});
//開(kāi)啟應(yīng)用
app.start();
process.on('uncaughtException', function (err) {
console.error(' Caught exception: ' + err.stack);
});
Pomelo支持的通信協(xié)議
在與客戶端通信時(shí)pomelo提供了hybirdconnector和sioconnector触趴,hybirdconnector支持TCP、WebSocket,sioconnector支持socket.io爽冕。實(shí)際編程中,可使用pomelo提供的接口自定義connector乌奇。
前端服務(wù)器 | 類 | 協(xié)議 | 通信 | 適用范圍 |
---|---|---|---|---|
hybirdconnector | pomelo.connectors.hybirdconnector | TCP、WebSocket | 使用二進(jìn)制通信 | 移動(dòng)端 |
sioconnector | pomelo.connectors.sioconnector | socket.io | 使用JSON通信 | PC端 |
udpconnector | pomelo.connectors.udpconnector | UDP | 二進(jìn)制協(xié)議 | 網(wǎng)路環(huán)境差數(shù)據(jù)包小的環(huán)境 |
mqttconnector | pomelo.connectors.mqttconnector | MQTT | 二進(jìn)制物聯(lián)網(wǎng)協(xié)議 | 嵌入式設(shè)備 |
pomelo內(nèi)部有各種協(xié)議的實(shí)現(xiàn)礁苗,典型的有protobuf试伙、mqtt。mqtt物聯(lián)網(wǎng)協(xié)議的特點(diǎn)是體積小疏叨、效率高、省電卦溢,pomelo+mqtt能實(shí)現(xiàn)單機(jī)30w在線的推送秀又。
Web端API
Web端JavaScript開(kāi)發(fā)庫(kù)
對(duì)于瀏覽器來(lái)說(shuō)单寂,HTML5中已經(jīng)支持了WebSocket宣决,因此使用支持WebSocket的瀏覽器可以直接與服務(wù)器的hybirdconnector建立通信。對(duì)于比較舊的瀏覽器袱讹,還沒(méi)有支持websocket的可使用基于socket.io的方式與服務(wù)器建立連接。因此椒丧,對(duì)于Web端救巷,Pomelo提供了兩套開(kāi)發(fā)庫(kù),分別適用于支持WebSocket的瀏覽器和不支持WebSocket的瀏覽器棒假。
開(kāi)發(fā)庫(kù) | 描述 |
---|---|
pomelo-jsclient-socket.io | 適用于socket.io |
pomelo-jsclient-websocket | 適用于websocket |
無(wú)論是socket.io還是websocket都提供了統(tǒng)一的API
初始化
pomelo.init(params, cb);
客戶端pomelo初始化精盅,在客戶端第一次調(diào)用時(shí)使用。參數(shù)params中需要指定連接的服務(wù)器的主機(jī)地址和端口叹俏,回調(diào)函數(shù)cb在連接成功后會(huì)進(jìn)行回調(diào)。
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);
});
};
請(qǐng)求服務(wù)
pomelo.request(route, msg, cb);
request
用于請(qǐng)求服務(wù)屡谐,route
是服務(wù)端的路由愕掏,格式為"xxx.xxx.xxx"顶伞。msg
為請(qǐng)求內(nèi)容剑梳,cb
響應(yīng)回來(lái)后的回調(diào)函數(shù)阻荒。
參數(shù) | 描述 |
---|---|
route |
服務(wù)端的路由众羡,格式為"xxx.xxx.xxx"。 |
msg |
請(qǐng)求內(nèi)容 |
cb |
響應(yīng)成功后的回調(diào)函數(shù) |
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);
};
例如:同網(wǎng)關(guān)服務(wù)器建立連接后 羊壹,向其發(fā)送查詢前端服務(wù)器入口的請(qǐng)求齐婴。
/**
* 連接gate服務(wù)器
* 客戶端首先要給gate服務(wù)器查詢一個(gè)connector服務(wù)器,gate給其回復(fù)一個(gè)connector的地址及端口號(hào)
* */
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);
});
});
}
斷開(kāi)連接
客戶端與服務(wù)器斷開(kāi)連接有兩種可能情妖,一種是心跳超時(shí)诱担,一種是直接斷開(kāi)。
pomelo.disconnect();
disconnect()
方法用于Pomelo主動(dòng)斷開(kāi)連接
pomelo.disconnect = function() {
if(socket) {
socket.disconnect();
socket = null;
}
};
客戶端檢測(cè)主動(dòng)斷開(kāi)
pomelo.on("disconnect", function(){
console.log("主動(dòng)斷開(kāi)");
});
客戶端檢測(cè)心跳超時(shí)
pomelo.on("heartbeat timeout", function(){
console.log("心跳超時(shí)");
});
事件監(jiān)聽(tīng)
on()
方法用于從EventEmmiter
繼承過(guò)來(lái)料睛,用來(lái)對(duì)服務(wù)端的推送做出響應(yīng)摇邦。route
用戶可以自定義,格式一般為onXXX
居扒。
pomelo.on(route, cb);
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
例如:系統(tǒng)內(nèi)置事件監(jiān)聽(tīng)
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");
});
例如:自定義事件監(jiān)聽(tīng)
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();