江西小程序-打造一款简单猜拳小程序(go+websocket+redis+mysql+小程序前端)(二)
本节思路
1、redis的go客户端安装
2、基于redis的set集合,实现房间的概念,一个房间对应一个set集合,集合内保存该房间内用户的唯一标识
我们给每个用户生成了唯一标识uuid(后期接入微信小程序,则可以使用微信用户openid代替),于是set集合大致如下:
房间1
用户A-uuid
用户B-uuid
......
房间2
用户C-uuid
用户D-uuid
......
......
3、用户的uuid,又对应着go服务端里面的一个map
ActiveClients = make(map[string]ClientConn)
该map以用户的uuid为key,在线用户的websocket链接为value
于是在发送消息时,取到redis里某房间内所有的uuid,就可以得到对应的websocket链接,实现房间内的广播
如果限制房间内只有2个用户,则实现了一对一私聊
安装redis的go客户端
go get -u github.com/go-redis/redis
服务器端go代码:
package main
import (
"golang.org/x/net/websocket"
"fmt"
"log"
"net/http"
"github.com/go-redis/redis"
"encoding/json"
)
var (
JSON = websocket.JSON // codec for JSON
Message = websocket.Message // codec for string, []byte
ActiveClients = make(map[string]ClientConn) // map containing clients
User = make(map[string]string)
)
type ClientConn struct {
websocket *websocket.Conn
}
type UserMsg struct {
Room string
Cmd string
User string
Content string
Uuid 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")
//用户列表新增当前用户
ActiveClients[userMsg.Uuid] = sockCli
//保存到redis房间set集合内
redisClient.SAdd(userMsg.Room,userMsg.Uuid)
//退出
} else if userMsg.Cmd == "logout" {
fmt.Println("logout")
delete(ActiveClients,userMsg.Uuid)
redisClient.SRem(userMsg.Room,userMsg.Uuid)
//发消息
} 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 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())
}
}
为了生成唯一uuid,选择房间号和用户名,客户端代码由php实现(chat.php):
<?php
$room = $_GET["room"];
$username = $_GET["username"];
$uuid = md5(time().$username);
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Sample of websocket with golang</title>
<script src="//cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script>
$(function() {
var userAgent = navigator.userAgent;
var ws = new WebSocket("ws://xxx.xx.xxx.xx:8929/echo");
ws.onopen = function (evt) {
//连接成功
console.log("Connected to WebSocket server.");
//发送登录信息
msg = new Object();
msg.Room = '<?php echo $room;?>';
msg.Cmd = 'login';
msg.User = '<?php echo $username;?>';
msg.Uuid = '<?php echo $uuid;?>';
ws.send(JSON.stringify(msg));
};
ws.onclose = function (evt) {
console.log("Close WebSocket server.");
//发送退出信息
msg = new Object();
msg.Room = '<?php echo $room;?>';
msg.Cmd = 'logout';
msg.User = '<?php echo $username;?>';
msg.Uuid = '<?php echo $uuid;?>';
ws.send(JSON.stringify(msg));
};
ws.onmessage = function(e) {
$('<li>').text(event.data).appendTo($ul);
};
var $ul = $('#msg-list');
$('#sendBtn').click(function(){
//发送信息
msg = new Object();
msg.Room = '<?php echo $room;?>';
msg.Cmd = 'send';
msg.User = '<?php echo $username;?>';
msg.Uuid = '<?php echo $uuid;?>';
msg.Content = $('#name').val();
ws.send(JSON.stringify(msg));
});
});
</script>
</head>
<body>
<input id="name" type="text"/>
<input type="button" id="sendBtn" value="send"/>
<ul id="msg-list"></ul>
</body>
</html>
两个不同浏览器下,分别访问http://yourdomain/chat.php?room=1&username=keyunq1
http://yourdomain/chat.php?room=1&username=keyunq2
则可实现一对一私聊。
不完善之处:对于redis里房间的set集合,程序没有实现删除元素功能,如某用户的websocket链接关闭,set集合里未删除该用户的uuid,则发消息时,继续从set集合里取数据循环,则会发送数据到一个不存在的websocket,产生错误,此处待完善。
[...]本节思路:基于本系列第二节内容,实现一个websocket服务端,redis为存储介质,以自己约定的cmd命令为简单通讯协议,与小程序进行通讯,实现简单的2人猜拳功能[...]
[...]分享者:keyunq,来自原文地址[...]