网页版五子棋项目的问题处理

文章目录

  • config.WebSocketConfig
  • 将键值对加⼊OnlineUserManager中
  • 线程安全、锁
  • ObjectMapper来处理json
  • 针对多开情况的判定
  • 处理连接关闭、异常(玩家中途退出)后的不合理操作
  • 游戏大厅数据更新

config.WebSocketConfig

把MatchAPI注册进去

• 在addHandler 之后,再加上⼀个.addInterceptors(newHttpSessionHandshakeInterceptor()) 代码,这样可以把之前登录过程中往HttpSession中存放的数据(主要是User对象),到WebSocket的session中.⽅便后⾯的代码中获取到当前用户信息

// 通过 .addInterceptors(new HttpSessionHandshakeInterceptor() 这个操作来把 HttpSession ⾥的属性放到 WebSocket 的 session 中 // 参考: https://docs.spring.io/springframework/docs/5.0.7.RELEASE/spring-framework-reference/web.html#websocketserver-handshake// 然后就可以在 WebSocket 代码中 WebSocketSession ⾥拿到 HttpSession 中的 attribute.registry.addHandler(matchAPI, "/findMatch").addInterceptors(new HttpSessionHandshakeInterceptor());}

将键值对加⼊OnlineUserManager中

• 当玩家断开websocket连接,则将键值对从OnlineUserManager中删除
• 在玩家连接好的过程中,随时可以通过userId来查询到对应的会话,以便向客户端返回数据
由于存在两个⻚⾯,游戏⼤厅和游戏房间,使⽤两个哈希表来分别存储两部分的会话
在这里插入图片描述

线程安全、锁

由于handlerMatch 在单独的线程中调⽤.因此要考虑到访问队列的线程安全问题.需要加上锁
• 每个队列分别使⽤队列对象本⾝作为锁即可.
• 在⼊⼝处使⽤wait来等待,直到队列中达到2个元素及其以上,才唤醒线程消费队列

private void handlerMatch(Queue<User> matchQueue) {synchronized (matchQueue){try {// 很可能队列初始情况为0,用while循环检查,不能用if(匹配成功需要有两个玩家)while (matchQueue.size() < 2){matchQueue.wait();return;}//从队列中取出两个玩家User player1 = matchQueue.poll();User player2 = matchQueue.poll();System.out.println("匹配出了两个玩家:"+player1.getUsername()+" , "+player2.getUsername());//获取玩家的websocket的会话,告诉玩家 排到了WebSocketSession session1 = onlineUserManager.getFromGameHall(player1.getUserId());WebSocketSession session2 = onlineUserManager.getFromGameHall(player2.getUserId());//理论上来说,匹配队列中的玩家一定是在线状态(前面已经处理过,断开连接的玩家会被移除匹配队列//为了谨慎,再进行一次判断if (session1 == null){//玩家1不在线就把玩家2放回到匹配队列中matchQueue.offer(player2);return;}if (session2 == null){//玩家2不在线就把玩家1放回到匹配队列中matchQueue.offer(player1);return;}//TODO 把这两个玩家放到一个游戏房间中Room room = new Room();roomManager.add(room,player1.getUserId(),player2.getUserId());//给玩家反馈信息 匹配到了MatchResponse response1 = new MatchResponse();response1.setOk(true);response1.setMessage("matchSuccess");session1.sendMessage(new TextMessage(objectMapper.writeValueAsString(response1)));MatchResponse response2 = new MatchResponse();response2.setOk(true);response2.setMessage("matchSuccess");session2.sendMessage(new TextMessage(objectMapper.writeValueAsString(response2)));}catch (IOException | InterruptedException e){e.printStackTrace();}
public void add(User user){if (user.getScore() < 2000){synchronized (normalQueue){normalQueue.offer(user);normalQueue.notify();}System.out.println("把玩家"+user.getUsername()+"加入到了 normalQueue 中!");} else if (user.getScore() >= 2000 && user.getScore() < 3000) {synchronized (highQueue){highQueue.offer(user);highQueue.notify();}System.out.println("把玩家"+user.getUsername()+"加入到了 highQueue 中!");}else {synchronized (veryHighQueue){veryHighQueue.offer(user);veryHighQueue.notify();}System.out.println("把玩家"+user.getUsername()+"加入到了 veryHighQueue 中!");}}
public void remove(User user){if (user.getScore() < 2000){synchronized (normalQueue){normalQueue.remove(user);}System.out.println("把玩家"+user.getUsername()+"移除normalQueue队列");} else if (user.getScore() >= 2000 && user.getScore() < 3000) {synchronized (highQueue){highQueue.remove(user);}System.out.println("把玩家"+user.getUsername()+"移除highQueue队列");}else {synchronized (veryHighQueue){veryHighQueue.remove(user);}System.out.println("把玩家"+user.getUsername()+"移除veryHighQueue队列");}}

ObjectMapper来处理json

MatchResponse response = new MatchResponse();response.setOk(false);response.setReason("您尚未登录! 不能进行匹配!");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));//TextMessage--一个文本格式的websocket数据 通过ObjectMapper把对象转成JSON字符串!

针对多开情况的判定

//  获取玩家身份信息(哪个玩家在游戏大厅中 建立了连接)//   (注意!!!可能出现玩家身份信息为空的现象----玩家直接通过 /game_hall.html 进入游戏大厅try {User user = (User) session.getAttributes().get("user");//判断当前用户是否已经登录,禁止多开WebSocketSession webSocketSession = onlineUserManager.getFromGameHall(user.getUserId());if( webSocketSession != null || onlineUserManager.getFromGameRoom(user.getUserId()) != null){MatchResponse response = new MatchResponse();response.setOk(true);response.setReason("此用户已登录! 禁止重复登录!");response.setMessage("repeatConnection");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));//session.close();//连接断开return;}
//判断用户有没有多开if (onlineUserManager.getFromGameHall(user.getUserId()) != null|| onlineUserManager.getFromGameRoom(user.getUserId()) !=null){response.setOk(false);response.setReason("禁止多开游戏");session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));return;}
success: function(data) { // bodyconsole.log(JSON.stringify(data));if (data && data.userId > 0) {// 登录成功, 跳转到游戏⼤厅alert("登录成功!")location.assign('/game_hall.html');} else {alert("登录失败! 用户名密码错误! 或者 该账号正在游戏中!");}}

下线的时候注意针对多开情况的判定,避免错误删除玩家

try {//玩家下线,移除User user = (User) session.getAttributes().get("user");//  把该玩家设置为下线状态// 避免移除多开情况时,错误删除WebSocketSession webSocketSession = onlineUserManager.getFromGameHall(user.getUserId());if (webSocketSession == session){onlineUserManager.exitGameHall(user.getUserId());}matcher.remove(user);//玩家正在匹配中,连接断开,移除玩家}

处理连接关闭、异常(玩家中途退出)后的不合理操作

此时连接已经关闭,不应该再发送信息给客户端

catch (NullPointerException e){e.printStackTrace();//连接已经关闭,不应该再发送信息给客户端
//            //把 当前用户未登录 这个信息返回回去
//            MatchResponse response = new MatchResponse();
//            response.setOk(false);
//            response.setReason("您尚未登录! 不能进行匹配!");
//            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
//            //TextMessage--一个文本格式的websocket数据 通过ObjectMapper把对象转成JSON字符串!}

用户操作不可控,谨慎处理可能发生的情况

WebSocketSession existSession = onlineUserManager.getFromGameRoom(user.getUserId());if (existSession != session){System.out.println("当前的会话不是玩家游戏中的会话, 不做处理!");return;}

游戏大厅数据更新

对局结束后,分数、对局数会发生改变,因此游戏大厅中的数据需要从数据库中获取

public Object getUserInfo(HttpServletRequest req){try {HttpSession httpSession = req.getSession(false);User user = (User) httpSession.getAttribute("user");//去数据库中找最新的数据(一轮比赛后数据会有变化User newUser = userMapper.selectByName(user.getUsername());return newUser;}catch (NullPointerException e){return new User();}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/74026.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【初探数据结构】归并排序与计数排序的序曲

&#x1f4ac; 欢迎讨论&#xff1a;在阅读过程中有任何疑问&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;如果你觉得这篇文章对你有帮助&#xff0c;记得点赞、收藏&#xff0c;并分享给更多对数据结构感…

算法刷题记录——LeetCode篇(8.7) [第761~770题](持续更新)

更新时间&#xff1a;2025-03-30 算法题解目录汇总&#xff1a;算法刷题记录——题解目录汇总技术博客总目录&#xff1a;计算机技术系列博客——目录页 优先整理热门100及面试150&#xff0c;不定期持续更新&#xff0c;欢迎关注&#xff01; 763. 划分字母区间 给你一个字…

Pod 网络与 CNI 的作用

在 Kubernetes 中&#xff0c;Pod 网络 是实现容器间通信的核心机制&#xff0c;每个 Pod 拥有独立的 IP 地址&#xff0c;可直接跨节点通信。CNI&#xff08;Container Network Interface&#xff09; 是 Kubernetes 的网络插件标准&#xff0c;负责为 Pod 分配 IP、配置网络规…

使用keepalived结合tomcat和nginx搭建三主热备架构

角色主机名软件IP地址用户client172.25.250.90keepalivedVIP172.25.250.100keepalivedVIP172.25.250.101keepalivedVIP172.25.250.102masterserverAkeepalived, nginx172.25.250.30backupserverBkeepalived, nginx172.25.250.31backupserverCkeepalived, nginx172.25.250.32web…

STRUCTBERT:将语言结构融入预训练以提升深度语言理解

【摘要】最近&#xff0c;预训练语言模型BERT&#xff08;及其经过稳健优化的版本RoBERTa&#xff09;在自然语言理解&#xff08;NLU&#xff09;领域引起了广泛关注&#xff0c;并在情感分类、自然语言推理、语义文本相似度和问答等各种NLU任务中达到了最先进的准确率。受到E…

leetcode_977. 有序数组的平方_java

977. 有序数组的平方https://leetcode.cn/problems/squares-of-a-sorted-array/ 1.题目 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1…

Nginx—nginx.conf 配置结构详解

一、nginx.conf 配置结构 函数 说明 main 全局配置 event 配置工作模式以及连接数 http http模块相关配置 server 虚拟主机配置&#xff0c;可以有多个 location 路由规则&#xff0c;表达式 upstream 集群、内网服务器&#xff08;负载均衡也在这里边配&#xff…

斐波那契数列----C语言

关于斐波那契 已知&#xff1a; 问题背景&#xff1a;一对兔子从第3个月开始每月生一对新兔子&#xff0c;新兔子同样在第3个月开始繁殖。 关键观察&#xff1a; 第1个月&#xff1a;1对&#xff08;初始兔子&#xff09;。 第2个月&#xff1a;1对&#xff08;未成熟&#…

vulhub靶场—— Tomcat8

目录 一、漏洞描述 二、靶场搭建 三、漏洞复现 1、弱密码 2、文件上传 一、漏洞描述 环境描述&#xff1a; Tomcat 支持后台部署 war 文件&#xff0c;可以直接将 webshell 部署到 web 目录下。tomcat 默认的管理页面 manager 使用 basic 认证用户名和密码登录&#xff0…

使用 Spring AI Aliabab Module RAG 构建 Web Search 应用

使用 Spring AI Alibaba 构建大模型联网搜索应用 Spring AI 实现了模块化 RAG 架构&#xff0c;架构的灵感来自于论文“模块化 RAG&#xff1a;将 RAG 系统转变为类似乐高的可重构框架”中详述的模块化概念。 Spring AI 模块化 RAG 体系 总体上分为以下几个步骤&#xff1a; …

一些练习 C 语言的小游戏

一些练习 C 语言的小游戏 — 1. 猜数字游戏 描述&#xff1a;程序随机生成一个数字&#xff0c;玩家需要猜测这个数字&#xff0c;并根据提示&#xff08;太高或太低&#xff09;调整猜测&#xff0c;直到猜中为止。 功能点&#xff1a; 随机数生成 (rand() 函数)。循环和…

关于中文编程的一些思考

随着信息化与数字化的发展&#xff0c;工业4.0时代亦将徐徐到来。当计算机的普及程度越来越高&#xff0c;数据的产生、传输、处理等变得越来越快、越来越大量的时候&#xff0c;人们想要自动化办公的愿望也越来越强烈&#xff0c;希望能将自身从耗费脑力但是重复繁琐的工作中解…

golang 日志log与logrus

目录 一、Go 标准库 log 详解 1. 功能特点 2. 常用函数 3. 示例代码 4. 优势和局限 二、第三方库 logrus 详解 1. 功能特点 2. 核心功能 3. 示例代码 4. 优势和扩展性 三、总结 1. 何时选择 log&#xff1f; 2. 何时选择 logrus&#xff1f; 3. 对比总结 一、Go 标…

消费品行业创新创业中品类创新与数字化工具的融合:以开源 AI 智能客服、AI 智能名片及 S2B2C 商城小程序为例

摘要&#xff1a; 本文聚焦于消费品行业的创新与创业&#xff0c;深入探讨“选择大于努力”这一观点&#xff0c;强调品类选择在品牌发展中的关键作用。同时&#xff0c;详细分析了品类创新对于新消费品牌崛起以及传统品牌转型的重要意义。在此基础上&#xff0c;引入开源 AI 智…

Razer macOS v0.4.10快速安装

链接点这里下载最新的 .dmg 文件。将下载的 .dmg 映像文件拖入 应用程序 文件夹中。若首次打开时出现安全警告【什么扔到废纸篓】&#xff0c;这时候点击 Mac 的“系统偏好设置”-> “安全性与隐私”-> “通用”&#xff0c;然后点击底部的 “打开”。【或者仍然打开】 对…

Flask项目部署:Flask + uWSGI + Nginx

目录 1,网络架构 2,环境安装 2.1,安装yum:Shell软件包管理器 2.2 安装python 2.3 安装uWSGI 2.4 安装Flask 3,上传工程包到服务器,打包Flask项目 4,创建和配置 uwsgi 配置文件 uwsgi.ini 4.1配置文件 4.2配置文件注释详解 5,启动服务 6,安装nginx 7,nginx配置 8,…

[FPGA基础学习]实现流水灯与按键暂停

FPGA实现LED流水灯 1.vscode的安装和使用 vscode下载 Visual Studio Code - Code Editing. Redefined vscode插件&#xff08;Verilog-HDL/SystemVerilog&#xff09;下载 quartus绑定vscode 2.用6个LED完成周期为1秒的跑马灯效果 流水灯模块设计 时钟输入 DE2-115开发板…

【TensorRT】TensorRT从安装到推理——Python 环境下 MobileNetV4 三分类任务

我想开发一个基于深度学习的分类小软件&#xff0c;逐渐了解到了TensorRT在模型推理速度上的优势&#xff0c;经过一下午资料的查找实现了将onnx模型转为TensorRT格式模型的推理及测试过程。将实现过程记录下来方便日后查看。 本文实验设备是MX350显卡 2G显存 一 、安装Tenso…

1.两数之和(Java)

1. 题目描述 LeetCode 1. 两数之和&#xff08;Two Sum&#xff09; 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那两个整数&#xff0c;并返回它们的索引。 示例 1&#xff1a; 输入&#xff1a;nums [2,7,11,15], target 9 …

《深入探索 Python 数据分析:用 Pandas 高效处理与可视化大型数据集》

《深入探索 Python 数据分析:用 Pandas 高效处理与可视化大型数据集》 引言:从零到分析高手 数据是当代社会最宝贵的资源,而数据分析技能是现代职业人不可或缺的一部分。在数据科学的领域中,Python 已成为当之无愧的“首选语言”,其强大的生态系统和简洁的语法让人如虎添…