本節(jié)思路:
cmd命令按照如下流程: 當(dāng)客戶端點(diǎn)擊準(zhǔn)備按鈕,發(fā)送ready(client->server)命令到服務(wù)端,服務(wù)端更新該用戶的ready標(biāo)識位(存于redis),計(jì)算房間人數(shù)是否已滿,以及房間所有用戶是否都已設(shè)置ready標(biāo)識位,如是,則發(fā)送start(server->client)命令給所有用戶,開始游戲。 客戶端選擇好自己的出拳信息,發(fā)送guess(client->server)命令到服務(wù)端,服務(wù)端記錄該用戶出拳數(shù)據(jù),判斷房間內(nèi)所有人都以提交guess命令,則計(jì)算最后結(jié)果,發(fā)送result(server->client)命令給所有用戶。 客戶端接收到result命令后,重新進(jìn)入ready流程。 如退出小程序,客戶端發(fā)送logout(client->server)命令到服務(wù)端,服務(wù)端從列表中刪除該用戶,重新發(fā)送init(server->client)命令到所有其他在線用戶,更新在線用戶列表。
命令流程如下:
效果示意圖: go服務(wù)端 package main import ( "golang.org/x/net/websocket" "fmt" "log" "net/http" "github.com/go-redis/redis" "encoding/json" "strconv" ) const max_room_num = 2 var ( JSON = websocket.JSON // codec for JSON Message = websocket.Message // codec for string, []byte ActiveClients = make(map[string]ClientConn) // map containing clients //在線websocket列表 User = make(map[string]string) ) type ClientConn struct { websocket *websocket.Conn } type UserMsg struct { Room string Cmd string User string AvatarUrl string Content string Uuid string HandNum string GuessNum string } type UserInfo struct { User string AvatarUrl string Uuid string } type ReplyMsg struct { Room string Cmd string Data string } type GuessResult struct { Result string CurrentNum int HandRecord map[string]string GuessRecord map[string]string } func echoHandler(ws *websocket.Conn) { var err error var userMsg UserMsg for { var data []byte if err = websocket.Message.Receive(ws, &data); err != nil { fmt.Println("can't receive") break } err = json.Unmarshal(data, &userMsg) fmt.Println(userMsg) go wsHandler(ws,userMsg) } } func wsHandler(ws *websocket.Conn,userMsg UserMsg) { sockCli := ClientConn{ws} var err error redisClient := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", // no password set DB: 0, // use default DB }) //登錄 if userMsg.Cmd == "login" { fmt.Println("login") //判斷房間人數(shù)是否已滿 checkNumTmp := redisClient.SCard(userMsg.Room) checkNum := checkNumTmp.Val() if(checkNum < max_room_num) { fmt.Println("checkNum success") //socket用戶列表新增當(dāng)前用戶websocket連接 ActiveClients[userMsg.Uuid] = sockCli //用戶uuid保存到redis房間set集合內(nèi) redisClient.SAdd("ROOM:"+userMsg.Room,userMsg.Uuid) var me UserInfo me.User = userMsg.User me.AvatarUrl = userMsg.AvatarUrl me.Uuid = userMsg.Uuid //生成用戶信息json串 b, err := json.Marshal(me) if err != nil { fmt.Println("Encoding User Faild") } else { //保存用戶信息到redis redisClient.Set("USER:"+me.Uuid,b,0) //初始化用戶 initOnlineMsg(redisClient,userMsg) } } else { var rm ReplyMsg rm.Room = userMsg.Room rm.Cmd = "loginFailed" rm.Data = "登錄失敗,人數(shù)已滿" sendMsg,err2 := json.Marshal(rm) sendMsgStr := string(sendMsg) fmt.Println(sendMsgStr) if err2 != nil { } else { if err = websocket.Message.Send(ws, sendMsgStr); err != nil { log.Println("Could not send UsersList to ", userMsg.User, err.Error()) } } } //準(zhǔn)備 } else if userMsg.Cmd == "ready" { redisClient.Set("READY:"+userMsg.Uuid,"ready",0) //從redis取房間內(nèi)的所有用戶uuid roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room) //用戶uuid保存到一個(gè)go切片online online := roomSlice.Val() i := 0 //循環(huán)取在線用戶個(gè)人信息 if len(online) != 0 { for _, na := range online { if na != "" { userJson := redisClient.Get("READY:"+na) userJson2 := userJson.Val() if userJson2 == "ready" { i++ } } } } if i == len(online) && i == max_room_num { var rm ReplyMsg rm.Room = userMsg.Room rm.Cmd = "start" rm.Data = "" broadcast(redisClient,userMsg,rm) } //退出 } else if userMsg.Cmd == "logout" { fmt.Println("logout") //socket用戶列表刪除該用戶websocket連接 delete(ActiveClients,userMsg.Uuid) //從redis房間set集合內(nèi)刪除該用戶uuid redisClient.SRem("ROOM:"+userMsg.Room,userMsg.Uuid) //初始化用戶 initOnlineMsg(redisClient,userMsg) //出拳 } else if userMsg.Cmd == "guess" { var result string fmt.Println("guess") fmt.Println(userMsg.HandNum) fmt.Println(userMsg.GuessNum) myHandNum,_ := strconv.Atoi(userMsg.HandNum) myGuessNum,_ := strconv.Atoi(userMsg.GuessNum) redisClient.Set("HANDNUM:"+userMsg.Uuid,myHandNum,0) redisClient.Set("GUESSNUM:"+userMsg.Uuid,myGuessNum,0) //從redis取房間內(nèi)的所有用戶uuid roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room) //用戶uuid保存到一個(gè)go切片online online := roomSlice.Val() i := 0 //循環(huán)取在線用戶 if len(online) != 0 { for _, na := range online { if na != "" { handnumCmd := redisClient.Get("HANDNUM:"+na) handnum := handnumCmd.Val() if handnum != "" { i++ } } } } //房間內(nèi)所有人都已提交,則計(jì)算最后結(jié)果 if i == len(online) && i == max_room_num { var handRecordList map[string]string handRecordList = make(map[string]string) var guessRecordList map[string]string guessRecordList = make(map[string]string) //計(jì)算正確結(jié)果currentNum currentNum := 0 //循環(huán)取在線用戶 if len(online) != 0 { for _, na := range online { if na != "" { //取某用戶的出拳數(shù)據(jù),已用戶名為key,存入結(jié)果map handnumCmd := redisClient.Get("HANDNUM:"+na) handnum := handnumCmd.Val() guessnumCmd := redisClient.Get("GUESSNUM:"+na) guessnum := guessnumCmd.Val() userJson := redisClient.Get("USER:"+na) userJson2 := userJson.Val() var user UserInfo json.Unmarshal([]byte(userJson2), &user) handRecordList[user.User] = handnum guessRecordList[user.User] = guessnum //計(jì)算結(jié)果 thandnum,_ := strconv.Atoi(handnum) currentNum = currentNum + thandnum } } } //給各個(gè)用戶發(fā)送結(jié)果消息 if len(online) != 0 { for _, na := range online { if na != "" { guessnumCmd := redisClient.Get("GUESSNUM:"+na) guessnum := guessnumCmd.Val() tguessnum ,_ := strconv.Atoi(guessnum) if tguessnum == currentNum { result = "1" } else { result = "0" } var guessResult GuessResult guessResult.Result = result guessResult.CurrentNum = currentNum guessResult.HandRecord = handRecordList guessResult.GuessRecord = guessRecordList resultTmp,_ := json.Marshal(guessResult) resultData := string(resultTmp) //刪除用戶準(zhǔn)備狀態(tài) redisClient.Del("READY:"+na) //刪除用戶猜拳數(shù)據(jù) redisClient.Del("HANDNUM:"+na) redisClient.Del("GUESSNUM:"+na) var rm ReplyMsg rm.Room = userMsg.Room rm.Cmd = "result" rm.Data = resultData sendMsg,_ := json.Marshal(rm) sendMsgStr := string(sendMsg) if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil { log.Println("Could not send UsersList to ", "", err.Error()) } } } } } //發(fā)消息 } else { /* //從redis取房間內(nèi)的所有用戶uuid roomSlice := redisClient.SMembers(userMsg.Room) //用戶uuid保存到一個(gè)go切片online online := roomSlice.Val() //循環(huán)給房間內(nèi)用戶發(fā)送消息 if len(online) != 0 { for _, na := range online { if na != "" { //ActiveClients[na].websocket就是用戶對應(yīng)的websocket鏈接 if err = websocket.Message.Send(ActiveClients[na].websocket, userMsg.User+"說:"+userMsg.Content); err != nil { log.Println("Could not send message to ", userMsg.User, err.Error()) } } } }*/ } } //房間成員初始化,有人加入或者退出都要重新初始化,相當(dāng)于聊天室的在線用戶列表的維護(hù) func initOnlineMsg(redisClient *redis.Client,userMsg UserMsg) { var err error //從redis取房間內(nèi)的所有用戶uuid roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room) //用戶uuid保存到一個(gè)go切片online online := roomSlice.Val() var onlineList []string //循環(huán)取在線用戶個(gè)人信息 if len(online) != 0 { for _, na := range online { if na != "" { userJson := redisClient.Get("USER:"+na) userJson2 := userJson.Val() onlineList = append(onlineList,userJson2) } } } fmt.Println("get online success") //生成在線用戶信息json串 //c, err := json.Marshal(onlineList) onlineListStr,err2 := json.Marshal(onlineList) var rm ReplyMsg rm.Room = userMsg.Room rm.Cmd = "init" rm.Data = string(onlineListStr) sendMsg,err2 := json.Marshal(rm) sendMsgStr := string(sendMsg) fmt.Println("init") if err2 != nil { } else { //給所有用戶發(fā)初始化消息 if len(online) != 0 { for _, na := range online { if na != "" { if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil { log.Println("Could not send UsersList to ", "", err.Error()) } } } } //若房間人數(shù)滿,發(fā)送就緒消息 if len(online) >= max_room_num { fmt.Println("full") var rm ReplyMsg rm.Room = userMsg.Room rm.Cmd = "full" rm.Data = "" sendMsg,_ := json.Marshal(rm) sendMsgStr := string(sendMsg) for _, na := range online { if na != "" { if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil { log.Println("Could not send UsersList to ", "", err.Error()) } } } } } } //廣播消息 func broadcast(redisClient *redis.Client,userMsg UserMsg,rm ReplyMsg) { var err error //從redis取房間內(nèi)的所有用戶uuid roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room) //用戶uuid保存到一個(gè)go切片online online := roomSlice.Val() sendMsg,err2 := json.Marshal(rm) sendMsgStr := string(sendMsg) fmt.Println("broadcast") if err2 != nil { } else { //給所有用戶發(fā)消息 if len(online) != 0 { for _, na := range online { if na != "" { if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil { log.Println("Could not send UsersList to ", "", err.Error()) } } } } } } func main() { http.Handle("/echo", websocket.Handler(echoHandler)) http.Handle("/", http.FileServer(http.Dir("."))) err := http.ListenAndServe(":8929", nil) if err != nil { panic("ListenAndServe: " + err.Error()) } }
小程序代碼: //app.js App({ onLaunch: function () { console.log("App生命周期函數(shù)——onLaunch函數(shù)"); }, checkSession:function(mysessionid) { return new Promise(function(resolve, reject) { wx.request({ url: 'https://xxx.xxxxx.com/check.php', header: { sessionid:mysessionid }, success: function(res) { console.log("檢查sessionid是否有效") resolve(res.data) }, fail: function(e) { reject(e) } }) }) }, login:function() { return new Promise(function(resolve, reject) { wx.login({ success: function (res0) { if (res0.code) { wx.request({ url: 'https://xxx.xxxxx.com/login.php', data: { code: res0.code }, header: { 'content-type': 'application/json' }, success: function(res) { console.log("取得新的sessionid") console.log(res.data) var mysessionid = res.data.k wx.setStorageSync("mysessionid",mysessionid) var myuuid = res.data.v wx.setStorageSync("myuuid",myuuid) resolve(mysessionid) }, fail: function(e) { reject(e) } }) } } }) }) }, getWxUserInfo:function() { return new Promise(function(resolve, reject) { wx.getUserInfo({ withCredentials: false, success: function(res) { console.log("取得新的userInfo") var userInfo = res.userInfo wx.setStorageSync("userInfo",userInfo) console.log("setUserInfo") resolve(userInfo) } }) }) }, getUserInfo:function() { var that = this return new Promise(function(resolve, reject) { var mysessionid = wx.getStorageSync('mysessionid') if(mysessionid) { console.log("sessionid存在") that.checkSession(mysessionid).then(function(sessionContent){ if(sessionContent == 0) { console.log("sessionid無效-取userInfo存到本地") that.login().then(function(){ that.getWxUserInfo().then(function(userInfo){ resolve(userInfo) }) }) } else { console.log("sessionid有效-直接取本地userInfo") var userInfo = wx.getStorageSync("userInfo") resolve(userInfo) } }) } else { console.log("sessionid不存在,重新走登錄流程") that.login().then(function(){ that.getWxUserInfo().then(function(userInfo){ resolve(userInfo) }) }) } }) }, globalData:{ userInfo:null, onlineList:[], onlineStatus:false, myHandNum:0, myGuessNum:0 } }) page/index.js //index.js //獲取應(yīng)用實(shí)例 var app = getApp() Page({ data: { userInfo: {}, onlineList:{}, status:0, statusStr:"等待中", guessBoxStatus:"hideBox", handList:['0','1','2','3','4','5'], handStyleList:['primary','default','default','default','default','default'], guessList:['0','1','2','3','4','5','6','7','8','9','10'], guessStyleList:['primary','default','default','default','default','default','default','default','default','default','default'], buttonList:['0','1','2'], buttonStrList:['準(zhǔn)備','開始','提交'], buttonStyleList:['btnShow','btnHide','btnHide'], buttonFuncList:['ready','start','guess'] }, onLoad: function () { console.log("Page onLoad函數(shù)"); wx.playBackgroundAudio({ dataUrl: 'https://xxx.xxxxx.com/8585.mp3', title: '古琴音效', coverImgUrl: 'https://xxx.xxxxx.com/logo.png', success: function() { console.log("播放音效") } }) }, onHide: function() { console.log('發(fā)送注銷消息') var myuuid = wx.getStorageSync('myuuid') var msg = new Object(); msg.Room = '1'; msg.Cmd = 'logout'; msg.Uuid = myuuid; var str = JSON.stringify(msg) wx.sendSocketMessage({ data:str }) wx.closeSocket() app.globalData.onlineStatus = false }, onShow: function() { var that = this app.getUserInfo().then(function(userInfo){ that.setData({ userInfo:userInfo }) that.wsHandler(userInfo) that.initBox() }) }, wsHandler: function(userInfo) { var that = this //websocket wx.connectSocket({ url: 'wss://xx.xxxxx.com/echo' }) wx.onSocketOpen(function(res) { console.log('WebSocket連接已打開!') var myuuid = wx.getStorageSync('myuuid') var msg = new Object(); msg.Room = '1'; msg.Cmd = 'login'; msg.User = userInfo.nickName; msg.AvatarUrl = userInfo.avatarUrl; msg.Uuid = myuuid; var str = JSON.stringify(msg) wx.sendSocketMessage({ data:str }) }) wx.onSocketMessage(function(res) { var msg = JSON.parse(res.data) if(msg.Cmd == 'init') { var userList = JSON.parse(msg.Data) app.globalData.onlineList = [] for(var i=0;i<userList.length;i++){ var user = JSON.parse(userList[i]) app.globalData.onlineList.push(user) } that.setData({ onlineList:app.globalData.onlineList, status:0, statusStr:'等待中' }) } if(msg.Cmd == 'full') { that.setData({ status:1, statusStr:'準(zhǔn)備開始' }) } if(msg.Cmd == 'result') { var result = JSON.parse(msg.Data) var content = "總數(shù)為"+result.CurrentNum+"\n" for (var value in result.HandRecord) { content = content+value+"出拳:"+result.HandRecord[value]+"\n"; } for (var value in result.GuessRecord) { content = content+value+"猜拳:"+result.GuessRecord[value]+"\n"; } if(result.Result == 1) { content = "恭喜你,猜中啦\n" + content wx.showModal({ content: content, showCancel: false, success: function (res) { if (res.confirm) { that.initBox() } } }); } if(result.Result == 0) { content = "很遺憾,猜錯(cuò)啦\n" + content wx.showModal({ content: content, showCancel: false, success: function (res) { if (res.confirm) { that.initBox() } } }); } } if(msg.Cmd == 'start') { that.setData({ status:2, statusStr:'游戲中', guessBoxStatus:'showBox', buttonStyleList:['btnHide','btnHide','btnShow'], }) } }) }, setHandNum: function(event) { var that = this console.log(event.target.dataset.handnum) app.globalData.myHandNum = event.target.dataset.handnum var myList = that.data.handStyleList for(var i=0;i<myList.length;i++) { if(i == event.target.dataset.handnum) { myList[i] = 'primary' } else { myList[i] = 'default' } } that.setData({ handStyleList:myList }) }, setGuessNum: function(event) { var that = this console.log(event.target.dataset.guessnum) app.globalData.myGuessNum = event.target.dataset.guessnum var myList = that.data.guessStyleList for(var i=0;i<myList.length;i++) { if(i == event.target.dataset.guessnum) { myList[i] = 'primary' } else { myList[i] = 'default' } } that.setData({ guessStyleList:myList }) }, guess: function() { var that = this var userInfo = that.data.userInfo var myuuid = wx.getStorageSync('myuuid') var msg = new Object(); msg.Room = '1'; msg.Cmd = 'guess'; msg.User = userInfo.nickName; msg.AvatarUrl = userInfo.avatarUrl; msg.Uuid = myuuid; msg.HandNum = app.globalData.myHandNum msg.GuessNum = app.globalData.myGuessNum var str = JSON.stringify(msg) wx.sendSocketMessage({ data:str }) }, ready: function() { var that = this var userInfo = that.data.userInfo var myuuid = wx.getStorageSync('myuuid') var msg = new Object(); msg.Room = '1'; msg.Cmd = 'ready'; msg.User = userInfo.nickName; msg.AvatarUrl = userInfo.avatarUrl; msg.Uuid = myuuid; var str = JSON.stringify(msg) wx.sendSocketMessage({ data:str }) that.setData({ status:1, statusStr:'等待對手,準(zhǔn)備開始', buttonStyleList:['btnHide','btnHide','btnHide'], }) }, start: function() { var that = this var userInfo = that.data.userInfo var myuuid = wx.getStorageSync('myuuid') var msg = new Object(); msg.Room = '1'; msg.Cmd = 'start'; msg.User = userInfo.nickName; msg.AvatarUrl = userInfo.avatarUrl; msg.Uuid = myuuid; var str = JSON.stringify(msg) wx.sendSocketMessage({ data:str }) }, initBox: function() { var that = this that.setData({ status:0, statusStr:"等待中", guessBoxStatus:"hideBox", handList:['0','1','2','3','4','5'], handStyleList:['primary','default','default','default','default','default'], guessList:['0','1','2','3','4','5','6','7','8','9','10'], guessStyleList:['primary','default','default','default','default','default','default','default','default','default','default'], buttonList:['0','1','2'], buttonStrList:['準(zhǔn)備','開始','提交'], buttonStyleList:['btnShow','btnHide','btnHide'], buttonFuncList:['ready','start','guess'] }) }, getAudioStatus: function() { wx.getBackgroundAudioPlayerState({ success: function(res) { var status = res.status var dataUrl = res.dataUrl var currentPosition = res.currentPosition var duration = res.duration var downloadPercent = res.downloadPercent console.log("音樂狀態(tài)"+status) console.log("音樂長度"+duration) } }) } })
ps:播放音樂的功能,在開發(fā)工具可以看到,真機(jī)上沒有聽到聲音,暫時(shí)還沒找到解決辦法 check.php <?php $post_data = $_POST; $header = get_all_headers(); $sessionid = $header['sessionid']; $host = '127.0.0.1'; $port = '6379'; $timeout = 0; $redis = new Redis(); $redis->connect($host, $port, $timeout); $session_content = $redis->get("miniappsession:".$sessionid); if($session_content) { echo $session_content; } else { echo 0; } /** * 獲取自定義的header數(shù)據(jù) */ function get_all_headers(){ // 忽略獲取的header數(shù)據(jù) $ignore = array('host','accept','content-length','content-type'); $headers = array(); foreach($_SERVER as $key=>$value){ if(substr($key, 0, 5)==='HTTP_'){ $key = substr($key, 5); $key = str_replace('_', ' ', $key); $key = str_replace(' ', '-', $key); $key = strtolower($key); if(!in_array($key, $ignore)){ $headers[$key] = $value; } } } return $headers; } login.php <?php $code = $_GET['code']; define("APPID",'xxxxxxxxxxxxxxxxxxxxx'); define("SECRET",'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'); $url = "https://api.weixin.qq.com/sns/jscode2session?appid=".APPID."&secret=".SECRET."&js_code=".$code."&grant_type=authorization_code"; $rs = curlGet($url); $arr = json_decode($rs); $str = randomFromDev(32); $host = '127.0.0.1'; $port = '6379'; $timeout = 0; $redis = new Redis(); $redis->connect($host, $port, $timeout); $expires_time = 15*24*60*60; $session_content = md5($arr->openid.$expires_time); $redis->setex("miniappsession:".$str,$expires_time,$session_content); $sessionObj['k'] = $str; $sessionObj['v'] = $session_content; echo json_encode($sessionObj); function randomFromDev($len) { $fp = @fopen('/dev/urandom','rb'); $result = ''; if ($fp !== FALSE) { $result .= @fread($fp, $len); @fclose($fp); } else { trigger_error('Can not open /dev/urandom.'); } $result = md5($result); // convert from binary to string //$result = base64_encode($result); // remove none url chars //$result = strtr($result, '+/', '-_'); // Remove = from the end //$result = str_replace('=', ' ', $result); return $result; } function curlGet($url, $method = 'get', $data = '') { $ch = curl_init(); $header = 'Accept-Charset: utf-8'; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method)); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)'); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_AUTOREFERER, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $temp = curl_exec($ch); return $temp; } |
工作日 8:30-12:00 14:30-18:00
周六及部分節(jié)假日提供值班服務(wù)