[网页五子棋][对战模块]处理连接成功,通知玩家就绪,逻辑问题(线程安全,先手判定错误)
文章目录
- 处理连接成功
- 通知玩家就绪
- 逻辑图
- 问题 1:线程安全
- 问题 2:先手判定错误
- 两边都是提示:轮到对方落子
处理连接成功
实现 GameAPI
的 afterConnectionEstablished
方法
- 首先需要检测用户的登录状态(从
session
中拿到当前用户信息) - 然后要判定当前玩家是否是在房间中
- 接下来进行多开判定,如果玩家已经在游戏中,则不能再次连接
- 把两个玩家放到对应的房间对象中,当两个玩家都建立了连接,房间就放满了,这个时候通知双方都准备就绪
- 如果有第三个玩家尝试也想加入房间,则给出一个提示,房间已经满了
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception { GameReadyResponse resp = new GameReadyResponse(); // 1. 先获取到用户的身份信息(从 HttpSession 里面拿到当前用户的对象) User user = (User) session.getAttributes().get("user"); if(user == null) { resp.setOk(false); resp.setReason("用户尚未登录!"); session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp))); return; } // 2. 判定当前用户是否已经在房间里(拿房间管理器 RoomManager 进行查询) Room room = roomManager.getRoomByUserId(user.getUserId()); if (room == null) { // 如果为 null,当前没有找到对应的房间,就是该玩家还没有匹配到 resp.setOk(false); resp.setReason("用户尚未匹配到!"); session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp))); return; } // 3. 判定当前是不是多开(该用户是不是已经在其他地方进入游戏了) // 前面准备了一个 OnlineUserManager if (onlineUserManager.getFromGameRoom(user.getUserId()) != null || onlineUserManager.getFromGameHall(user.getUserId()) != null) { // 如果一个账号,一边是在游戏大厅,一边是在游戏房间,也视为多开 resp.setOk(false); resp.setReason("禁止多开游戏页面!"); session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp))); } // 4. 设置当前玩家上线 onlineUserManager.enterGameHall(user.getUserId(), session); // 5. 把两个玩家加入到游戏房间中 // 当前这个逻辑是在 game_room.html 页面加载的时候进行的, // 前面的创建房间/匹配过程,是在 game_hall.html 页面中完成的 // 因此前面匹配到对手之后,需要经过页面跳转,来到 game_room.html 才算正式进入游戏房间(玩家准备就绪) // 直行到当前逻辑,说明玩家已经页面跳转成功了 // 页面跳转是很有可能出现失败的情况的(成本高,风险大) if (room.getUser1() == null) { // 第一个玩家还未加入房间,就把当前连上 websocket 的玩家作为 user1 加入到房间中 room.setUser1(user); // 把先连入房间的玩家作为先手 room.setWhiteUser(user.getUserId()); System.out.println("玩家 " + user.getUsername() + " 作为玩家1,已经准备就绪!"); return;} if (room.getUser2() == null) { // 如果进入这个逻辑,说明玩家1 已经加入房间,现在要给当前玩家作为玩家2 room.setUser2(user); System.out.println("玩家 " + user.getUsername() + " 作为玩家2,已经准备就绪!"); // 当两个玩家都加入成功之后,就要让服务器,给这两个玩家都返回 websocket 的响应数据 // 通知这两个玩家:游戏双方都已经准备好了 // 通知玩家1 noticeGameReady(room, room.getUser1(), room.getUser2()); // 通知玩家2 noticeGameReady(room, room.getUser2(), room.getUser1()); return; } // 6. 此处又有玩家尝试连接同一个房间,就提示报错 // 这种情况理论上是不存在的,为了让程序更加的健壮,还是做一个判定和提示 resp.setOk(false); resp.setReason("当前房间已满,你不能加入房间!"); session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
} private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException { GameReadyResponse resp = new GameReadyResponse(); resp.setMessage("gameReady"); resp.setOk(true); resp.setReason(""); resp.setRoomId(room.getRoomId()); resp.setThisUserId(thisUser.getUserId()); resp.setThatUserId(thatUser.getUserId()); resp.setWhiteUser(room.getWhiteUser()); // 把当前的响应数据传回给玩家 WebSocketSession session = onlineUserManager.getFromGameRoom(thisUser.getUserId()); session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
}
- 之前已经写了一个
OnlineUserManager
对象了,也确实能够管理一些用户的在线状态 - 但是这个状态仅仅是局限在
game_hall
这个页面中 - 现在我们已经是在
game_room
中了
- 之前在退出
game_hall
页面的时候,就会断开websocket
连接,也就会在服务器的OnlineUserManager
中删掉对应的元素
因此玩家从游戏大厅离开之后,进入游戏房间页面的时候,就需要重新管理用户的在线状态了
通知玩家就绪
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { User user = (User) session.getAttributes().get("user"); if (user == null) { // 此处我们简单处理,在断开连接的时候就不给客户端返回响应了 return; } WebSocketSession existSession = onlineUserManager.getFromGameRoom(user.getUserId()); if(session == existSession) { // 加上这个判定,目的是为了避免在多开的情况下,第二个用户退出连接动作,导致第一个用户的会话被删除 onlineUserManager.exitGameRoom(user.getUserId()); } System.out.println("当前用户 " + user.getUsername() + " 游戏房间连接异常!");
}
逻辑图
问题 1:线程安全
在玩家 1 连入服务器和玩家 2 连入服务器这两个操作,就是并发的
- 不能保证是上面逻辑图中 1 先建立,2 后建立的顺序
- 这就意味着代码中的这段逻辑是存在多线程环境下调用的,可能会出现线程安全问题
- 我们就要把这里的逻辑判定,使用锁保护起来,避免多个客户端都认为自己是先手方
需要竞争的资源是什么,就对什么加锁
- 对谁加锁,针对这个对象访问的时候才有互斥效果
- 此时我们是多个线程在同时访问、修改同一个
room
对象 - 所以我们需要针对
room
对象加锁
- 要保证玩家 1 和 2 要互斥,玩家 3 和 4 要互斥,玩家 5 和 6 要互斥
- 同个房间里的两个对象才会有竞争,非同房里面的玩家互不干扰
问题 2:先手判定错误
两边都是提示:轮到对方落子
- 客户端代码中尝试获取响应中的
isWhite
字段 - 但是实际响应的数据中,根本就灭有
isWhite
字段,有的是whiteUser
字段 - 因此代码中进行取这个字段,就都取到了一个
undefined
,这里的判断结果都为false
,所以在先手选择都是选择对方
解决办法:
相关文章:

[网页五子棋][对战模块]处理连接成功,通知玩家就绪,逻辑问题(线程安全,先手判定错误)
文章目录 处理连接成功通知玩家就绪逻辑图问题 1:线程安全问题 2:先手判定错误两边都是提示:轮到对方落子 处理连接成功 实现 GameAPI 的 afterC…...
TensorFlow Extended (TFX) 生产环境模型版本控制与回滚实战指南
TFX 版本控制核心架构 TFX 通过以下组件构建完整的模型生命周期管理系统: ML Metadata (MLMD):记录所有实验和管道的元数据Pusher 组件:负责模型部署与版本标记Model Registry:集中式模型存储库&#x…...

【Web应用】若依框架:基础篇11功能详解-系统接口
文章目录 ⭐前言⭐一、课程讲解⭐二、自己动手实操⭐总结 标题详情作者JosieBook头衔CSDN博客专家资格、阿里云社区专家博主、软件设计工程师博客内容开源、框架、软件工程、全栈(,NET/Java/Python/C)、数据库、操作系统、大数据、人工智能、工控、网络、…...

【Docker项目实战篇】Docker部署PDF查看器PdfDing
【Docker项目实战篇】Docker部署PDD查看器PdfDing 一、PdfDing介绍1.1 PdfDing简介1.2 PdfDing主要特点1.3 主要使用场景 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Pd…...
Redis 常用数据类型和命令使用
目录 1 string 2 hash 3 list 4 set集合 5 zset有序集合 1 string 值可以是字符串、数字和二进制的value,值最大不能超过512MB 应用场景: 应用程序缓存 计数器 web共享session 限速 1.1 设置单个键值 set <key> value [EX seconds|PX…...

【Linux系统】第八节—进程概念(上)—冯诺依曼体系结构+操作系统+进程及进程状态+僵尸进程—详解!
hi,我是云边有个稻草人 偶尔中二的博主^(* ̄(oo) ̄)^,与你分享专业知识,祝博主们端午节快乐! Linux—本节博客所属专栏—持续更新中—欢迎订阅! 目录 一、冯诺依曼体系结构 二、操作系统(Opera…...
WPF 全局加载界面、多界面实现渐变过渡效果
WPF 全局加载界面与渐变过渡效果 完整实现方案 MainWindow.xaml <Window x:Class"LoadingScreenDemo.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml&quo…...
WebSocket与实时对话式AI服务的集成
WebSocket与实时对话式AI服务的集成 在现代对话式AI系统中,传统的HTTP请求-响应模型已难以满足实时交互的体验需求。特别是用户对响应速度、逐字输出、会话上下文保持等方面提出更高要求时,需要一种能够建立持久连接并支持双向通信的协议。WebSocket正是在这一背景下,成为A…...
【xmb】】内部文档148344599
这里写自定义目录标题 CyberDog 2 仿真智能物流配送系统 – 初赛设计报告摘要目录1 引言2 任务与需求分析3 系统总体设计4 核心算法与模块实现5 仿真测试与结果分析6 结论与展望 CyberDog 2 仿真智能物流配送系统 – 初赛设计报告 团队名称: (晚点写&am…...
MobaXterm国内下载与安装使用教程
MobaXterm是一款为 Windows 用户量身打造的远程终端工具,它将多种网络功能集成在一个轻量级、便携式的界面中,尤其适合需要频繁与远程主机交互的开发者、系统运维工程师以及科研技术人员。无论是管理 Linux 服务器、远程执行命令,还是图形化运…...

数据结构——优先级队列(PriorityQueue)
1.优先级队列 优先级队列可以看作队列的另一个版本,队列的返回元素是由是由插入顺序决定的,先进先出嘛,但是有时我们可能想要返回优先级较高的元素,比如最大值?这种场景下就由优先级队列登场。 优先级队列底层是由堆实…...

代谢组数据分析(二十六):LC-MS/MS代谢组学和脂质组学数据的分析流程
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包依赖包安装包加载需要的R包数据下载以及转换mzML数据预处理代谢物注释LipidFinder过滤MultiABLER数据预处理过滤补缺失值对数变换数据标准化下游数据分析总结系统信息参考介…...
服务器上用脚本跑python深度学习的注意事项(ubantu系统)
bash: $\r: command not found 问题原因: 出现 bash: $\r: command not found 以及路径中出现 \r 通常是因为脚本文件是在Windows系统下编辑,然后在Linux(如Ubuntu)系统中运行。在Windows系统中,文本文件的换行符是 \…...

【ARM】【FPGA】【硬件开发】Chapter.1 AXI4总线协议
Chapter.1 AXI4总线协议 作者:齐花Guyc(CAUC) 一、总线介绍 AXI4总线 AXI4总线就像是SoC内部的“高速公路”,负责在不同硬件模块之间高效传输数据。 AXI4协议通过 5个独立通道 传输数据和控制信号,每个通道都有自己的信号线,互…...
青少年编程与数学 02-020 C#程序设计基础 10课题、桌面应用开发
青少年编程与数学 02-020 C#程序设计基础 10课题、桌面应用开发 一、桌面应用1. 主要特点2. 常见类型3. 优势4. 局限性 二、开发步骤1. 准备工作2. 创建项目3. 开发应用4. 运行调试5. 打包发布 三、Windows 窗体应用(一)定义(二)特…...

把 jar 打包成 exe
1. 把自己的项目先正常打成jar包 2. 使用exe4j工具将jar转换为exe 2.1 exe4j下载地址:https://www.ej-technologies.com/download/exe4j/files 2.2 下载完成之后激活 2.3 可以点击Change License,输入秘钥L-g782dn2d-1f1yqxx1rv1sqd 2.4 直接下一步…...

【目标检测】检测网络中neck的核心作用
1. neck最主要的作用就是特征融合,融合就是将具有不同大小感受野的特征图进行了耦合,从而增强了特征图的表达能力。 2. neck决定了head的数量,进而潜在决定了不同尺度样本如何分配到不同的head,这一点可以看做是将整个网络的多尺…...

【经验】Ubuntu中设置terminator的滚动行数、从Virtualbox复制到Windows时每行后多一空行
1、设置terminator的滚动行数 1.1 问题描述 在终端 terminator 中,调试程序时,只能查看有限行数的打印日志,大约是500行,怎么能增加行数 1.2 解决方法 1)安装terminator sudo apt install terminator和 terminato…...

使用微软最近开源的WSL在Windows上优雅的运行Linux
install wsl https://github.com/microsoft/WSL/releases/download/2.4.13/wsl.2.4.13.0.x64.msi install any distribution from microsoft store, such as kali-linux from Kali office website list of distribution PS C:\Users\50240> wsl -l -o 以下是可安装的有…...

HackMyVM-Teacher
信息搜集 主机发现 ┌──(kali㉿kali)-[~] └─$ nmap -sn 192.168.43.0/24 Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-01 01:02 EDT Nmap scan report for 192.168.43.1 Host is up (0.0084s latency). MAC Address: C6:45:66:05:91:88 (Unknow…...

BugKu Web渗透之矛盾
开启场景,打开网页。发现是一段php代码。 这段代码也很好理解,就是get方式传参num,如果num不是数字类型,那么输出num的值,并且num1时,输出flag的值。 首先看看is_numeric的意思。 开始我想到了使用科学技术…...
hot100 -- 4.子串系列
1.和为 K 的子数组 问题: 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 方法1:暴力枚举 # 方法1:暴力枚举(遍历子数组起点和终点&…...

Python实现P-PSO优化算法优化卷积神经网络CNN回归模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 随着人工智能和深度学习技术的快速发展,卷积神经网络(CNN)在图像分类、目标检测…...

ssm 学习笔记day03
环境搭建 spring配置数据库 1.在pom.xml安装相应的依赖 2.在properties里面配置数据库的相关信息,需要强调的一点是,一定不要在properties里面添加任何空格,否则就会像我一样搞了两小时,数据一直报错,然后发现是空格的…...

mkdir: cannot create directory ‘gitlab-stu’: No space left on device
Linux中创建目录时报错“mkdir: cannot create directory ‘gitlab-stu’: No space left on device”,磁盘空间不足。 使用df命令查看,发现 / 下面use%占满了: 查看inode使用情况: 可以看到docker的数据大部分存放在/var/lib/do…...
【前端面经】云智慧一面
写在前面:面经只是记录博主遇到的题目。每题的答案在编写文档的时候已经有问过deepseek,它只是一种比较普世的答案,要学得深入还是靠自己 Q:手撕代码,两个有序数组排序 A: function mysort(arr1, arr2) {…...

ESP8285乐鑫SOCwifi芯片32bit MCU和2.4 GHz Wi-Fi
简介 ESP8285 拥有完整的且⾃成体系的 Wi-Fi ⽹络功能,既能够独⽴应⽤,也可以作为从机搭载于其他主机 MCU 运⾏。当 ESP8285 独⽴应⽤时,能够直接从外接 flash 中启动。内置的⾼速缓冲存储器有利于提⾼系统性能,并且优化存储系统。…...

DL00916-基于深度学习的金枪鱼各类别目标检测含完整数据集
文末有获取方式 🚀 基于深度学习的金枪鱼目标检测——开创智能识别新领域! 在计算机视觉和深度学习的快速发展中,目标检测 技术已成为提升行业效率的核心利器。而对于海洋生物领域,尤其是金枪鱼的 目标检测,更是填补了…...

不可变集合类型转换异常
记录一个异常:class java.util.ImmutableCollections$ListN cannot be cast to class java.util.ArrayList (java.util.ImmutableCollections$ListN and java.util.ArrayList 文章目录 1、原因2、解决方式一3、解决方式二4、关于不可变集合的补充4.1 JDK8和9的对比4…...

【PyQt5】从零开始的PyQt5 - QLabel篇
从零开始的PyQt5 - QLabel篇 引言一、简述二、例程2.1 显示到QWidget窗口上2.2 重新设置Label大小和对齐方式2.3 添加内容,设置边框2.4 显示富文本 三、参考 引言 QLabel主要用于显示文本或图像,不提供用户交互功能。本文主要简述PyQt5中的QLabel以及展…...