当前位置: 首页 > news >正文

全双工通信协议WebSocket——使用WebSocket实现智能学习助手/聊天室功能

一.什么是WebSocket?

WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器的全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输

HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。

这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。

这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步 AJAX 请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。

http协议:

07e80c5383974729ba528bceef04debb.jpeg

 

websocket协议:

aec402415caf4ee68e8b89bad2e044c8.jpeg

二.HTTP协议和WebSocket协议对比:

  • HTTP是短连接
  • WebSocket是长连接
  • HTTP通信是单向的,基于请求响应模式 WebSocket支持双向通信
  • HTTP和WebSocket底层都是TCP连接

三.基于WebSocket的智能学习助手功能实现

1.需求

通过 websocket 实现一个简易的聊天室功能 

        当点击智能学习助手选项,会进入一个聊天室,小助手自动向用户问好,用户可以向小助手提问问题,小助手后端查询到问题答案后会进行回复

bfa15c4c381e4e42829925803765104d.png

2.前端

前端环境:vue3+element-plus+pinia

因为我的这个项目用户端和管理端共用该功能,所以URL上带上了从pinia中获取的当前登录用户的信息,用于与后端建立唯一的连接标识

下面的代码是只对于学习小助手组件的.vue文件,读者有需自行扩展

<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";import useUserInfoStore from "@/stores/userInfo.js";
const userInfoStore = useUserInfoStore();
const userInfo = ref({ ...userInfoStore.info });// 发送的信息
const say = ref("");
// 内容
const content = ref("");
// 管理员1,普通用户0
const role = userInfo.value.role == "管理员" ? 1 : 0;
// 发送给后端的URL
const url = ref("ws://localhost:8080/char/" + role);var websocket = null;
//判断当前浏览器是否支持WebSocket
if ("WebSocket" in window) {//连接WebSocket节点websocket = new WebSocket(url.value);
} else {ElMessage.error("Not support websocket");
}//连接发生错误的回调方法
websocket.onerror = function () {ElMessage.error("连接错误");
};//连接成功建立的回调方法
websocket.onopen = function () {ElMessage.success("连接成功");createContent(false, "你好,我是智能学习小助手~");
};//接收到消息的回调方法
websocket.onmessage = function (event) {createContent(false, event.data);
};//连接关闭的回调方法
websocket.onclose = function () {ElMessage.success("连接关闭");
};//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {websocket.close();
};//发送消息
function send() {websocket.send(say.value);createContent(true, say.value);say.value = "";
}//关闭连接
function closeWebSocket() {websocket.close();
}// 构造消息框
const createContent = (isMyMsg, msg) => {let html;// 当前用户消息if (isMyMsg) {html ='<div class="el-row" style="padding: 5px 0">\n' +'  <div class="el-col el-col-22" style="text-align: right; padding-right: 10px">\n' +'    <div style="color: white;text-align: center;border-radius: 10px;font-family: sans-serif;padding: 10px;width:auto;display:inline-block !important;display:inline;background-color: deepskyblue;">' +msg +"</div>\n" +"  </div>\n" +'  <div class="el-col el-col-2">\n' +'  <span class="el-avatar el-avatar--circle" style="height: 40px; width: 40px; line-height: 40px;">\n' +'    <img src="' +userInfo.value.userUrl +'" style="object-fit: cover;">\n' +"  </span>\n" +"  </div>\n" +"</div>";} else {// 助手信息html ='<div class="el-row" style="padding: 5px 0">\n' +'  <div class="el-col el-col-2" style="text-align: right">\n' +'  <span class="el-avatar el-avatar--circle" style="height: 40px; width: 40px; line-height: 40px;">\n' +'    <img src="' +"https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" +'" style="object-fit: cover;">\n' +"  </span>\n" +"  </div>\n" +'  <div class="el-col el-col-22" style="text-align: left; padding-left: 10px">\n' +'    <div style="color: white;text-align: center;border-radius: 10px;font-family: sans-serif;padding: 10px;width:auto;display:inline-block !important;display:inline;background-color: deepskyblue;">' +msg +"</div>\n" +"  </div>\n" +"</div>";}content.value += html;
};
</script>
<template><el-card class="page-container"><template #header><div class="block text-center"><el-row :gutter="20"><el-col :span="6" :offset="11"><span>智能学习小助手</span></el-col></el-row><!-- 分割线 --><el-divider /><!-- 走马灯 --><el-carousel height="150px"><el-carousel-item v-for="item in 4" :key="item"><h3 class="small justify-center" text="2xl">韩磊大帅哥{{ item }}</h3></el-carousel-item></el-carousel></div></template><!-- 滚动条 --><el-scrollbar height="460px"><div v-html="content"></div></el-scrollbar><template #footer><el-row><el-inputstyle="width: 100%":autosize="{ minRows: 4, maxRows: 8 }"type="textarea"v-model="say"placeholder="有困难就找汪汪队~"/></el-row><el-row style="margin-top: 10px"><el-col :span="2" :offset="20"><div class="grid-content ep-bg-purple" /><el-button type="primary" @click="send()">发送</el-button></el-col><el-col :span="1"><div class="grid-content ep-bg-purple" /><el-button type="primary" @click="say = ''">清空文本</el-button></el-col></el-row></template></el-card>
</template>
<style lang="scss" scoped>
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
.el-carousel__item h3 {color: #475669;opacity: 0.75;line-height: 150px;margin: 0;text-align: center;
}
.img {width: 100%;height: 460px;
}
.el-carousel__item:nth-child(2n) {background-color: #99a9bf;
}.el-carousel__item:nth-child(2n + 1) {background-color: #d3dce6;
}
.tip {color: white;text-align: center;border-radius: 10px;font-family: sans-serif;padding: 10px;width: auto;display: inline-block !important;display: inline;
}
.right {background-color: deepskyblue;
}
.left {background-color: forestgreen;
}
</style>

3.后端

后端环境:springboot+lombok+web+mybatisplus+websocket

3.1.Springboot 添加Pom依赖

<!--websocket--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>

3.2.添加Websocket配置


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** WebSocket配置类,用于注册WebSocket的Bean*/
@Configuration
public class WebSocketConfiguration {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}

3.3.构建数据库

3.3.1.配置并连接数据库并创建问题表(自行完成)

3.3.2.pojo类

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("lw_answers")
public class Answer {@TableId(type = IdType.AUTO)private Long answerId; // 问题IDprivate String issue; // 问题private String answer; // 回答
}

3.3.3.注册Mapper接口

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hl.pojo.entity.Answer;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface AnswerMapper extends BaseMapper<Answer> {
}

3.3.4.注册WebSocketServer接口

因为我们每个建立会话的对象要唯一!所以对于不同的用户我们根据用户角色和用户id来建立唯一标识

package com.hl.server.websocket;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hl.common.constant.MessageConstant;
import com.hl.common.context.BaseContext;
import com.hl.common.enumeration.UserRole;
import com.hl.pojo.entity.Answer;
import com.hl.server.mapper.AnswerMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** WebSocket服务*/
@Slf4j
@Component
@ServerEndpoint("/char/{role}")
public class WebSocketServer {/*** WebSocket API 是独立于任何特定框架的标准,* 它的生命周期和管理是由 WebSocket 容器负责的,而不是由 Spring 容器负责。* 因此,WebSocket 容器不会识别或处理 Spring 的依赖注入注解。*/private static AnswerMapper answerMapper;@Autowiredprivate void setAnswerMapper(AnswerMapper answerMapper) {WebSocketServer.answerMapper = answerMapper;}//存放会话对象private static Map<String, Session> sessionMap = new ConcurrentHashMap<>();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session,@PathParam("role") Integer role) {String key=getUserInfo(role);log.info("WebSocketServer onOpen role:{}",key);sessionMap.put(key, session);}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message,@PathParam("role") Integer role) {String key=getUserInfo(role);log.info("WebSocketServer onMessage user:{} message:{}",key,message);LambdaQueryWrapper<Answer> queryWrapper=new LambdaQueryWrapper<Answer>().like(Answer::getIssue,message);Answer ans = answerMapper.selectOne(queryWrapper);String answer = ans==null? MessageConstant.BU_ZHIDAO:ans.getAnswer();sessionMap.get(key).getAsyncRemote().sendText(answer);}/*** 连接关闭调用的方法*/@OnClosepublic void onClose(@PathParam("role") Integer role) {String key=getUserInfo(role);log.info("WebSocketServer onClose:{}",key);sessionMap.remove(key);}/*** 获取唯一标识key*/public String getUserInfo(Integer role) {String key="";// 根据LocalThread获取当前登录用户idLong id= BaseContext.getCurrentId();if(role== UserRole.COMMON.getValue()){//普通用户key=UserRole.COMMON.getDesc()+id;}else{//管理员key=UserRole.ADMIN.getDesc()+id;}return key;}}

注: WebSocket API 是独立于任何特定框架的标准, 它的生命周期和管理是由 WebSocket 容器负责的,而不是由 Spring 容器负责。 因此,WebSocket 容器不会识别或处理 Spring 的依赖注入注解,所以我们不能使用字段注入!

 

 

 

相关文章:

全双工通信协议WebSocket——使用WebSocket实现智能学习助手/聊天室功能

一.什么是WebSocket&#xff1f; WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器的全双工通信——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c;并进行双向数据传输 HTTP 协议是一种无状态的、无连接的、单向的应用…...

Rust-Trait 特征编程

昨夜江边春水生&#xff0c;艨艟巨舰一毛轻。 向来枉费推移力&#xff0c;此日中流自在行。 ——《活水亭观书有感二首其二》宋朱熹 【哲理】往日舟大水浅&#xff0c;众人使劲推船&#xff0c;也是白费力气&#xff0c;而此时春水猛涨&#xff0c;巨舰却自由自在地飘行在水流中…...

彻底理解哈希表(HashTable)结构

目录 介绍优缺点概念哈希函数快速的计算键类型键转索引霍纳法则 均匀的分布 哈希冲突链地址法开放地址法线性探测二次探测再哈希法 扩容/缩容实现哈希创建哈希表质数判断哈希函数插入&修改获取数据删除数据扩容/缩容函数全部代码 哈希表&#xff08;Hash Table&#xff09;…...

微信小程序的汽车维修预约管理系统

文章目录 项目介绍具体实现截图技术介绍mvc设计模式小程序框架以及目录结构介绍错误处理和异常处理java类核心代码部分展示详细视频演示源码获取 项目介绍 系统功能简述 前台用于实现用户在页面上的各种操作&#xff0c;同时在个人中心显示各种操作所产生的记录&#xff1a;后…...

LeetCode:3255. 长度为 K 的子数组的能量值 II(模拟 Java)

目录 3255. 长度为 K 的子数组的能量值 II 题目描述&#xff1a; 实现代码与解析&#xff1a; 模拟 原理思路&#xff1a; 3255. 长度为 K 的子数组的能量值 II 题目描述&#xff1a; 给你一个长度为 n 的整数数组 nums 和一个正整数 k 。 一个数组的 能量值 定义为&am…...

深入了解逻辑回归:机器学习中的经典算法

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

软件测试基础十三(python 函数)

函数 1. 函数的意义 代码复用 提高效率&#xff1a;Python中的函数允许将一段可重复使用的代码封装起来。例如&#xff0c;在一个数据分析项目中&#xff0c;可能需要多次计算一组数据的平均值。可以将计算平均值的代码定义为一个函数&#xff1a; def calculate_average(nu…...

计算机网络——HTTP篇

基础篇 IOS七层网络模型 TCP/IP四层模型&#xff1f; 应⽤层&#xff1a;位于传输层之上&#xff0c;主要提供两个终端设备上的应⽤程序之间的通信&#xff0c;它定义了信息交换的格式&#xff0c;消息会交给下⼀层传输层来传输。 传输层的主要任务就是负责向两台设备进程之间…...

信息化运维方案,实施方案,开发方案,信息中心安全运维资料(软件资料word)

1 编制目的 2 系统运行维护 2.1 系统运维内容 2.2 日常运行维护方案 2.2.1 日常巡检 2.2.2 状态监控 2.2.3 系统优化 2.2.4 软件系统问题处理及升级 2.2.5 系统数据库管理维护 2.2.6 灾难恢复 2.3 应急运行维护方案 2.3.1 启动应急流程 2.3.2 成立应急小组 2.3.3 应急处理过程 …...

自动化工具 Gulp

自动化工具 gulp 摘要 概念&#xff1a;gulp用于自动化开发流程。 理解&#xff1a;我们只需要编写任务&#xff0c;然后gulp帮我们执行 核心概念&#xff1a; 任务&#xff1a;通过定义不同的任务来组织你的构建流程。 管道&#xff1a;通过管道方式将文件从一个插件传递…...

css实现div被图片撑开

固定好盒子的宽度&#xff0c;高度随传过来的图片大小决定 <div class"tab-con"> <img:src"concertInfo.detail"alt""> </div>.tab-con {margin-bottom: 20px;width: 700px;img {width: 700px;height: auto;object-fit: cont…...

Power Pivot、Power BI 和 SQL Server Analysis Services 的公式语言:DAX(数据分析表达式)

DAX&#xff08;Data Analysis Expressions&#xff09;是一种用于 Power Pivot、Power BI 和 SQL Server Analysis Services 的公式语言&#xff0c;旨在帮助用户进行数据建模和复杂计算。DAX 的设计初衷是使数据分析变得简单而高效&#xff0c;特别是在处理数据模型中的表关系…...

大模型应用编排工具Dify二开之工具和模型页面改造

1.前言 简要介绍下 dify&#xff1a; ​ 一款可以对接市面上主流大模型的任务编排工具&#xff0c;可以通过拖拽形式进行编排形成解决某些业务场景的大模型应用。 背景信息&#xff1a; ​ 环境&#xff1a;dify-0.8.3、docker-21 ​ 最近笔者在做 dify的私有化部署和二次…...

Pytorch用BERT对CoLA、新闻组文本数据集自然语言处理NLP:主题分类建模微调可视化分析...

原文链接&#xff1a;https://tecdat.cn/?p38181 自然语言处理&#xff08;NLP&#xff09;领域在近年来发展迅猛&#xff0c;尤其是预训练模型的出现带来了重大变革。其中&#xff0c;BERT 模型凭借其卓越性能备受瞩目。然而&#xff0c;对于许多研究者而言&#xff0c;如何高…...

LightGBM-GPU不能装在WSL,能装在windows上

这是一篇经验总结文章&#xff0c;注重思路&#xff0c;忽略细节。 1.起因 用多个机器学习方法训练模型&#xff0c;比较性能&#xff0c;发现Light GBM方法获得的性能明显更高&#xff0c;但问题是在CPU上训练的速度特别特别慢&#xff0c;需要用GPU训练。 2.开始装LightGB…...

工业相机常用功能之白平衡及C++代码分享

目录 1、白平衡的概念解析 2、相机白平衡参数及操作 2.1 相机白平衡参数 2.2 自动白平衡操作 2.3 手动白平衡操作流程 3、C++ 代码从XML读取参数及设置相机参数 3.1 读取XML 3.2 C++代码,从XML读取参数 3.3 给相机设置参数 1、白平衡的概念解析 白平衡(White Balance)…...

Foundry 单元测试

安装 Foundry 如果你还没有安装 Foundry&#xff0c;请按照此处的说明进行操作&#xff1a;Foundry 安装 Foundry Hello World 只需运行以下命令&#xff0c;它将为你设置环境&#xff0c;创建测试并运行它们。&#xff08;当然&#xff0c;这假设你已经安装了 Foundry&…...

idea database连接数据库后看不到表解决方法、格式化sql快捷键

最下面那个勾选上就可以了 或 格式化sql快捷键&#xff1a; 先选中&#xff0c; 使用快捷键格式化 SQL&#xff1a; Windows/Linux: Ctrl Alt L macOS: Cmd Alt L...

【数学二】线性代数-向量-向量组的秩、矩阵得秩

考试要求 1、理解 n n n维向量、向量的线性组合与线性表示的概念. 2、理解向量组线性相关、线性无关的概念,掌握向量组线性相关、线性无关的有关性质及判别法. 3、了解向量组的极大线性无关组和向量组的秩的概念,会求向量组的极大线性无关组及秩. 4、了解向量组等价的概念,…...

ABAP开发-内存管理

系列文章目录 文章目录 系列文章目录前言一、概述二、程序间调用三、外部会话和内部会话四、SAP内存与ABAP内存五、实例总结 前言 一、概述 内存是程序之间为了传递数据而使用的共享存储空间&#xff0c;在每个程序里使用的内存有SAP内存和ABAP内存 SAP内存分类 SAP内存 主会…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...