AD:靠谱的微信小程序外包开发

admin

admin

发表于 2017-07-26 21:05:02

摘要:微信小程序开发者社区(www.mntuku.cn)为您提供:打造一款猜拳小程序(go+websocket+redis+mysql+小程序前端)《四》 ...
相关的资讯,希望对您有帮助。

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

本节思路:基于本系列第二节内容,实现一个websocket服务端,redis为存储介质,以自己约定的cmd命令为简单通讯协议,与小程序进行通讯,实现简单的2人猜拳功能cmd命令按照如下流程:login(client-server),小程序发给 ...

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

cmd命令按照如下流程:
login(client->server),小程序发给服务端,登录命令,服务端接收命令后,判断房间人数是否已满,未满则保存客户端websocket连接到列表,客户端用户信息存入redis,更新在线用户列表,然后发送init(server->client)命令,通知所有在线用户,更新在线用户列表。

当客户端点击准备按钮,发送ready(client->server)命令到服务端,服务端更新该用户的ready标识位(存于redis),计算房间人数是否已满,以及房间所有用户是否都已设置ready标识位,如是,则发送start(server->client)命令给所有用户,开始游戏。

客户端选择好自己的出拳信息,发送guess(client->server)命令到服务端,服务端记录该用户出拳数据,判断房间内所有人都以提交guess命令,则计算最后结果,发送result(server->client)命令给所有用户。

客户端接收到result命令后,重新进入ready流程。

如退出小程序,客户端发送logout(client->server)命令到服务端,服务端从列表中删除该用户,重新发送init(server->client)命令到所有其他在线用户,更新在线用户列表。

命令流程如下:
login(client->server)
init(server->client)
ready(client->server)
start(server->client)
guess(client->server)
result(server->client)
logout(client->server)

效果示意图:
login

ready

start

result

go服务端

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(cant 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.js
App({
  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

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

check.php

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

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;
}

源码:https://github.com/keyunq/ytguess

下载:ytguess-master.zip


17阅读 | 0评论
你的回应
写文章

8049