宝哥软件园

创建一个猜谜小程序(go websocket redis mysql小程序前端)《四》.

编辑:宝哥软件园 来源:互联网 时间:2021-12-14

本节思路:基于本系列第二节的内容,实现一个websocket服务器,使用redis作为存储介质,使用自己约定的cmd命令作为简单的通信协议,与小程序进行通信,实现简单的2人猜测功能。

Cmd命令遵循以下过程:登录(client-server),小程序将登录命令发送到服务器。服务器收到命令后,判断房间号是否已满。如果未满,则保存客户端websocket并连接到列表,将客户端用户信息存储在redis中,更新在线用户列表,然后发送init(server-client)命令通知所有在线用户并更新在线用户列表。

当客户端单击ready按钮时,它会向服务器发送ready(client-server)命令,服务器会更新用户的ready标志(存储在redis中),计算房间中的人数是否已满,以及房间中的所有用户是否都设置了ready标志。如果是这样,它会向所有用户发送start(服务器-客户端)命令来启动游戏。

客户端选择自己的打孔信息,向服务器发送猜测(client-server)命令,服务器记录用户的打孔数据,判断房间里的每个人都在提交猜测命令,计算最终结果,向所有用户发送结果(server-client)命令。

接收到结果命令后,客户端重新进入就绪流程。

如果小程序退出,客户端向服务器发送注销(客户端-服务器)命令,服务器从列表中删除用户,向所有其他在线用户重新发送init(服务器-客户端)命令,并更新在线用户列表。

命令流程如下:登录(客户端-服务器)初始化(服务器-客户端)就绪(客户端-服务器)启动(服务器-客户端)猜测(客户端-服务器)结果(服务器-客户端)注销(客户端-服务器)

示意图:登录

打造一款猜拳小程序(go+websocket+redis+mysql+小程序前端)《四》 ...(图1)

准备好的

打造一款猜拳小程序(go+websocket+redis+mysql+小程序前端)《四》 ...(图2)

开始

打造一款猜拳小程序(go+websocket+redis+mysql+小程序前端)《四》 ...(图3)

结果

打造一款猜拳小程序(go+websocket+redis+mysql+小程序前端)《四》 ...(图4)

Go服务器

package main import(' golang . org/x/net/web socket ' ' fmt ' ' log ' ' net/http ' ' github . com/go-redis/redis ' ' encoding/JSON ' ' strconv ')const max _ room _ num=2var(JSON=web socket。JSON消息=websocket的JSON///编解码器。混乱

age // 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") //判断房间人数是否已满 checkNumTmp := redisClient.SCard(userMsg.Room) checkNum := checkNumTmp.Val() if(checkNum < max_room_num) { fmt.Println("checkNum success") //socket用户列表新增当前用户websocket连接 ActiveClients[userMsg.Uuid] = sockCli //用户uuid保存到redis房间set集合内 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 = "登录失败,人数已满" 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()) } } } //准备 } else if userMsg.Cmd == "ready" { redisClient.Set("READY:"+userMsg.Uuid,"ready",0) //从redis取房间内的所有用户uuid roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room) //用户uuid保存到一个go切片online online := roomSlice.Val() i := 0 //循环取在线用户个人信息 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集合内删除该用户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取房间内的所有用户uuid roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room) //用户uuid保存到一个go切片online online := roomSlice.Val() i := 0 //循环取在线用户 if len(online) != 0 { for _, na := range online { if na != "" { handnumCmd := redisClient.Get("HANDNUM:"+na) handnum := handnumCmd.Val() if handnum != "" { i++ } } } } //房间内所有人都已提交,则计算最后结果 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) //计算正确结果currentNum currentNum := 0 //循环取在线用户 if len(online) != 0 { for _, na := range online { if na != "" { //取某用户的出拳数据,已用户名为key,存入结果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 //计算结果 thandnum,_ := strconv.Atoi(handnum) currentNum = currentNum + thandnum } } } //给各个用户发送结果消息 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) //删除用户准备状态 redisClient.Del("READY:"+na) //删除用户猜拳数据 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()) } } } } } //发消息 } else { /* //从redis取房间内的所有用户uuid roomSlice := redisClient.SMembers(userMsg.Room) //用户uuid保存到一个go切片online online := roomSlice.Val() //循环给房间内用户发送消息 if len(online) != 0 { for _, na := range online { if na != "" { //ActiveClients[na].websocket就是用户对应的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()) } } } }*/ }}//房间成员初始化,有人加入或者退出都要重新初始化,相当于聊天室的在线用户列表的维护func initOnlineMsg(redisClient *redis.Client,userMsg UserMsg) { var err error //从redis取房间内的所有用户uuid roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room) //用户uuid保存到一个go切片online online := roomSlice.Val() var onlineList []string //循环取在线用户个人信息 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 { //给所有用户发初始化消息 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()) } } } } //若房间人数满,发送就绪消息 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取房间内的所有用户uuid roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room) //用户uuid保存到一个go切片online online := roomSlice.Val() sendMsg,err2 := json.Marshal(rm) sendMsgStr := string(sendMsg) fmt.Println("broadcast") if err2 != nil { } else { //给所有用户发消息 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.jsApp({  onLaunch: function () {    console.log("App生命周期函数——onLaunch函数");  },  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//获取应用实例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:['准备','开始','提交'],    buttonStyleList:['btnShow','btnHide','btnHide'],    buttonFuncList:['ready','start','guess']  },  onLoad: function () {    console.log("Page onLoad函数");    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('发送注销消息')    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:'准备开始'        })      }      if(msg.Cmd == 'result') {                var result = JSON.parse(msg.Data)        var content = "总数为"+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 = "很遗憾,猜错啦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:'等待对手,准备开始',      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:['准备','开始','提交'],      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("音乐状态"+status)          console.log("音乐长度"+duration)      }    })  }})

ps:播放音乐的功能,在开发工具可以看到,真机上没有听到声音,暂时还没找到解决办法代码的健壮性,页面效果还需要再优化

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数据 */function get_all_headers(){    // 忽略获取的header数据    $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;}
更多资讯
游戏推荐
更多+