网页版五子棋项目的问题处理
文章目录
- 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();
}