[网页五子棋][匹配模块]前后端交互接口(消息推送机制)、客户端开发(匹配页面、匹配功能)
让多个用户,在游戏大厅中能够进行匹配,系统会把实力相近的两个玩家凑成一桌,进行对战
约定前后端交互接口
消息推送机制
匹配这样的功能,也是依赖消息推送机制的
- 玩家 1 点击开始匹配按钮,就会告诉服务器:我要开始进行匹配
- 玩家发送匹配请求,这个事情是确定(点击了匹配按钮,就会发送匹配请求),服务器什么时候告知玩家匹配结果(你到你排到了谁),就需要等待匹配结束的时候才能告知
- 正是因为服务器自己也不确定,什么时候能告知玩家匹配的结果,因此就需要依赖消息推送机制
- 当服务器这里匹配成功之后,就主动的告诉当前排到的所有玩家,“你排到了”
这里就需要用到消息推送机制,也就是需要用到 websocket
- 接下来约定和前后端交互接口,也都是基于
websocket
来展开的- 和前面的 HTTP 还是有点区别的
交互接口约定
websocket
可以传输文本数据,也能传输二进制数据。此处就直接设计成让 websocket
传输 JSON
格式的文本数据即可
匹配请求
- 客户端通过
websocket
给服务器发送一个JSON
格式的文本数据ws://127.0.0.1:8080/findMatch
{
message: 'startMarch' / 'stopMatch'
// 开始匹配/结束匹配
}
在通过 websocket
传输请求数据的时候,数据中是不必带有用户身份信息的,当前用户的身份信息,在前面登录完成后,就已经保存到 HttpSession
中了。websocket
里,也是能拿到之前登录好的 HttpSession
中的信息的
匹配响应 1
ws://127.0.0.1:8080/Match
{
ok: true, // 匹配成功
reason: ‘’, // 匹配如果失败,失败原因的信息
message: ‘startMatch’ / ‘stopMatch’,
}
这个响应,是客户端给服务器发送请求之后,服务器立即返回的匹配响应
匹配响应 2
ws://127.0.0.1:8080/findMatch
{
ok: true.
reason: ‘’,
message: ‘matchSuccess’
}
这个响应是真正匹配到对手之后,服务器主动推送回来的信息
- 匹配到的对手不需要在这个响应中体现,仍然都放到服务器这边来保存即可
客户端开发
匹配页面
game_hall. html
创建 game_hall. html
,主要包含
#screen
用于显示玩家的分数信息button#match-button
作为匹配按钮
<!DOCTYPE html>
<html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>游戏大厅</title> <link rel="stylesheet" href="css/common.css"> <link rel="stylesheet" href="css/game_hall.css">
</head>
<body> <div class="nav">五子棋对战</div> <!-- 整个页面的容器元素 --> <div class="container"> <!-- 这个 div 在 container 中是处于垂直水平居中这样的位置 --> <div> <!-- 展示用户信息 --> <div id="screen"></div> <!-- 匹配按钮 --> <div id="match-button">开始匹配</div> </div> </div> <script src="js/jquery.min.js"></script> <script> $.ajax({ type: 'get', url: '/userInfo', success: function(body) { // 将 user 对喜爱那个添加到页面的显示内容中 let screenDiv = document.querySelector("#screen"); screenDiv.innerHTML = '玩家: ' + body.username + '分数: ' + body.score + "<br> 比赛场次: " + body.totalCount + "获胜场次: " + body.winCount; }, error: function() { alert("获取用户信息失败!") } }) </script>
</body>
</html>
game_hall.css
.container { width: 100%; height: calc(100% - 50px); display: flex; align-items: center; justify-content: center;
} #screen { width: 400px; height: 200px; font-size: 20px; background-color: gray; color: white; border-radius: 10px; text-align: center; line-height: 100px;
} #match-button { width: 400px; height: 50px; font-size: 20px; color: white; background-color: orange; border: none; outline: none; border-radius: 10px; text-align: center; line-height: 50px; margin-top: 20px;
} #match-button:active { background-color: gray;
}
- 当我们修改了
css
样式/JS
文件之后,往往要在浏览器中使用cmd+shift+R
(Windows:ctrl+f5
)强制刷新,才能生效- 否则浏览器可能仍然在执行旧版本的代码(浏览器自带缓存)
匹配功能
编辑 game_hall.html
的 js
部分代码
- 点击匹配按钮,就会进入匹配逻辑,同时按钮上提示“
匹配中...(点击取消)
”字样 - 再次点击匹配按钮,则会取消匹配
- 当匹配成功后,服务器会返回匹配成功的响应,页面跳转到
game_room.html
<!DOCTYPE html>
<html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>游戏大厅</title> <link rel="stylesheet" href="css/common.css"> <link rel="stylesheet" href="css/game_hall.css">
</head>
<body> <div class="nav">五子棋对战</div> <!-- 整个页面的容器元素 --> <div class="container"> <!-- 这个 div 在 container 中是处于垂直水平居中这样的位置 --> <div> <!-- 展示用户信息 --> <div id="screen"></div> <!-- 匹配按钮 --> <div id="match-button">开始匹配</div> </div> </div> <script src="js/jquery.min.js"></script> <script> $.ajax({ type: 'get', url: '/userInfo', success: function(body) { // 将 user 对喜爱那个添加到页面的显示内容中 let screenDiv = document.querySelector("#screen"); screenDiv.innerHTML = '玩家: ' + body.username + '分数: ' + body.score + "<br> 比赛场次: " + body.totalCount + "获胜场次: " + body.winCount; }, error: function() { alert("获取用户信息失败!") } }); // 此处进行初始化 websocket,并且实现前端匹配逻辑 // 此处的路径必须写作 /findMatch,千万不要写作 /findMatch/ let websocket = new websocket('ws://127.0.0.1:8080/findMatch'); websocket.onopen = function() { console.log("onopen"); } websocket.onclose = function() { console.log("onclose"); } websocket.onerror = function() { console.log("onerror"); } // 监听页面关闭事件,在页面关闭之前,手动调用这里 websocket 的 close 方法 window.onbeforeunload = function() { websocket.close(); } // 一会重点来实现,要处理服务器返回的响应 websocket.onmessage = function(e) { // 处理服务器返回的响应数据,这个响应就是针对“开始匹配”/“结束匹配”来对应的 // 解析得到的响应对象,返回的数据是一个 JSON 字符串,解析成 js 对象 let resp = JSON.parse(e.data); let match-button = document.querySelector('#match-button'); if (!resp.ok) { console.log("游戏大厅中接收到了失败响应!" + resp.reason); return; } if (resp.message == 'startMatch') { // 开始匹配请求发送成功 console.log("进入匹配队列成功!"); matchButton.innerHTML = '匹配中...(点击停止)'; }else if(resp.message == 'stopMatch') { // 结束匹配请求发送成功 console.log("离开匹配队列成功!"); matchButton.innerHTML = '开始匹配'; }else if(resp.message == 'matchSuccess') { // 已经匹配到对手了 console.log("匹配到对手! 进入游戏房间!"); location.assign('/game_room.html'); }else { console.log("收到了非法的响应! message=" + resp.message); } } // 给匹配按钮添加一个点击事件 let matchButton = document.querySelector('#match-button'); matchButton.onclick - function() { // 在触发 websocket 请求之前,先确认下 websocket 连接是否好着 if (websocket.readyState == websocket.OPEN) { // 如果当前 redyState 处于 OPEN 状态,说明连接好着的 // 这里发送的数据有两种可能,开始匹配/停止匹配 if (matchButton.innerHTML == '开始匹配') { console.log("开始匹配"); websocket.send(JSON.stringify({ message: 'startMatch', })); } else if (matchButton.innerHTML == '匹配中...(点击停止)') { console.log("停止匹配"); websocket.send(JSON.stringify({ message: 'stopMatch', })); } }else { // 这是说明连接当前是异常状态 alert("当前您的连接已经断开!请重新登录!"); location.assign('/login.html'); } } </script>
</body>
</html>
- 此部分主要逻辑从
36
行开始
JSON
字符串和JS
对象的转换
JSON
字符串转换成JS
对象:JSON.pares
JS
对象转换成JSON
对象:JSON.stringify
JSON
字符串和Java
对象的转换
JSON
字符串转换成Java
对象:ObjectMapper.readValue
Java
对象转成JSON
字符串:ObjectMapper.writeValueAsString