项目一:使用 Spring + SpringMVC + Mybatis + lombok 实现网络五子棋

一:系统展示:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

二:约定前后端接口

2.1 登陆

登陆请求:

GET /login HTTP/1.1
Content-Type: application/x-www-form-urlencoded

username=zhangsan&password=123

登陆响应:

  1. 正常对象:正常对象会在数据库中存储,直接从数据库中取出即可,无需通过 set 方法进行设置
HTTP/1.1 200 OK
Content-Type: application/json

{
	"userid": 1,
	"username": "zhangsan",
	"password": "123",
	"score": 1000,
	"totalcount": 0,
	"wincount": 0
}
  1. 登陆失败返回空对象
HTTP/1.1 200 OK
Content-Type: application/json

{
	"userid": 0,
	"username": null,
	"score": 0,
	"totalcount": 0,
	"wincount": 0
}

2.2 注册

  1. 注册请求
GET /register HTTP/1.1
Content-Type: application/x-www-form-urlencoded

username=zhangsan&password=123
  1. 注册响应
HTTP/1.1 200 OK
Content-Type: application/json

{
	"userid": 1,
	"username": "zhangsan",
	"password": "123",
	"score": 1000,
	"totalcount": 0,
	"wincount": 0
}
  1. 获取当前用户信息
GET /userinfo HTTP/1.1
Content-Type: application/json

{
	"userid": 1,
	"username": "zhangsan",
	"score": 1000,
	"totalcount": 0,
	"wincount": 0
}

2.2 匹配

  1. 匹配请求
ws://127.0.1:8080/Match

{
	"message": "startMatch" / ”stopMatch“    //startMatch 代表开始匹配,stopMatch 代表停止匹配
}

请求不必带有用户的信息。因为在登陆成功后会把用户信息保存在 httpsession 中,websocket 可以拿到这个 httpseesion 中存储的数据。

  1. 匹配响应 1
ws://127.0.0.1:8080/Match

{
	"ok": true,                            // 进入成功为 true  匹配成功,否则为 false
	"reason": "失败原因",                   // 失败原因(若匹配失败则返回此字段)
	"message": "startMatch" / "stopMatch" / 'matchSuccess' / 'repeatConnection'  //startMatch 代表开始匹配,stopMatch 代表停止匹配 ,matchSuccess 代表匹配成功 ,'repeatConnection 代表用户多开
}

2.3 对战

对战和匹配使用两套逻辑,使用不同的 websocket 路径处理,能够更好的解耦合

  1. gameready 响应
{
	"message": "gameReady",
	"ok": true,                       // 匹配成功为 true,否则为 false,false 代表有某些问题
	"reason": "",                     // 出错原因(若匹配失败则返回此字段)
	"roomid": "123456",               // 房间 ID
	"thisuserid": 1,                  // 自己的用户 ID
	"thatuserid": 2,                  // 对手的用户 ID
	"whiteuser": 1                    // 先手玩家 ID,1 表示自己先,2 表示对手先
}

这个请求是玩家匹配成功后,由服务器生成的内容,把这个内容返回给浏览器

  1. 下棋请求 1
{
	"message": "putChess",
	"userid": 1,
	"row": 0,
	"col": 0
}
  1. 下棋响应
{
	"message": "putChess",
	"userid": 1,
	"row": 0,              // 行
	"col": 0,              // 列
	"winner": 0            // 当前是否分出胜负,0 代表无胜负,非 0 代表获胜方用户 ID
}

三: websocket 前置知识

对于 http 来说,能够实现客户端向服务器发送数据,但是很难实现服务器向客户端发送数据,虽然可以通过轮转实现,但是这种实现太消耗 cpu,性能也不好,而且实现起来也比较麻烦,所以我们选择使用 websocket 协议,websocket 协议能实现客户端和服务器的双向通信,符合我们目前的需求场景。

WebSocket 与 HTTP 的区别:

  • 持久连接:WebSocket 连接建立后会一直保持,直到显式关闭,减少了多次连接开销。
  • 双向通信:WebSocket 支持全双工通信,即客户端和服务器可以在任意时间发送消息。
  • 低延迟:WebSocket 的头部信息少,通信延迟低,非常适合实时性要求高的应用场景。

使用 WebSocket 的场景

  • 实时聊天系统(如在线客服、聊天应用)
  • 在线游戏(如棋类对战、竞技游戏)
  • 实时数据推送(如股票行情、天气更新、体育比分)

3.1 websocket 连接流程

使用 WebSocket 连接的步骤:

步骤描述
配置 WebSocket 端点在后端定义 WebSocket 端点的 URL,供客户端通过该 URL 连接到服务器。
实现 TextWebSocketHandler在后端实现 WebSocket 连接处理逻辑,包括连接建立、接收消息、连接关闭等事件的处理。
前端建立 WebSocket 连接前端使用 JavaScript 创建 WebSocket 连接,并负责发送和接收消息。

步骤一:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new ChatHandler(), "/chat"); // 将 /chat 端点与 ChatHandler 绑定
    }
}
项目描述
/chat客户端连接到服务器的 WebSocket URL(端点),用于建立 WebSocket 连接。
ChatHandler自定义的 WebSocket 处理器,用于处理 WebSocket 的事件,例如连接建立、消息接收和关闭连接。

步骤 2:实现 TextWebSocketHandler 类

import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

public class ChatHandler extends TextWebSocketHandler {
		//TextWebSocketHandler 是 Spring 提供的一个辅助类,用于处理 WebSocket 的文本消息。

    // 1. 连接建立时调用
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("连接已建立:" + session.getId());
    }

    // 2. 接收到消息时调用
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println("收到消息:" + message.getPayload());
        // 将收到的消息返回给客户端
        session.sendMessage(new TextMessage("服务器响应:" + message.getPayload()));
    }

    // 3. 连接关闭时调用
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("连接已关闭:" + session.getId());
    }

    // 4. 处理错误时调用
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.err.println("传输错误:" + exception.getMessage());
    }
}
方法名称描述
afterConnectionEstablished客户端成功连接时调用,例如用户打开 WebSocket 页面。可在此进行初始化操作。
handleTextMessage服务器接收到客户端发送的消息时调用。通常用于处理消息,并将结果发送回客户端。
afterConnectionClosedWebSocket 连接关闭时调用,例如用户关闭页面或断开连接。可在此进行清理操作。
handleTransportError连接出错时调用,例如网络故障。在此可以记录错误日志或进行相应的错误处理。

步骤三:前端实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket 示例</title>
</head>
<body>
    <h2>WebSocket 简单示例</h2>
    <div>
        <input type="text" id="messageInput" placeholder="输入消息">
        <button onclick="sendMessage()">发送消息</button>
    </div>
    <div id="response"></div>

    <script>
        // 1. 创建 WebSocket 连接
        let websocket = new WebSocket("ws://" + location.host + "/chat");//建立连接

        // 2. 连接建立时调用
        websocket.onopen = function() {
            console.log("WebSocket 连接已建立");
        };

        // 3. 接收消息时调用
        websocket.onmessage = function(event) {
            console.log("收到消息:" + event.data);
            document.getElementById("response").innerText = "服务器:" + event.data;
        };

        // 4. 连接关闭时调用
        websocket.onclose = function() {
            console.log("WebSocket 连接已关闭");
        };

        // 5. 出错时调用
        websocket.onerror = function(error) {
            console.error("WebSocket 出现错误:" + error);
        };

        // 发送消息给服务器
        function sendMessage() {
            let message = document.getElementById("messageInput").value;
            websocket.send(message); // 发送消息
        }
    </script>
</body>
</html>
方法或属性描述
new WebSocket(“ws://” + location.host + “/chat”)创建 WebSocket 连接到服务器的 /chat 端点,绑定到 WebSocket 处理器。
onopen当连接成功时调用,用于通知用户连接已建立或执行初始化操作。
onmessage当接收到服务器发送的消息时调用,将接收到的消息显示在页面上或进行相应处理。
onclose当连接关闭时调用,用于通知用户连接已关闭或执行清理操作。
onerror当连接出错时调用,用于记录错误信息或执行错误处理操作。

通过这三个步骤就可以把特定的前端页面和特定的后端类进行连接并通信了

3.2 客户端和服务器互发数据

在 WebSocket 通信中,客户端和服务器都可以通过特定的方法发送数据。

3.2.1 客户端发送数据给服务器

在前端,客户端使用 WebSocket.send() 方法向服务器发送数据。

// 假设已经创建了 WebSocket 连接
let websocket = new WebSocket("ws://" + location.host + "/chat");

// 定义一个函数,通过 WebSocket 向服务器发送消息
function sendMessage() {
    let message = document.getElementById("messageInput").value; // 获取输入框中的消息
    websocket.send(message); // 使用 send 方法发送消息给服务器
    console.log("发送消息给服务器:" + message);
}

数据通过 send 方法发送后,后端会调用对应的 handleTextMessage 处理方法来处理接收到的数据。

3.2.2 服务器发送数据给客户端

在 Spring Boot 中,服务器使用 WebSocketSession.sendMessage() 方法向客户端发送数据。

import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

public class ChatHandler extends TextWebSocketHandler {

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println("收到客户端消息:" + message.getPayload());

        // 构造服务器的响应消息
        TextMessage responseMessage = new TextMessage("服务器响应:" + message.getPayload());
        
        // 发送响应消息给客户端
        session.sendMessage(responseMessage);
        System.out.println("发送消息给客户端:" + responseMessage.getPayload());
    }
}

数据通过 sendMessage 方法发送后,前端会触发 onmessage 方法来接收并处理服务器返回的消息。

3.3 Json 数据 Java 对象 JS 对象的互相转换

在前端,数据需要以 JS 对象的形式处理;在后端,数据需要以 Java 对象的形式处理。而传输过程中使用的是 JSON 格式的数据,因此在数据传输时需要实现这三种对象之间的相互转换。

数据处理阶段描述
前端发送数据JavaScript 对象 转换为 JSON 字符串,并发送给后端。
后端接收数据JSON 字符串 转换为 Java 对象,并进行处理。
后端返回数据Java 对象 转换为 JSON 字符串,并发送给前端。
前端接收数据JSON 字符串 转换为 JavaScript 对象,并进行处理。

3.3.1 JSON 字符串与 JavaScript 对象的转换

3.3.1.1 JavaScript 对象转为 JSON 字符串

使用 JSON.stringify() 方法将 JavaScript 对象转换为 JSON 字符串。

let jsObject = {
    name: "张三",
    age: 25,
    city: "北京"
};

// 将 JavaScript 对象转换为 JSON 字符串
let jsonString = JSON.stringify(jsObject);
console.log(jsonString); // 输出: {"name":"张三","age":25,"city":"北京"}
3.3.3.2 JSON 字符串转为 JavaScript 对象

使用 JSON.parse() 方法将 JSON 字符串转换为 JavaScript 对象。

let jsonString = '{"name":"张三","age":25,"city":"北京"}';

// 将 JSON 字符串转换为 JavaScript 对象
let jsObject = JSON.parse(jsonString);
console.log(jsObject.name); // 输出: 张三

3.3.2、JSON 字符串与 Java 对象的转换

3.3.2.1 引入 Jackson 依赖

如果使用 Maven 项目,在 pom.xml 中添加 Jackson 的依赖:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.3</version>
</dependency>
3.2.2.2 JSON 字符串转为 Java 对象

使用 ObjectMapper 类的 readValue() 方法将 JSON 字符串转换为 Java 对象。

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;

public class JsonExample {
    public static void main(String[] args) throws Exception {
        // JSON 字符串
        String jsonString = "{\"name\":\"张三\",\"age\":25,\"city\":\"北京\"}";

        // 创建 ObjectMapper 实例
        ObjectMapper objectMapper = new ObjectMapper();

        // 将 JSON 字符串转换为 Java 对象
        Person person = objectMapper.readValue(jsonString, Person.class);

        // 输出解析后的数据
        System.out.println("姓名: " + person.getName());
        System.out.println("年龄: " + person.getAge());
        System.out.println("城市: " + person.getCity());
    }
}

// Java 对象类
@Data
class Person {
    private String name;
    private int age;
    private String city;
}
3.2.2.3 Java 对象转为 JSON 字符串

使用 ObjectMapper 类的 writeValueAsString() 方法将 Java 对象转换为 JSON 字符串。

import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonExample {
    public static void main(String[] args) throws Exception {
        // 创建一个 Java 对象
        Person person = new Person();
        person.setName("张三");
        person.setAge(25);
        person.setCity("北京");

        // 创建 ObjectMapper 实例
        ObjectMapper objectMapper = new ObjectMapper();

        // 将 Java 对象转换为 JSON 字符串
        String jsonString = objectMapper.writeValueAsString(person);
        System.out.println(jsonString); // 输出: {"name":"张三","age":25,"city":"北京"}
    }
}

四:会话的相关知识

会话管理操作描述
会话创建和持续时间当用户首次登录或访问页面时,服务器创建一个新的会话,并生成唯一的会话 ID 发送给客户端。客户端后续请求会携带该会话 ID,服务器通过此 ID 确认用户身份并使用相同的会话对象。
会话过期或手动销毁会话有一个过期时间(如 30 分钟),在过期时间内用户无请求,服务器会自动使会话失效。用户也可以通过退出登录手动销毁会话,服务器会调用 session.invalidate() 方法清除会话数据。
再次登录会创建新会话当用户退出登录或会话过期后再次登录时,服务器会创建一个新的会话,并生成新的会话 ID,之前会话的数据不会继续保留。

五:源代码

gittee 地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ice___Cpu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值