网页版五子棋—— WebSocket 协议

目录

·前言

一、背景介绍

二、原理解析

1.连接过程(握手)

2.报文格式

三、代码示例

1.服务端代码

(1)TestAPI 类

(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 这个类的全部代码及注释如下所示:

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 的相关配置,代码及介绍注释如下所示:

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 {// 自动注入@Autowiredprivate TestAPI testAPI;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {// 当客户端连接 /test 这样的路径后就会触发 testAPI 进而调用其内部的方法registry.addHandler(testAPI,"/test");}
}

2.客户端代码

        这里我们编写一个 html 页面来与服务器代码建立 WebSocket 连接,TestAPI.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 实例, 要保证这里的路径与配置的路径一致 /testlet 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 之后就要开始五子棋项目的正式编写了,如果对本篇文章的内容有所疑惑,欢迎在评论区进行留言,我们下一篇文章再见吧~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/59416.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【综合案例】使用React编写B站评论案例

一、效果展示 默认效果&#xff0c;一开始默认按照最热进行排序 发布了一条评论 按照最新进行排序 按照最新进行排序 二、效果说明 页面上默认有3条评论&#xff0c;且一开始进入页面的时候是按照点赞数量进行倒序排列展示&#xff0c;可以点击【最热 、最新】进行排序的切换。…

docker镜像文件导出导入

1. 导出容器&#xff08;包含内部服务&#xff09;为镜像文件&#xff08;docker commit方法&#xff09; 原理&#xff1a;docker commit命令允许你将一个容器的当前状态保存为一个新的镜像。这个新镜像将包含容器内所有的文件系统更改&#xff0c;包括安装的软件、配置文件等…

区块链技术与应用-PKU 学习笔记

课程地址 资料&#xff1a; ETH-Security 区块链学习记录_比特币 BTC 密码学原理 比特币&#xff0c;又称加密货币(crypto-currency)&#xff0c;它主要利用了密码学中的哈希函数(cryptographic hash function)的抗碰撞特性(collision resistance)和单向散列特性(hiding) …

在Java中,实现数据库连接通常使用JDBC

学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把手教你开发炫酷的vbs脚本制作(完善中……&#xff09; 4、牛逼哄哄的 IDEA编程利器技巧(编写中……&#xff09; 5、面经吐血整理的 面试技…

【优选算法 — 双指针】双指针小专题

和为 s 的两个数 和为s的两个数 题目描述 解法一&#xff1a;暴力枚举 暴力枚举&#xff0c;先固定一个数&#xff0c;然后让这个数和另一个数匹配相加&#xff0c; 如果当前的数 所有剩余的数 target&#xff0c;则返回这两个数&#xff0c;否则固定下一个数&#…

并查集(基础学习与应用)

并查集 基本原理&#xff1a; 对于多个集合&#xff0c;每个集合中的多个元素用一颗树的形式表示&#xff0c;根节点的编号即为整个集合的编号&#xff0c;每个树上节点存储其父节点&#xff0c;使得当前集合的每个子节点都可以通过对父节点的询问来找到根节点&#xff0c;根…

003-Kotlin界面开发之声明式编程范式

概念本源 在界面程序开发中&#xff0c;有两个非常典型的编程范式&#xff1a;命令式编程和声明式编程。命令式编程是指通过编写一系列命令来描述程序的运行逻辑&#xff0c;而声明式编程则是通过编写一系列声明来描述程序的状态。在命令式编程中&#xff0c;程序员需要关心程…

Spring Boot 与 Vue 共筑地方特色美食分享卓越平台

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

react使用Fullcalendar 实战用法

使用步骤请参考&#xff1a;react使用Fullcalendar 卡片式的日历&#xff1a; 需求图&#xff1a; 卡片式的日历&#xff0c;其实我是推荐 antd的&#xff0c;我两个都写了一下都能实现。 antd 的代码&#xff1a; antd的我直接用的官网示例&#xff1a;antd 日历示例 i…

Flutter 正在切换成 Monorepo 和支持 workspaces

其实关于 Monorepo 和 workspaces 相关内容在之前《Dart 3.5 发布&#xff0c;全新 Dart Roadmap Update》 和 《Flutter 之 ftcon24usa 大会&#xff0c;创始人分享 Flutter 十年发展史》 就有简单提到过&#xff0c;而目前来说刚好看到 flaux 这个新进展&#xff0c;所以就再…

在做题中学习(74):比较含退格的字符串

解法&#xff1a;用栈来模拟 思路&#xff1a;不用真的定义一个栈,用字符串string来模拟栈的行为 入栈&#xff1a;s[i] ! #时 push_back(s[i]) 出栈:s[i] # 的时候&#xff0c;并且s.size() > 0&#xff0c;pop_back(s[i])循环结束得到结果 注意&#xff1a;如果真的…

前后端交互通用排序策略

目录 排序场景 排序实现思路 1. 静态代码排序实现 2.数据库驱动排序实现 3. 基于Java反射的动态排序实现 通用排序工具 SortListUtil 结语 排序场景 在面向前端数据展示的应用场景中&#xff0c;我们旨在实现一个更加灵活的排序机制&#xff0c;该机制能够支持对从后端传递…

MD5(Crypto)

解题思路 打开文件发现一串代码&#xff0c;结合题目提示&#xff0c;应该是 MD5 加密。 找个在线的 MD5 解密网站&#xff0c;行云流水得到 flag。 题目设计原理 题目设计&#xff1a;无他&#xff0c;MD5 加密。 题目原理&#xff1a; MD5&#xff08;Message-Digest Algo…

跟李沐学AI:BERT

什么是NLP中的迁移学习 使用预训练好的模型来抽取词、句子的特征&#xff1a;Word2Vec或者预训练好的语言模型。 使用预训练好的语言模型&#xff0c;一般不会再对语言模型进行微调&#xff0c;即不进行更新。 Word2Vec一般用于替代embedding层 但是Word2Vec往往忽略了时序…

单元/集成测试解决方案

在项目开发的前期针对软件单元/模块功能开展单元/集成测试&#xff0c;可以尽早地发现软件Bug&#xff0c;避免将Bug带入系统测试阶段&#xff0c;有效地降低HIL测试的测试周期&#xff0c;也能有效降低开发成本。单元/集成测试旨在证明被测软件实现其单元/架构设计规范、证明被…

C语言复习第9章 字符串/字符/内存函数

目录 一、字符串函数1.1 读取字符串gets函数原型Example 1.2 字符串拷贝strcpy函数原型模拟实现官方源码 1.3 求字符串长度strlen函数原型关于返回值size_与算术转换的一个易错点模拟实现:递归模拟实现:指针-指针模拟实现:暴力官方源码 1.4 字符串追加strcat函数原型注意自己给…

WPF 特性------Binding

工业控制中&#xff0c;经常会需要把一个bool 型输入信号的状态显示在面板上&#xff0c;使用wpf 绑定的办法&#xff0c;可简洁实现&#xff1a; 实现步骤&#xff1a; 1&#xff0c;定义类&#xff1a; using System; using System.Collections.Generic; using System.Com…

【MySQL】深层理解索引及特性(重点)--下(12)

索引&#xff08;重点&#xff09; 1. 索引的作用2. 索引操作2.1 主键索引2.1.1 主键索引的特点2.1.2 创建主键索引 2.2 唯一键索引2.2.1 唯一键索引的特点2.2.2 唯一索引的创建 2.3 普通索引2.3.1 普通索引的特点2.3.2 普通索引的创建 2.4 全文索引2.4.1 全文索引的作用2.4.2 …

基于SpringBoot+微信小程序+协同过滤算法+二维码订单位置跟踪的农产品销售平台-新

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; “农产品商城”小程序…

C++ | Leetcode C++题解之第541题反转字符串II

题目&#xff1a; 题解&#xff1a; class Solution { public:string reverseStr(string s, int k) {int n s.length();for (int i 0; i < n; i 2 * k) {reverse(s.begin() i, s.begin() min(i k, n));}return s;} };