用小程序WebSocket做一个开房游戏-原理解析

最近看小程序交流群里面很多人在讨论WebSocket的用法,刚好这段时间接触了小程序的WebSocket,但是网上教程不多,遂加入自己的一些想法和实现,写下此篇文章,若有不妥,敬请斧正。

WebSocket实现原理

00

浏览器通过发起请求,服务器接收到浏览器的请求,发送给浏览器响应,此时完成了“握手”的过程。在WebSocket API中,浏览器和服务器只需要一次握手,即可进行双向数据传输,也就是他们之间形成了一条双向通道,互相可以发送和接收数据。此时服务器不再被动接收浏览器的请求之后才返回数据,而是可以自主推送数据给浏览器。

小程序客户端API

01

说到底也就是客户端要发起请求,服务端做接收。那么小程序中如何发起一个WebSocket请求呢?当然,答案是翻看小程序官方开发文档,具体请自行翻阅,这里不做解释。

这是官方文档中WebSocket的API,各个API的释义看命名就可以很明显的知道用途。那么我们直接看流程吧。

首先创建一个房间,此时应该发起一个WebSocket连接,与服务端建立一个长连接。创建房间时随机一个房间ID,再发起WebSocket连接,如下:

url为服务器WebSocket地址,参数带sid和roomId方便服务端处理此次连接是哪个用户发起的,下面服务端的解析会说明。

同时,在建立连接的时候,还要初始化一下出错后的监听回调事件。

其中,连接发生错误和连接关闭是两个不同的情况。连接发生错误有可能是服务端在连接成功后,挂掉了,或者因为网络原因,此次连接断开了。连接关闭是指此次连接断开,并会返回断开的原因。

此时就已经建立起了与服务器的WebSocket连接,接下来我们要监听收发消息和处理了。

定义了2个方法,只要调用相应的方法即可接收或者发送数据了。

服务端设计

02

难度其实在于服务端的设计,小程序客户端只要处理收到的和发送请求就可以了,可服务端的设计会涉及比较多的层面。我们接下来讨论如何做到开房间的模式。由于个人比较习惯于java,所以用的是java实现的,如果想采用其他语言,相信了解下实现原理,也可以采用其他语言实现了。

//新连接

public void afterConnectionEstablished(WebSocketSession webSocketSession)

首先建立起新连接的时候会调用这个方法,此时我们可以做一些数据的初步处理。

首先拿到客户端传上来的参数,sid和roomId。在经过数据库换算后得到当前用户的实体entity,此时还有当前用户连接的一个WebSocketSession,我们可以将他们建立起关系。

拿到WebSocketSession可以取到WebSocket的id,此id从服务的启动后每个连接自增1,所以服务有效期间内,是唯一的。可以此来做Map的key,建立对应关系。id -> UserConnectInfo(包括用户基本信息、所属房间号、WebSocketSession)

String id = webSocketSession.getId();

那么每一次连接获得的WebSocketSession我们都可以获取id,到map里面查找,便知道是新连接或者是哪个用户发起的。

以roomId为key,将用户实体也丢进去,就可以知道该房间里有哪些用户,在房间众多的情况下,不会造成数据混乱。

roomId -> List

//收到消息

protected void handleTextMessage(WebSocketSession session, TextMessage message)

此方法是在客户端调用sendMessage方法时,服务端的响应。

服务端在该方法中,调用message.getPayload()即可取到从客户端送上来的data,格式需要自己转换。如果嫌麻烦,用字符串挺好。取当前用户的信息,则使用之前的idToUserMap.get(session.getId());可以获取到当前用户的实体信息了,并且可以获得到该用户是哪个房间的,通过房间id调用roomToUsersMap.get(roomId);就可知道当前房间还有哪些人了。

实例

【左图为房主视角,右图为玩家视角】

当房主创建房间时,roomToUsersMap中并没有这个key,所以将创建房间的人定为房主,在UserConnectInfo设置房主标志,以便客户端处理。

【左图为房主视角,右图为玩家视角】

当玩家点击准备按钮时,客户端调用了sendMessage()这个方法,并且传入了data:"ready",服务端通过message.getPayload()拿到了“ready”,可以判断是用户点击了准备按钮,此时可以做准备的流程:

1.发送给客户端的消息类型设置为“准备”的枚举。

2.将当前发送准备请求的用户,设置为准备状态。

3.计算已准备的总人数。

4.群发给客户端,告知所有人xx已准备。

5.客户端收到通知,通过type判断枚举,确认是ready的流程,就走准备的步骤,将xx用户的状态改为准备,显示出来。

总结:点击开始以及后续的步骤,都可以参考准备过程来做。流程都是客户端发一个请求,服务端判断这个请求是谁,想做什么,做完之后,群发或者单发,客户端接收到数据,进行处理。

//关闭连接

public void afterConnectionClosed(WebSocketSession session, CloseStatus status)

当客户端切断中止WebSocket请求后,服务端会调用这个方法。这个方法就用来去除map中已断开连接的用户。可以用来做到:当房主撤走时,下一个人就是房主。

------有点乱?------

画图比较直观点。来,上图。

对于map的映射关系,不怕,也有图!

服务器Nginx转发设置

03

众所周知,腾讯的限制比较恶心变态,但是你想做,只能遵守他们的规则。http要求是https,当然WebSocket的ws也要求是wss。那么如何满足wss呢?

如果服务器还不支持https的请求,没有申请证书的各位,此处不再讨论。有配置了https证书的童鞋们,如何配置wss呢?ok,百度之。

首先需要设置一个default upgrade,这是根据nginx官网来的。

在443下面新增一个websocket的映射,对应到服务端的地址,需要注意的是如果没有设置连接时长,由于nginx默认的限制,会在没有数据交互的前提下,60s左右就会断开连接。所以可以根据需要设置长短,此处设置为20分钟。

-没有了-

一个好尼玛随意的公众号

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180402G0ZGR100?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励