1.websocket簡介
微信小程序原生websoceket接口,使用方便瑰煎,調(diào)試簡單屹篓,開發(fā)ws小程序非常方便。
但作者之前基于mine和netty開發(fā)很多年撇吞,對『基于消息的socket開發(fā)模式』情有獨鐘俗冻。故,在項目中簡單封裝了小程序原生websocket接口牍颈,使代碼組織結(jié)構(gòu)以消息為主線迄薄,圍繞消息和消息的處理邏輯展開≈笏辏可使代碼思路清晰讥蔽,解耦邏輯涣易,方便維護。
// 代碼邏輯組織結(jié)構(gòu)冶伞,圍繞消息展開新症,整個項目解耦清晰,擴展性好
Client.addMessageHandler('join_room',function(message){
// doSomething
})
Client.addMessageHandler('game_start',function(message){
// doSomething
})
2.原生微信小程序websocket用法
wx.connectSocket 連接服務(wù)器
wx.onSocketOpen 連接成功后回掉
wx.onSocketMessage 接收消息
wx.sendSocketMessage 發(fā)送消息
3.先封裝一個簡易通信協(xié)議
3.1簡易協(xié)議v1.0
發(fā)送和服務(wù)器返回消息碰缔,包含cmd和body兩個字段账劲;
其中cmd字段表示消息要完成的操作類型,body字段包含本次操作的業(yè)務(wù)數(shù)據(jù)金抡;
cmd必須有值瀑焦,body字段可以為空
{cmd:'cmd_name',body:{}}
3.2先看一下封裝后,基于消息的websocket使用方法
const Client = require('../../util/PKClient.js');
const Cmd = require('../../util/Cmd.js');
Page({
data: {
server_is_connect: false, // 服務(wù)器是否連接
join_room_ready: false, // 自己加入房間是否成功
player_is_ready: false, // 雙方玩家是否就緒
},
onLoad: function (query) {
var that = this;
wx.showLoading({
title: '加入PK中...',
})
// ON_OPEN ==================================
Client.addEventHandler('onOpen', function (res) {
wx.hideLoading();
console.log('onOpen');
that.setData({
server_is_connect: true
})
Client.sendMsg(Cmd.cmdHello());
//加入房間
wx.showLoading({
title: 'PK準備中...',
})
var cmd = Client.cmdJoinRoom(query.room_id, that.data.my_info);
Client.sendMsg(cmd);
})
// ON_ERROR ==================================
Client.addEventHandler('onError', function (res) {
console.log('onError');
that.connectError();
that.errorExit('服務(wù)器錯誤' + res.errMsg);
})
// ON_CLOSE ==================================
Client.addEventHandler('onClose', function (res) {
that.connectError();
console.log('onClose');
})
// 以下全部是基于消息的開發(fā)梗肝,收到不同消息榛瓮,做不同的處理
Client.addMessageHandler('hello', function (message) {
console.log('hell back @ ' + (new Date()));
})
// 對方要求重新開始游戲,收到消息后巫击,界面上提示玩家
Client.addMessageHandler('restart_request', function (message) {
that.setData({
is_peer_request_restart: true
});
})
// 收到對方玩家掉線的消息
Client.addMessageHandler('peer_offline', function (message) {
Client.close();
that.errorExit('對方已掉線,PK結(jié)束');
});
// 客戶端發(fā)送加入房間消息禀晓,服務(wù)器端回復(fù)
Client.addMessageHandler('join_room', function (message) {
/*
wx.hideLoading();
if (!message.body['result']) {
that.errorExit('好友正在PK中,你可以邀請其他好友PK');
Client.close();
} else {
that.setData({
join_room_ready: true
})
wx.showToast({
title: '加入房間成功',
})
}*/
});
// 服務(wù)器下發(fā)的雙方全部就緒的消息坝锰,收到這個消息后粹懒,控制界面開始游戲
Client.addMessageHandler('ready_confirm', function (message) {
/*
var quiz = message.body['quiz'];
for (var i in quiz) {
quiz[i]['type_name'] = Api.getGarbageName(quiz[i]['type']);
}
that.setData({
is_peer_request_restart: false,
score: {},
game_over: false,
time: 0,
timeFormat: '0',
quiz: quiz,
quiz_id: 0,
showResult: false,
him_info: message.body['him_info'],
player_is_ready: true
});
that.data.setInter = setInterval(
function () {
that.setData({
time: that.data.time + 1,
timeFormat: that.formatTime(that.data.time + 1)
});
}
, 1000);
*/
});
Client.addMessageHandler('game_over', function (message) {
var r = message.body['result'];
that.gameOver(r);
});
// DO_CONNECT ==================================
// 以上消息回掉全部安裝完畢了,連接服務(wù)器
Client.connect('wss://mini001.xxx.com/ws/server');
},
// 點擊重新開始按鈕顷级,給服務(wù)器發(fā)送一個 要求重新開始的請求
// 服務(wù)器會廣播到房間內(nèi)其他玩家
onRestart: function () {
var cmd = Client.cmdRestartRequest();
Client.sendMsg(cmd);
}
},
// 收到對方玩家 再來一次 的消息凫乖,回應(yīng)服務(wù)器
onRestartResponse: function () {
Client.sendMsg(Cmd.cmdRestartResponse());
},
// 選擇答案,如果正確弓颈,提交服務(wù)器
onClickAnswer: function (e) {
var selectType = e.currentTarget.dataset.type;
var quiz = this.data.quiz;
var answer = quiz[this.data.quiz_id]['type'];
if (selectType != answer) {
wx.showToast({
title: '回答錯誤',
})
} else {
wx.showToast({
title: '回答正確',
})
Client.sendMsg(Cmd.cmdRight());
}
},)
// Cmd.js 生成簡易協(xié)議文本
const getCmdStr = (cmd,body) => {
var openid = Login.getOpenId();
return JSON.stringify({cmd:cmd,openid:openid,body:body});
}
const cmdNewRoom = (my_info) => {
return getCmdStr('new_room',my_info);
}
const cmdJoinRoom = (room_id,my_info) => {
return getCmdStr('join_room', { id: room_id, my_info: my_info});
}
const cmdTxt = (room_id,txt) => {
return getCmdStr('send_txt',{txt:txt,room_id:room_id});
}
const cmdHello = (room_id, txt) => {
return getCmdStr('hello','');
}
const cmdRight = () => {
return getCmdStr('right', '');
}
const cmdRestartRequest = () => {
return getCmdStr('restart_request', '');
}
const cmdRestartResponse = () => {
return getCmdStr('restart_response', '');
}
module.exports = {
cmdNewRoom: cmdNewRoom,
cmdJoinRoom: cmdJoinRoom,
cmdTxt: cmdTxt,
cmdHello: cmdHello,
cmdRight: cmdRight,
cmdRestartRequest: cmdRestartRequest,
cmdRestartResponse: cmdRestartResponse
}
4.基于消息的微信小程序websocket開發(fā)
4.1核心API
4.1.1安裝三種事件的回調(diào)函數(shù)帽芽,如果不關(guān)心這個事件也可以不安裝
Client.addEventHandler('onOpen',function(){ });
Client.addEventHandler('onError',function(){ });
Client.addEventHandler('onClose',function(){ });
4.1.2安裝消息回掉,即什么消息要怎么處理
Client.addMessageHandler('hello',function(message){})
Client.addMessageHandler('peer_offline',function(message){})
4.1.3 發(fā)起連接
Client.connect('ws://xxxx')
開發(fā)完成
5.Client.js封裝代碼
// Client.js
function json_decode(str) {
var p = new Promise(function (resolve, reject) {
var cc = JSON.parse(str);
resolve(cc);
});
return p;
}
let is_open = false;
let socketMsgQueue = [];
let heartbeatTimer = '';
let eventHandler = {
onOpen: false,
onError: false,
onClose: false
};
let messageHandler = {};
const addMessageHandler = (cmd, handler) => {
messageHandler[cmd] = handler;
}
const addEventHandler = (event, handler) => {
eventHandler[event] = handler;
}
const connect = (server_url) => {
wx.connectSocket({ url: server_url })
}
const close = (reason) => {
wx.closeSocket({ reason: reason });
}
const sendMsg = (msg) => {
console.log('send msg:' + msg);
if (is_open) {
wx.sendSocketMessage({
data: msg
})
} else {
socketMsgQueue.push(msg)
}
}
/**原生安裝 */
wx.onSocketOpen(function (res) {
// 20s心跳翔冀,防止服務(wù)器端主動斷開
heartbeatTimer = setInterval(function () {
sendMsg(cmdHello());
}, 20000);
console.log('wx.onSocketOpen', res);
is_open = true;
for (let i = 0; i < socketMsgQueue.length; i++) {
sendMsg(socketMsgQueue[i])
}
socketMsgQueue = []
if (eventHandler['onOpen']) {
eventHandler['onOpen'](res);
}
})
wx.onSocketMessage(function (res) {
console.log('wx.onSocketMessage', res);
json_decode(res.data).then(function (message) {
var cmd = message['cmd'];
if (messageHandler[cmd]) {
messageHandler[cmd](message);
} else {
console.log('cmd not handler', message);
}
}).catch(function (e) {
console.log("cmd parse faild", e);
console.log(e);
})
});
wx.onSocketClose(function (res) {
if (res.code == 1006) {
clearInterval(heartbeatTimer);
close('服務(wù)器端主動斷開');
}
console.log('wx.onSocketClose', res);
if (eventHandler['onClose']) {
eventHandler['onClose'](res);
}
});
wx.onSocketError(function (res) {
clearInterval(heartbeatTimer);
console.log('wx.onSocketError', res);
if (eventHandler['onError']) {
eventHandler['onError'](res);
}
})
module.exports = {
connect: connect,
close: close,
sendMsg: sendMsg,
addMessageHandler: addMessageHandler,
addEventHandler: addEventHandler,
}