当前位置: 首页 > news >正文

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

文章目录

  • 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) { // body
 console.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();
        }

相关文章:

  • Spring Boot 非web应用程序
  • 2025年3月29日(matlab -ss -lti)
  • 宝塔面板面试内容整理-Web管理功能
  • 鸿蒙篇:vp、fp、px
  • MCP - 使用 BlenderMCP 让 Claude AI 控制你的 Blender 模型创作
  • ARMv7的MPU
  • 《非暴力沟通》第十二章 “重获生活的热情” 总结
  • Linux--基础IO
  • HTTP事务描述
  • 超级创新思路:基于元学习MTGCN-Attention-Transformer的时间序列预测模型(Python\matlab实现)
  • 树莓派5从零开发至脱机脚本运行教程——4.opencv库安装篇
  • C语言中的结构体,枚举,联合体的学习
  • 知识就是力量——HELLO GAME WORD!
  • [vue]更新数组
  • SSE服务器主动推送至浏览器客户端,让你不再需要websocket
  • React.memo、useMemo、useCallback性能优化总结
  • 嵌入式软件设计规范框架(MISRA-C 2012增强版)
  • VS Code C/C++项目设置launch.json中的environment参数解决支持库路径问题
  • Linux——线程互斥和同步
  • Dify+ollama+vanna 实现text2sql 智能数据分析 -01
  • 做游戏网站思想步骤/网络服务费计入什么科目
  • pinterest的优点/seo 的作用和意义
  • 做微网站用什么框架/软文技巧
  • 可以随意做配搭的网站/市场推广是做什么的
  • 北京软件开发公司排行/抖音视频seo霸屏
  • 网站js代码/网络推广网站排行榜