溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

微信小程序開發(fā)之websocket的示例分析

發(fā)布時(shí)間:2021-09-03 10:50:18 來源:億速云 閱讀:189 作者:小新 欄目:移動(dòng)開發(fā)

這篇文章給大家分享的是有關(guān)微信小程序開發(fā)之websocket的示例分析的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。

為什么需要websocket?

傳統(tǒng)的實(shí)時(shí)交互的游戲,或服務(wù)器主動(dòng)發(fā)送消息的行為(如推送服務(wù)),如果想做在微信上,可能你會(huì)使用輪詢的方式進(jìn)行,不過這太消耗資源,大量的請(qǐng)求也加重了服務(wù)器的負(fù)擔(dān),而且延遲問題比較嚴(yán)重。如果是自己開發(fā)的app,為了解決這些問題,很多團(tuán)隊(duì)會(huì)自建socket,使用tcp長(zhǎng)鏈接、自定協(xié)議的方式與服務(wù)器進(jìn)行相對(duì)實(shí)時(shí)的數(shù)據(jù)交互。有能力的團(tuán)隊(duì),采用這種方式自然沒什么大問題。不過小團(tuán)隊(duì)可能就要花費(fèi)很多時(shí)間去調(diào)試,要解決很多難題,這個(gè)在成本上就劃不來。

H5引入了webSocket來解決網(wǎng)頁端的長(zhǎng)鏈接問題,而微信小程序也支持websocket。這是一個(gè)非常重要的特性,所以本系列的文章會(huì)專門拿出一篇來討論websocket。

webSocket本質(zhì)上也是TCP連接,它提供全雙工的數(shù)據(jù)傳輸。一方面可以避免輪詢帶來的連接頻繁建立與斷開的性能損耗,另一方面數(shù)據(jù)可以是比較實(shí)時(shí)的進(jìn)行雙向傳輸(因?yàn)槭情L(zhǎng)鏈接),而且WebSocket允許跨域通信(這里有個(gè)潛在的跨域安全的問題,得靠服務(wù)端來解決)。目前除IE外的瀏覽器已經(jīng)對(duì)webSocket支持得很好了,微信小程序再推一把之后,它會(huì)變得更加流行。

我們來設(shè)計(jì)一個(gè)新的demo,一個(gè)比較有趣的小游戲,多人版掃雷,準(zhǔn)確地講,多人版挖黃金。

微信小程序開發(fā)之websocket的示例分析游戲規(guī)則是這樣的:把雷換成金子,挖到金子加一分,每人輪流一次(A挖完輪到B,B挖完A才能再點(diǎn)擊),點(diǎn)中金子就算你的,也不會(huì)炸,游戲繼續(xù),直到把場(chǎng)上所有的金子都挖完游戲才結(jié)束。跟掃雷一樣,數(shù)字也是表示周邊有幾個(gè)金子,然后用戶根據(jù)場(chǎng)上已經(jīng)翻出來的數(shù)字來猜哪一格可能有金子。

這種交互的游戲難點(diǎn)在于,用戶的點(diǎn)擊操作都要傳到服務(wù)器上,而且服務(wù)器要實(shí)時(shí)的推送到其它玩家的應(yīng)用上。另外用戶自己也要接收對(duì)方操作時(shí)實(shí)時(shí)傳過來的數(shù)據(jù),這樣才不至于重復(fù)點(diǎn)中同一個(gè)格子。簡(jiǎn)單講,就是你要上報(bào)操作給服務(wù)器,而服務(wù)器也要實(shí)時(shí)給你推消息。為了簡(jiǎn)化整個(gè)模型,我們規(guī)定玩家必須輪流來點(diǎn)擊,玩家A點(diǎn)完后,才能輪到玩家B,玩家B操作完,玩家A才能點(diǎn)。

我們分幾步來實(shí)現(xiàn)這個(gè)功能。

一、實(shí)現(xiàn)思路

1、第一步,我們要先生成掃雷的地圖場(chǎng)景

這個(gè)算法比較簡(jiǎn)單,簡(jiǎn)述一下。隨機(jī)取某行某列就可以定位一個(gè)格子,標(biāo)記成金子(-1表示金子)。mimeCnt表示要生成的金子的數(shù)量,用同樣的方式循環(huán)標(biāo)記mimeCnt個(gè)隨機(jī)格子。生成完后,再用一個(gè)循環(huán)去掃描這些-1的格子,把它周邊的格子都加1,當(dāng)然必須是非金子的格子才加1。代碼放在 這里 。

微信小程序開發(fā)之websocket的示例分析

其中increaseArround用來把這格金子周邊的格子都加1,實(shí)現(xiàn)也比較簡(jiǎn)單:

微信小程序開發(fā)之websocket的示例分析

執(zhí)行g(shù)enMimeArr(),隨機(jī)生成結(jié)果如下:

微信小程序開發(fā)之websocket的示例分析

-1表示金子??戳讼旅菜茮]什么問題。接下去,我們就要接入webSocket了。

(這個(gè)是js版本的,其實(shí)生成地圖場(chǎng)景的工作是在后臺(tái)生成,這個(gè)js版本只是一個(gè)演示,不過算法是一樣的。)

2、我們需要一個(gè)支持webSocket的服務(wù)端

本例子中,我們使用python的tornado框架來實(shí)現(xiàn)(tornado提供了tornado.websocket模塊)。當(dāng)然讀者也可以使用socket.io,專為webSocket設(shè)計(jì)的js語言的服務(wù)端,用起來非常簡(jiǎn)單,它也對(duì)不支持webSocket的瀏覽器提供了兼容(flash或comet實(shí)現(xiàn))。

筆者本人比較喜歡使用tornado,做了幾年后臺(tái)開發(fā),使用最多的框架之一的就是它,NIO模型,而且非常輕量級(jí),同樣的rps,java可能需要700-800M的內(nèi)存,tornado只要30-40M,所以在一臺(tái)4G內(nèi)存的機(jī)子上可以跑上百個(gè)tornado服務(wù),而java,對(duì)不起,只能跑3個(gè)虛擬機(jī)。微服務(wù)的時(shí)代,這一點(diǎn)對(duì)小公司很重要。當(dāng)然如果讀者本人對(duì)java比較熟悉的話,也可以選擇netty框架嘗試一下。

webSocket用tornado的另一個(gè)好處是,它可以在同一個(gè)服務(wù)(端口)上同時(shí)支持webSocket及http兩種協(xié)議。tornado的官方demo代碼中展示了怎么實(shí)現(xiàn)同時(shí)使用兩種協(xié)議。在本游戲中,可以這么用:用戶進(jìn)入首頁,用http協(xié)議去拉取當(dāng)前的房間號(hào)及數(shù)據(jù)。因?yàn)槭醉撌谴蜷_最多的,進(jìn)了首頁的用戶不一定會(huì)玩游戲。所以首頁還沒必要建立webSocket鏈接,webSocket鏈接主要用來解決頻繁請(qǐng)求及推送的操作。首頁只有一個(gè)請(qǐng)求操作。選了房間號(hào)后,進(jìn)去下一個(gè)游戲頁面再開始建立webSocket鏈接。

3、客戶端

使用微信小程序開發(fā)工具,直接連接是會(huì)報(bào)域名安全錯(cuò)誤的,因?yàn)楣ぞ邇?nèi)部做了限制,對(duì)安全域名才會(huì)允許連接。所以同樣的,這里我們也繼續(xù)改下工具的源碼,把相關(guān)的行改掉就行修改方式如下:

找到asdebug.js的這一行,把它改成: if(false)即可。

`if (!i(r, "webscoket"))

懶得修改的讀者可以直接使用我破解過的IDE。 發(fā)起一個(gè)websocket鏈接的代碼也比較簡(jiǎn)單:

`wx.connectSocket({
url: webSocketUrl,
});
在調(diào)用這個(gè)請(qǐng)求代碼之前,先添加下事件監(jiān)聽,這樣才知道有沒有連接成功:<br /> `

wx.onSocketOpen(function(res){ console.log('websocket opened.'); });

`連接失敗的事件:
wx.onSocketError(function(res){ console.log('websocket fail');
}) `

收到服務(wù)器的消息時(shí)觸發(fā)的事件:

`
wx.onSocketMessage(function(res){
console.log('received msg: ' + res.data);
})
當(dāng)鏈接建立之后,發(fā)送消息的方法如下:<br /> `

消息發(fā)送

由于建立鏈接是需要幾次握手,需要一定的時(shí)間,所以在wx.connectSocket成功之前,如果直接wx.sendSocketMessage發(fā)送消息會(huì)報(bào)錯(cuò),這里做一個(gè)兼容,如果連接還沒建立成功,則用一個(gè)數(shù)組來保存要發(fā)送的信息;而鏈接第一次建立時(shí),把數(shù)據(jù)遍歷一遍,把消息拿出來一個(gè)個(gè)補(bǔ)發(fā)。這個(gè)邏輯我們封裝成一個(gè)send方法,如下:

`
function sendSocketMessage(msg) {
if (typeof(msg) === 'object') { // 只能發(fā)送string
msg = JSON.stringify(msg);
}
if (socketOpened) { // socketOpened變量在wx.onSocketOpen時(shí)設(shè)置為true
wx.sendSocketMessage({
data:msg
});
} else { // 發(fā)送的時(shí)候,鏈接還沒建立
socketMsgQueue.push(msg);
}
}

二、demo功能解析

1、首頁entry

為了簡(jiǎn)化模型,把重點(diǎn)放在webSocket上,我們把首頁做成自己填寫房間號(hào)的形式。讀者如果自己有時(shí)間和能力的話,可以把首頁做成一個(gè)房間列表,并顯示每個(gè)房間有多少人在玩,只有一人的可以進(jìn)去跟他玩。甚至后面還可以加上觀看模式,點(diǎn)擊別人的房間進(jìn)去觀看別人怎么玩。

微信小程序開發(fā)之websocket的示例分析

填寫房間號(hào)的input組件,添加一個(gè)事件,取得它的值event.detail.value后setData到本page里面。

點(diǎn)擊“開始游戲”,再把房間號(hào)存入app的globalData里面,然后wx.navigateTo到主游戲頁面index。

這個(gè)頁面比較簡(jiǎn)單。

2、主游戲頁面

我們封裝一個(gè)websocket/connect.js模塊,專門用來處理websocket鏈接。主要有兩個(gè)方法,connect發(fā)起webSocket鏈接,send用來發(fā)送數(shù)據(jù)。

index主頁面:

微信小程序開發(fā)之websocket的示例分析

初始化狀態(tài),9x9的格子,每一格子其實(shí)都是一個(gè)button按鈕。我們生成的地圖場(chǎng)景數(shù)據(jù),分別對(duì)應(yīng)著每一格。比如1表示周邊的1個(gè)金子,0表示周邊沒有金子,-1表示這格是個(gè)金子,我們的目標(biāo)就是找到這些-1。找得越多得分越高。

這里討論一個(gè)安全性問題。相信一句話:在前端做的安全措施大都是不靠譜的。上圖中的矩陣,每個(gè)格子背后的數(shù)據(jù),不應(yīng)該放在前端,因?yàn)閖s代碼是可以調(diào)試的,可以下斷點(diǎn)在相應(yīng)的變量上,就可以看到整個(gè)矩陣數(shù)據(jù),然后就知道哪些格子是金子,就可以作弊,這是非常不公平的。所以最好的方法是把這些矩陣數(shù)據(jù)存在后端,每次用戶操作的時(shí)候,把用戶點(diǎn)擊的坐標(biāo)發(fā)到后臺(tái),后臺(tái)再判斷相應(yīng)的坐標(biāo)是什么數(shù)據(jù),再返回給前端。這個(gè)看似有很多數(shù)據(jù)傳輸?shù)慕换シ绞剑鋵?shí)是不會(huì)浪費(fèi)資源,因?yàn)橛脩舻拿總€(gè)點(diǎn)擊操作,本來就要上報(bào)到后臺(tái),這樣游戲的另一玩家才知道你點(diǎn)了哪個(gè)格子。反正都是要傳數(shù)據(jù)的,所以肯定要傳坐標(biāo),這樣前端就完全沒有必要知道哪個(gè)格子是什么數(shù)據(jù),因?yàn)楹笈_(tái)的推送消息會(huì)告訴你。

這樣我們就繞過了前端存矩陣數(shù)據(jù)的問題。但是我們還是需要一個(gè)數(shù)組來存儲(chǔ)當(dāng)前矩陣狀態(tài)的,比如哪個(gè)格子已經(jīng)被翻開,里面是什么數(shù)據(jù),也就是說要存儲(chǔ)場(chǎng)上已經(jīng)被打開的格子。所以在后臺(tái),我們要存儲(chǔ)兩個(gè)數(shù)據(jù),一個(gè)是所有的矩陣數(shù)據(jù),也就是地圖場(chǎng)景數(shù)據(jù);另一個(gè)是當(dāng)前狀態(tài)的數(shù)據(jù),這個(gè)要用來同步雙方的界面。

3、結(jié)束頁面

游戲結(jié)束的判斷條件,就是場(chǎng)上所有的金子都被挖完了。這個(gè)條件也是在后臺(tái)判斷的。

在每次用戶挖到金子的時(shí)候,后臺(tái)都會(huì)多一個(gè)判斷邏輯,就是看這個(gè)金子是否是最后一個(gè)。如果是的話,就發(fā)送一個(gè)over類型的消息給游戲的所有玩家。

玩家終端接收到這個(gè)消息時(shí),就會(huì)結(jié)束當(dāng)前的游戲,并跳到結(jié)束頁面。

微信小程序開發(fā)之websocket的示例分析

沒有專門的設(shè)計(jì)師,隨便網(wǎng)上偷了張圖片貼上去,界面比較丑。下方顯示自己的得分和當(dāng)前的房間號(hào)。

三、實(shí)現(xiàn)細(xì)節(jié)

1、代碼結(jié)構(gòu)

微信小程序開發(fā)之websocket的示例分析

前端代碼,分了幾個(gè)模塊:pages放所有的頁面,common放通用的模塊,mime放挖金子的主邏輯(暫時(shí)沒用到),res放資源文件,websocket放webSocket相關(guān)的處理邏輯。

后臺(tái)代碼,讀者稍微了解一下就行了,不討論太多。里面我放了docker文件,熟悉docker的讀者可以直接一個(gè)命令跑起整個(gè)服務(wù)端。筆者在自己的服務(wù)器上跑了這個(gè)webSocket服務(wù),ip和端口已經(jīng)寫在前端代碼里,讀者輕虐??赡芊挪痪?,讀者可以自己把這個(gè)服務(wù)跑起來。

2、消息收發(fā)

(1)消息協(xié)議

我們簡(jiǎn)單地定義下,消息的格式如下。 發(fā)送消息:

`{type: 'dig', …}

服務(wù)器返回的消息:

{errCode: 0, data: {type: 'dig', …} }

因?yàn)閣ebSocket類型的消息跟傳統(tǒng)的http請(qǐng)求不太一樣,http請(qǐng)求沒有狀態(tài),一個(gè)請(qǐng)求過去,一會(huì)兒就返回,返回的數(shù)據(jù)肯定是針對(duì)這個(gè)請(qǐng)求的。而webSocket的模型是這樣的:客戶端發(fā)過去很多請(qǐng)求,然后也不知道服務(wù)器返回的數(shù)據(jù)哪個(gè)是對(duì)應(yīng)哪個(gè)請(qǐng)求,所以需要一個(gè)字段來把所有的返回分成多種類型,并進(jìn)行相應(yīng)的處理。

(2)發(fā)送消息

發(fā)送消息就比較容易了,上面我們定義了一個(gè)send方法及未連接成功時(shí)的簡(jiǎn)單的消息列表。

(3)接收消息

讀者在閱讀代碼的時(shí)候,可能會(huì)有一個(gè)疑惑,websocket/connect.js里只有send發(fā)送方法,而沒有接收推送消息的處理,那接收消息的處理在哪?怎么關(guān)聯(lián)起來的?

websocket/目錄里面還有另一個(gè)文件,msgHandler.js,它就是用來處理接收消息的主要處理模塊:

微信小程序開發(fā)之websocket的示例分析

從服務(wù)器推送過來的消息,主要有這三種類型:1挖金子操作,可能是自己的操作,也可能是對(duì)方的操作,里面有一個(gè)字段isMe來表示是否是自己的操作。接收到這類消息時(shí),會(huì)翻轉(zhuǎn)地圖上相應(yīng)的格子,并顯示出挖的結(jié)果。2創(chuàng)建或進(jìn)入房間的操作,一個(gè)房間有兩個(gè)用戶玩,創(chuàng)建者先開始。3游戲結(jié)束的消息,當(dāng)應(yīng)用接收到這類消息時(shí),會(huì)直接跳轉(zhuǎn)到結(jié)束頁面。

這個(gè)處理邏輯,是在websocket/connect.js的wx.onSocketMessage回調(diào)里關(guān)聯(lián)上的。

在消息的收發(fā)過程中,每個(gè)消息交互,調(diào)試工具都會(huì)記錄下來??梢栽谡{(diào)試工具里看到,在NetWork->WS里就可以看到:

微信小程序開發(fā)之websocket的示例分析

3、前端挖金子

代碼如下:

var websocket = require('../../websocket/connect.js'); var msgReceived = require('../../websocket/msgHandler.js');
Page({
    data: {
        mimeMap: null,
        leftGolds: 0, // 總共有多少金子 score: 0, // 我的得分 roomNo: 0 // 房間號(hào) },
    x: 0, // 用戶點(diǎn)中的列 y: 0, // 用戶點(diǎn)中的行 onLoad: function () { var roomNo = app.getRoomNo(); this.setData({
            roomNo: roomNo
        }); // test // websocket.send('before connection'); if (!websocket.socketOpened) { // setMsgReceiveCallback websocket.setReceiveCallback(msgReceived, this); // connect to the websocket websocket.connect();
            websocket.send({
              type: 'create' });
        } else {
            websocket.send({
              type: 'create',
              no: roomNo
            });
        }
    },
    digGold: function(event) { // 不直接判斷,而把坐標(biāo)傳給后臺(tái)判斷 // 被開過的就不管了 if (event.target.dataset.value < 9) { return;
        } // 取到這格的坐標(biāo) this.x = parseInt(event.target.dataset.x); this.y = parseInt(event.target.dataset.y); console.log(this.x, this.y); // 上報(bào)坐標(biāo) this.reportMyChoice();
    },
    reportMyChoice: function() {
        roomNo = app.getRoomNo();
        websocket.send({
            type: 'dig',
            x: this.x,
            y: this.y,
            no: roomNo
        });
    },
});

在page的onLoad事件里,先更新界面上的房間號(hào)信息。然后開始我們的重點(diǎn),websocket.connect發(fā)起webSocket鏈接,websocket是我們封裝的模塊。然后把我們msgHandler.js處理邏輯設(shè)置到服務(wù)端推送消息回調(diào)里面。接著,發(fā)送一個(gè)create消息來創(chuàng)建或加入房間。服務(wù)端會(huì)對(duì)這個(gè)消息做出響應(yīng),返回本房間的地圖場(chǎng)景數(shù)據(jù)。

digGold是每個(gè)格子的點(diǎn)擊事件處理函數(shù)。這兒有一個(gè)邏輯,一個(gè)格子周邊最多有8個(gè)格子,所以每個(gè)格子的數(shù)據(jù)最大不可能大于8,上面代碼中可以看到有一個(gè)9,這其實(shí)是為了跟0區(qū)分,用來表示場(chǎng)上目前的還沒被翻開的格子的數(shù)據(jù),用9來表示,當(dāng)然你也可以用10,100都行。

wxml的矩陣數(shù)據(jù)綁定代碼如下:

`
<view wx:for="{{mimeMap}}" wx:for-item="row" wx:for-index="i"
class="flex-container">
<button wx:for="{{row}}" wx:for-item="cell" wx:for-index="j"
class="flex-item {{cell<0?'gold':''}} {{cell<9?'open':''}}"
bindtap="digGold" data-x="{{j}}" data-y="{{i}}" data-value="{{cell}}">
{{cell<9?(cell<0?'*':cell):"-"}}
</button>
</view>

4、服務(wù)端實(shí)現(xiàn)

簡(jiǎn)單的提一下就好,因?yàn)楹笈_(tái)不是本系列文章的重點(diǎn),雖然這個(gè)demo的開發(fā)也花了大半的時(shí)候在寫后臺(tái)。前后端的消息交互,借助了webSocket通道,傳輸我們自己定義格式的內(nèi)容。上面有個(gè)截圖顯示了后臺(tái)代碼目錄的結(jié)構(gòu),劃分得比較隨意,handlers里存放了的是主要的處理邏輯。webSocketHandler是入口,在它的on_message里,對(duì)收到的客戶端的消息,根據(jù)類型進(jìn)行分發(fā),dig類型,分發(fā)到answerHandler去處理,create類型,分發(fā)到roomHandler里去處理。

還有一點(diǎn)稍微提一下,本例子中的后臺(tái)webSocket消息處理也跟傳統(tǒng)的http處理流程有一點(diǎn)不一樣。就是在最后返回的時(shí)候,不是直接返回的,而是廣播的形式,把消息發(fā)送給所有的人。比如用戶A點(diǎn)擊了格子,后臺(tái)收到坐標(biāo)后,會(huì)把這個(gè)坐標(biāo)及坐標(biāo)里的數(shù)據(jù)一起發(fā)送給房間里的所有人,而不是單獨(dú)返回給上報(bào)坐標(biāo)的人。只是會(huì)有一個(gè)isMe字段來告訴客戶端是否是自己的操作。

感謝各位的閱讀!關(guān)于“微信小程序開發(fā)之websocket的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI