网页版五子棋—— WebSocket 协议

目录

·前言

一、背景介绍

二、原理解析

1.连接过程(握手)

2.报文格式

三、代码示例

1.服务端代码

[(1)TestAPI 类](#(1)TestAPI 类)

[(2)WebSocketConfig 类](#(2)WebSocketConfig 类)

2.客户端代码

3.代码演示

·结尾


·前言

从本篇文章开始,我就来与大家分享网页版五子棋项目的一个实现过程了,这个项目一共有以下几个核心模块:用户模块、匹配模块、对战模块,后面文章会按照顺序来对每个模块进行介绍,并且此项目用到的核心技术有:Spring/SpringBoot/SpringMVC、WebSocket、MySQL、MyBatis、HTML/CSS/JS/AJAX,本篇文章来介绍一下 WebSocket 协议的内容、原理及代码示例,下面就开始本篇文章的内容介绍。

一、背景介绍

WebSocket 是一种支持网页端与服务端保持长连接的消息推送机制,什么是消息推送机制呢?在我们传统的 web 程序中,都是属于"一问一答"的形式,如下图所示:

这种情况下,服务器是被动的一方,如果客户端不主动发送请求,服务器就不会主动给客户端响应,我们所要编写的五子棋程序使用这种形式就会出现下面的情况:

此时由于玩家2 没有向服务器发送请求,就会导致玩家2 收不到玩家1 的落子情况,消息推送机制就是可以让服务器主动给客户端发送数据,如果想使用传统"一问一答"的形式实现消息推送的效果也是可以做到,那就要采用"轮询"的方式,如下图所示:

此时,在玩家1 没有落子之前,玩家2 就会以一定的时间间隔不断的向服务器发起请求,来询问玩家1 有没有落子,很明显,使用这种轮询操作的体验效果与开销与询问的时间间隔有关,关系如下:

  • 如果轮询时间间隔比较长:玩家1 落子之后,玩家2 不能及时获取到落子结果,对弈的体验性降低;
  • 如果轮询时间间隔比较短:虽然及时性得到改善,但是玩家2 就要浪费更多的机器资源,比如:网络带宽。

为了更好的实现消息推送机制,在我们五子棋项目中,就引入了 WebSocket 协议,这是一种接近于 TCP 级别的通信方式,一旦建立好连接,客户端与服务器都可以主动向对方发送数据。

基于 WebSocket 来实现的消息推送在五子棋项目中就会起到下图的效果:

此时,玩家1 的落子请求发送给服务器后,服务器就会自动把响应推送给玩家1 与玩家2 。

二、原理解析

1.连接过程(握手)

在我们使用网页端想尝试和服务器建立 WebSocket 连接会经过以下几个步骤:

  1. 网页端先给服务器发送一个 HTTP 请求,请求中会带有特殊的 header(Connection:Upgrade、Upgrade:WebSocket)这个 header 就是告诉服务器,我们需要进行协议的升级;
  2. 如果服务器支持 WebSocket,就会返回一个特殊的 HTTP 响应,这个响应的状态码是 101 (切换协议);
  3. 客户端与服务器之间开始使用 WebSocket 来进行通信。

上述的具体过程可以如下图所示:

2.报文格式

WebSocket 的报文格式如下图所示:

WebSocket 也是一种应用层的协议,它的下层是基于 TCP 的,下面我来对上图的部分内容做一个简单介绍:

  • FIN:当这里的内容为 1 时,表示要断开 WebSocket 连接;
  • RSV1~3:这是保留位,一般为0;
  • opcode:这是操作码,描述了当前 WebSocket 报文是什么类型,它可以描述当前报文是一个文本帧还是一个二进制帧,是一个 ping 帧还是一个 pong 帧;
  • Payload length:这是表示当前数据报携带的数据载荷的长度,它是一个可边长的字段,一个 WebSocket 的数据报能承载的载荷长度是非常长的;
  • Payload data:这是表示当前 WebSocket 实际报文要传输的数据载荷。

三、代码示例

下面我来实现一个简单的 WebSocket 代码,这里服务端的代码由 Java 来编写,客户端的代码由 JS 来编写。

1.服务端代码

编写服务端代码的流程如下:

  1. 创建出一个 Spring 项目,在 Spring 中内置了 WebSocket 可以直接使用;
  2. 创建 api.TestAPI 类,这个类用来处理 WebSocket 请求,并返回响应;
  3. 创建 config.WebSocketConfig 类,这个类用来配置请求路径和 TextWebSocketHandler 之间的对应关系。

代码结构如下所示:

(1)TestAPI 类

在这里,继承了 TextWebSocketHandler 这个类,我们需要重写里面的几个方法,如下图所示的选中方法: 关于重写的这四个方法都是什么作用,我在代码的注释中会进行详细的标注,那么关于 TestAPI 这个类的全部代码及注释如下所示:

java 复制代码
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

// 添加 @Component 注解,交给 Spring 管理
@Component
public class TestAPI extends TextWebSocketHandler {
    @Override
    // 连接就绪后就会触发这个方法来告知连接成功
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("连接成功");
    }

    @Override
    // 客户端/服务器 给 服务器/客户端 发送信息通过这个方法就可以接收到信息
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println("收到消息" + message.getPayload());
        // 让服务器收到消息之后原封不动的返回
        session.sendMessage(message);
    }

    @Override
    // 传输出现异常就会触发这个方法
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.out.println("连接异常");
    }

    @Override
    // 如果客户端/服务器关闭连接就会执行这个方法
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("连接关闭");
    }
}

(2)WebSocketConfig 类

在这里,实现了 WebSocketConfigurer 接口,用于 WebSocket 的相关配置,代码及介绍注释如下所示:

java 复制代码
import com.example.springgobang.aip.TestAPI;
import org.springframework.beans.factory.annotation.Autowired;
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;

// @EnableWebSocket 注解用来告诉 Spring 这是配置 WebSocket 的类
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    // 自动注入
    @Autowired
    private TestAPI testAPI;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 当客户端连接 /test 这样的路径后就会触发 testAPI 进而调用其内部的方法
        registry.addHandler(testAPI,"/test");
    }
}

2.客户端代码

这里我们编写一个 html 页面来与服务器代码建立 WebSocket 连接,TestAPI.html 页面代码及介绍如下所示:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!--输入框-->
    <input type="text" id="message">
    <!--按钮-->
    <button id="sendButton">发送</button>

    <script>
        // 创建 WebSocket 实例, 要保证这里的路径与配置的路径一致 /test
        let webSocket = new WebSocket("ws://127.0.0.1:8080/test");
        // 连接成功后会调用这个函数
        webSocket.onopen = function() {
            console.log("连接成功");
        }
        // 收到信息后会调用这个函数
        webSocket.onmessage = function(e) {
            console.log("收到消息: " + e.data);
        }
        // 连接异常时会执行这个函数
        webSocket.onerror = function() {
            console.log("连接异常");
        }
        // 连接断开后会调用这个函数
        webSocket.onclose = function() {
            console.log("连接关闭");
        }
        // 实现点击按钮后,通过 WebSocket 发送请求
        let messageInput = document.querySelector('#message');
        let sendButton = document.querySelector('#sendButton');
        sendButton.onclick = function() {
            console.log("发送消息: " + messageInput.value);
            // send 函数就会把输入框中的内容发送出去
            webSocket.send(messageInput.value);
        }
    </script>
</body>
</html>

3.代码演示

此时,关于 WebSocket 的演示代码就编写完毕了,下面我们来启动程序,在电脑的浏览器上输入:http://127.0.0.1:8080/TestAPI.html 来访问 TestAPI.html 页面,然后鼠标右键点击检查,或者按键盘上的 F12 按钮,进入控制台观察日志信息,然后在输入框中输入数据点击发送就可以观察到效果,具体的效果演示如下图所示:

此时在服务端断开连接(停止程序),结果如下图所示:

通过上述的运行结果可以看出来,服务端可以随时调用 session.sendMessage() 方法来给客户端发送响应,从而可以实现消息推送这样的机制。

·结尾

文章到此就要结束了,本篇文章介绍了 WebSocket 协议的一些基础原理,并用 WebSocket 进行了简单的网络编程,来演示了消息推送的效果,这是五子棋项目重要逻辑的实现依据,使用 WebSocket 就可以保证对弈双方可以实时在页面上观察到对方的落子情况,介绍完 WebSocket 之后就要开始五子棋项目的正式编写了,如果对本篇文章的内容有所疑惑,欢迎在评论区进行留言,我们下一篇文章再见吧~~~

相关推荐
終不似少年遊*4 分钟前
华为云计算HCIE笔记05
网络·华为云·云计算·学习笔记·hcie·认证·hcs
蜜獾云18 分钟前
docker 安装雷池WAF防火墙 守护Web服务器
linux·运维·服务器·网络·网络安全·docker·容器
程序猿进阶1 小时前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
小林熬夜学编程1 小时前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
Hacker_Fuchen2 小时前
天融信网络架构安全实践
网络·安全·架构
上海运维Q先生2 小时前
面试题整理15----K8s常见的网络插件有哪些
运维·网络·kubernetes
ProtonBase2 小时前
如何从 0 到 1 ,打造全新一代分布式数据架构
java·网络·数据库·数据仓库·分布式·云原生·架构
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭10 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
李小白6611 小时前
Spring MVC(上)
java·spring·mvc