前言

前两章教程,我们使用WebSocket的基础特性打造了一个小小聊天室,并在第二章对其进行了集群化改造。

系列教程回顾:

[WebSocket]第一章:手把手搭建WebSocket多人在线聊天室(SpringBoot+WebSocket)

[WebSocket]第二章:WebSocket集群分布式改造——实现多人在线聊天室

在本文中,我将介绍如何使用WebSocket向实时多人答题对战游戏提供服务端,并详细介绍通接口的设计。

这是我在最近作业竞赛中设计的小项目,和小伙伴们一起设计了整个游戏流程和后端代码,前端页面暂时就不放开给大家了,大家可以参考前两章教程自己动手写一下前端页面。

本文内容摘要:

  • 在线游戏常用的通讯方案
  • 如何使用WebSocket实现游戏对战实时通信
  • 游戏步骤的画面演示和对应的WebSocket接口设计

本文源码:(妈妈再也不用担心我无法复现文章代码啦)

https://github.com/qqxx6661/websocket-game-demo

正文

WebSocket实现在线多人游戏——对战答题

在线游戏常用的通讯方案

参考:

https://blog.csdn.net/honey199396/article/details/54603860

HTTP

优点:协议较成熟,应用广泛、基于TCP/IP,拥有TCP优点、研发成本很低,开发快速、开源软件较多,nginx,apache,tomact等

缺点:无状态无连接、只有PULL模式,不支持PUSH、数据报文较大

特性:基于TCP/IP应用层协议、无状态,无连接、支持C/S模式、适用于文本传输

TCP

优点:可靠性 、全双工协议、开源支持多、应用较广泛、面向连接、研发成本低、报文内容不限制(IP层自动分包,重传,不大于1452bytes)

缺点:操作系统:较耗内存,支持连接数有限、设计:协议较复杂,自定义应用层协议、网络:网络差情况下延迟较高、传输:效率低于UDP协议

特性:面向连接、可靠性、全双工协议、基于IP层、OSI参考模型位于传输层、适用于二进制传输

WebScoket

优点:协议较成熟、基于TCP/IP,拥有TCP优点、数据报文较小,包头非常小、面向连接,有状态协议、开源较多,开发较快

缺点:

特性:有状态,面向连接、数据报头较小、适用于WEB3.0,以及其他即时联网通讯

UDP

优点:操作系统:并发高,内存消耗较低、传输:效率高,网络延迟低、传输模型简单,研发成本低

缺点:协议不可靠、单向协议、开源支持少、报文内容有限,不能大于1464bytes、设计:协议设计较复杂、网络:网络差,而且丢数据报文

特性:无连接,不可靠,基于IP协议层,OSI参考模型位于传输层,最大努力交付,适用于二进制传输

总结

  • 对于弱联网类游戏,必须消除类的,卡牌类的,可以直接HTTP协议,考虑安全的话直接HTTPS,或者对内容体做对称加密;
  • 对于实时性,交互性要求较高,可以优先选择Websocket,其次TCP协议;
  • 对于实时性要求极高,且可达性要求一般可以选择UDP协议;
  • 局域网对战类,赛车类,直接来UDP协议吧;

WebSocket实现双人在线游戏实时通信

我们采用websocket作为我们的通信方案,主要是因为我们希望对战双方能够实时显示对方的得分。

本小节详细介绍了我们在线问答对战游戏中,具体的websocket通讯方式定义。

本问答游戏规则如下:

  • 用户打开h5页面后,输入自己的昵称,发送给服务端,服务端将用户昵称保存到hashmap,并记录用户状态(空闲,游戏中),接着用户进入大厅。
  • 大厅中用户可以互相选择,一旦某用户选择了另一位用户,将触发开始游戏,双方进入答题模式。
  • 答题的两位用户各回答10题,每题答对为10分,共100分,左上角页面显示自己的分数,右上角显示对方分数,实时通过websocket接收对方分数。
  • 10题结束,双方等待对方总分,最后判断输赢,显示结果界面。

所以我们需要设计三个WebSocket协议:

  • 用户创建昵称,进入玩家大厅
  • 用户选择对手,双方进入游戏
  • 对战过程实时显示双方分数

接下来详细介绍这三种WebSocket接口

用户创建昵称,进入玩家大厅

打开界面,进入游戏:

我们使用了HashMap存储用户状态,

private Map<String, StatusEnum> userToStatus = new HashMap<>();

用户状态分为空闲和游戏中:

public enum StatusEnum {IDLE,IN_GAME
}

WebSocket接口设计如下:

WebSocket接口代码如下:

@MessageMapping("/game.add_user")@SendTo("/topic/game")public MessageReply addUser(@Payload ChatMessage chatMessage, SimpMessageHeaderAccessor headerAccessor) throws JsonProcessingException {MessageReply message = new MessageReply();String sender = chatMessage.getSender();ChatMessage result = new ChatMessage();result.setType(MessageTypeEnum.ADD_USER);result.setReceiver(Collections.singletonList(sender));if (userToStatus.containsKey(sender)) {message.setCode(201);message.setStatus("该用户名已存在");message.setChatMessage(result);log.warn("addUser[" + sender + "]: " + message.toString());} else {result.setContent(mapper.writeValueAsString(userToStatus.keySet().stream().filter(k -> userToStatus.get(k).equals(StatusEnum.IDLE)).toArray()));message.setCode(200);message.setStatus("成功");message.setChatMessage(result);userToStatus.put(sender, StatusEnum.IDLE);headerAccessor.getSessionAttributes().put("username",sender);log.warn("addUser[" + sender + "]: " + message.toString());}return message;}

用户选择对手,双方进入游戏

在大厅中选择玩家,随后会进入对战:

我们使用了HashMap存储了正在对战的用户,给双方配对。

private Map<String, String> userToPlay = new HashMap<>();

WebSocket接口设计如下:

WebSocket接口代码如下:

@MessageMapping("/game.choose_user")@SendTo("/topic/game")public MessageReply chooseUser(@Payload ChatMessage chatMessage) throws JsonProcessingException {MessageReply message = new MessageReply();String receiver = chatMessage.getContent();String sender = chatMessage.getSender();ChatMessage result = new ChatMessage();result.setType(MessageTypeEnum.CHOOSE_USER);if (userToStatus.containsKey(receiver) && userToStatus.get(receiver).equals(StatusEnum.IDLE)) {List<QuestionRelayDTO> list=new ArrayList<>();questionService.getQuestions(limit).forEach(item->{QuestionRelayDTO relayDTO=new QuestionRelayDTO();relayDTO.setTopic_id(item.getId());relayDTO.setTopic_name(item.getQuestion());List<Answer> answers=new ArrayList<>();answers.add(new Answer(1,item.getId(),item.getOptionA(),item.getResult()==1?1:0));answers.add(new Answer(2,item.getId(),item.getOptionB(),item.getResult()==2?1:0));answers.add(new Answer(3,item.getId(),item.getOptionC(),item.getResult()==3?1:0));answers.add(new Answer(4,item.getId(),item.getOptionD(),item.getResult()==4?1:0));relayDTO.setTopic_answer(answers);list.add(relayDTO);});result.setContent(mapper.writeValueAsString(list));result.setReceiver(Arrays.asList(sender, receiver));message.setCode(200);message.setStatus("匹配成功");message.setChatMessage(result);userToStatus.put(receiver, StatusEnum.IN_GAME);userToStatus.put(sender, StatusEnum.IN_GAME);userToPlay.put(receiver,sender);userToPlay.put(sender,receiver);log.warn("chooseUser[" + sender + "," + receiver + "]: " + message.toString());} else {result.setContent(mapper.writeValueAsString(userToStatus.keySet().stream().filter(k -> userToStatus.get(k).equals(StatusEnum.IDLE)).toArray()));result.setReceiver(Collections.singletonList(sender));message.setCode(202);message.setStatus("该用户不存在或已在游戏中");message.setChatMessage(result);log.warn("chooseUser[" + sender + "]: " + message.toString());}return message;}

对战过程实时显示双方分数

对战过程中的演示图:左边显示我方分数,右边显示对方分数

WebSocket接口设计如下:

WebSocket接口代码如下:

@MessageMapping("/game.do_exam")@SendTo("/topic/game")public MessageReply doExam(@Payload ChatMessage chatMessage) throws JsonProcessingException {MessageReply message = new MessageReply();String sender = chatMessage.getSender();String receiver = userToPlay.get(sender);ChatMessage result = new ChatMessage();result.setType(MessageTypeEnum.DO_EXAM);log.warn("userToStatus:" + mapper.writeValueAsString(userToStatus));if (userToStatus.containsKey(receiver) && userToStatus.get(receiver).equals(StatusEnum.IN_GAME)) {result.setContent(chatMessage.getContent());result.setSender(sender);result.setReceiver(Collections.singletonList(receiver));message.setCode(200);message.setStatus("成功");message.setChatMessage(result);log.warn("doExam[" + receiver + "]: " + message.toString());}else{result.setReceiver(Collections.singletonList(sender));message.setCode(203);message.setStatus("该用户不存在或已退出游戏");message.setChatMessage(result);log.warn("doExam[" + sender + "]: " + message.toString());}return message;}

进一步

这个只是个两天赶出来的Demo,当然里成品还有非常大的差距。这里有几个需要继续解决的事情:

  • 实现自动匹配/排行榜
  • WebSocket通讯优化:在某些地方使用点对点通讯,而非全部使用广播通讯。

我们可以使用convertAndSendToUser()方法,按照名字就可以判断出来,convertAndSendToUser()方法能够让我们给特定用户发送消息。

spring webscoket能识别带”/user”的订阅路径并做出处理,例如,如果浏览器客户端,订阅了’/user/topic/greetings’这条路径,

stompClient.subscribe('/user/topic/greetings', function(data) {//...
});

就会被spring websocket利用UserDestinationMessageHandler进行转化成”/topic/greetings-usererbgz2rq”,”usererbgz2rq”中,user是关键字,erbgz2rq是sessionid,这样子就把用户和订阅路径唯一的匹配起来了

参考文献

点对点通讯:

https://blog.csdn.net/yingxiake/article/details/51224569

总结

我们在本文中实现了在线多人对战游戏的服务端WebSocket接口设计,进一步巩固了对WebSocket的基础和应用范围的理解。

本文工程源代码:

https://github.com/qqxx6661/websocket-game-demo

关注我

我目前是一名后端开发工程师。主要关注后端开发,数据安全,爬虫,边缘计算等方向。

微信:yangzd1102(请注明来意)

Github:@qqxx6661

个人博客:

  • CSDN:@Rude3Knife
  • 知乎:@Zhendong
  • 简书:@蛮三刀把刀
  • 掘金:@蛮三刀把刀

原创博客主要内容

  • Java知识点复习全手册
  • Leetcode算法题解析
  • 剑指offer算法题解析
  • SpringCloud菜鸟入门实战系列
  • SpringBoot菜鸟入门实战系列
  • 爬虫相关技术文章
  • 后端开发相关技术文章

个人公众号:后端技术漫谈

如果文章对你有帮助,不妨收藏起来并转发给您的朋友们~

[WebSocket]使用WebSocket实现实时多人答题对战游戏相关推荐

  1. .net 实时通信_【WebSocket】实时多人答题对战游戏

    本文公众号来源:后端技术漫谈 作者:蛮三刀把刀 前言 前两章教程,我们使用WebSocket的基础特性打造了一个小小聊天室,并在第二章对其进行了集群化改造. 系列教程回顾: 手把手搭建WebSocke ...

  2. 答题对战方案java_使用WebSocket实现实时多人答题对战游戏

    前言 前两章教程,我们使用WebSocket的基础特性打造了一个小小聊天室,并在第二章对其进行了集群化改造. 系列教程回顾: 在本文中,我将介绍如何使用WebSocket向实时多人答题对战游戏提供服务 ...

  3. 多人对战游戏观察者模式分析

    一.观察者模式 观察者模式是使用频率最高的设计模式之一,它用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应.在观察者模式中,发生改变的对象称为观察目标 ...

  4. Vue2 + Nodejs + WebSocket 完成你画我猜多人在线游戏

    使用 websocket + vue2 即可完成一个很有意思的在线游戏作品. 你画我猜,相信大家对这个游戏都很熟悉. 我用Vue2 + mint-ui + nodejs + websocket 实现了 ...

  5. 使用WebSocket解决页面数据实时同步

    功能: 一个页面需要在不同的PC端访问,在某一PC端对网页内容发生改变时,其他PC端页面数据实时更新显示. 实现: 采用webSocket+AOP通知的方式实现 思路: 当页面数据修改时,会通过后端保 ...

  6. springboot+websocket+layui制作的实时聊天室,后端开发入门样例

    实时聊天室 前言 效果图 涉及技术 springboot layui websocket 实现思路 websocket在springboot下的实现 前端实现 建立websocket连接 前端对应的w ...

  7. 基于netty+websocket实现门户游客实时统计功能

    基于netty+websocket实现门户游客实时统计功能 基本需求 商城门户页面需要实时展示游客访问的数量,商城后台页面需要实时游客访问量.登录用户数量,以及下订单用户数量. 技术选型 1.首先实时 ...

  8. WebSocket 实现页面数据实时更新

    WebSocket实时更新 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据.在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就 ...

  9. websocket实现多用户一对一实时聊天

    在线聊天模实现 项目是基于node.js服务器搭建的简易双向通信网页,实现了实时更新在线人数以及用户间即时通讯的功能. 1.websocket连接创建 基于后台websocket地址创建简易的连接,并 ...

最新文章

  1. http反向代理调度算法追朔
  2. elment-ui 表格进行实时百分比计算
  3. 面试官:精通 Mybatis?请回答下这几个问题
  4. 使用MFC进行编程,绘制直线椭圆以及样条曲线
  5. java 图片阴影_Java如何为 PPT 中的图形添加阴影效果
  6. 巨一自动化工业机器人_2021第11届深圳国际工业自动化及机器人展览会
  7. Jacoco--测试覆盖率工具
  8. linux vim自动执行快捷键,Linux Vim快捷键操作命令整理
  9. 【牛客 - 289K】这是一个沙雕题III(贪心,思维枚举,技巧trick,计算上下界)
  10. h3c防火墙u200配置命令_h3c 防火墙清除配置
  11. Django实现一个简单的中间件,不熟悉中间件的爬坑之路
  12. C# Winform控件动态删除
  13. CAS单点登陆原理简介及环境搭建
  14. Kafka启动报错:Timed out waiting for connection while in state: CONNECTING
  15. CVS文件的常见操作
  16. imx226_IMX226CQJ-海思网络摄像芯片
  17. c语言实现扫雷小游戏和扫雷源代码
  18. 百度Java面试题及答案(2022最新版)
  19. python读取cad元素_python3读取autocad图形文件.py实例
  20. 马尔可夫不等式 Markov's inequality

热门文章

  1. 汇编语言上机的4个步骤
  2. 恒峰祥可信讲述拼多多电商店铺推广和商品推广有什么不同
  3. Lineage Logistics完成对 UTI Forwarding的收购
  4. 微服务选择Spring Cloud还是Dubbo?
  5. DOS命令:bcdedit
  6. Spoon Kettle 输入之获取文件名(Get file names)
  7. 《PyQT5软件开发 - 控件篇》第3章 单行文本框QLineEdit
  8. 学习自旋电子学的笔记00:杂谈(闲话) OOMMF软件的安装
  9. CentOS 7报错:Failed to start xxx.service: Unit is masked
  10. 平面点集的凸包问题的算法分析