SpringBoot整合websockt实现消息对话

news2024/12/26 17:16:35

文章目录

  • 前言
  • websockt
    • 什么是websockt?
    • websockt和Socket区别
    • 代码部分
    • 实战应用

前言

websockt

什么是websockt?

WebSocket是一种在Web应用程序中实现实时双向通信的技术。Web应用程序通常是基于HTTP协议的,HTTP是一种请求/响应式的协议,客户端发起请求,服务器响应请求并发送响应,客户端收到响应后关闭连接。这意味着,如果客户端需要不断地从服务器获取更新,它必须定期发送请求以检查更新,这将导致大量的网络流量和不必要的服务器负载。

WebSocket通过在客户端和服务器之间创建持久化连接,允许双向实时通信。这意味着服务器可以主动向客户端推送更新,而不必等待客户端不停地请求。WebSocket连接始于HTTP握手,然后升级到WebSocket协议。在升级过程中,客户端和服务器之间的数据传输方式从HTTP变成了WebSocket协议,这样可以降低传输数据的延迟和提高传输数据的可靠性。

在WebSocket协议中,数据是以帧的形式传输的,帧包括数据头和数据体两个部分。数据头指示数据类型,数据传输的方向等信息,数据体则包括实际传输的数据。WebSocket支持文本类型和二进制类型的数据。文本类型的数据使用UTF-8编码,而二进制类型的数据可以包括任何二进制数据,如图像、音频、视频等。

WebSockets是非常适合实时通信的技术。它可以用于在线游戏、在线聊天、推送通知、实时监控等,并且比传统的轮询技术更加高效和可靠。对于现代Web应用程序而言,WebSockets已经成为不可或缺的技术之一。

websockt和Socket区别

区别WebSocketSocket
应用层协议基于HTTP协议,升级为WebSocket协议没有明确的应用层协议,常用于传输TCP或UDP数据
传输数据类型支持文本和二进制类型的数据通常用于传输二进制数据,如图片、视频等
客户端和服务器通信支持实时双向通信支持双向通信,但通常需要进行轮询或长连接
传输效率比传统的轮询技术更加高效和可靠,不会频繁请求服务器传输效率较低,需要进行轮询或者长连接来保持连接,会频繁请求服务器
应用场景适用于实时通信,如在线游戏、在线聊天、推送通知、实时监控等适用于需要传输大量二进制数据的场景,如文件传输、图片处理等

代码部分

引入maven依赖

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

说明:默认版本为1.5.10RELEASE,可自定义版本,加入“<version">"”标签即可
常量类

package com.dmsdbj.itoo.utils;
public class Constant {
    //webSocket相关配置
    //链接地址
    public static String WEBSOCKETPATHPERFIX = "/ws-push";
    public static String WEBSOCKETPATH = "/endpointWisely";
    //消息代理路径
    public static String WEBSOCKETBROADCASTPATH = "/topic";
    //前端发送给服务端请求地址
    public static final String FORETOSERVERPATH = "/welcome";
    //服务端生产地址,客户端订阅此地址以接收服务端生产的消息
    public static final String PRODUCERPATH = "/topic/getResponse";
    //点对点消息推送地址前缀
    public static final String P2PPUSHBASEPATH = "/user";
    //点对点消息推送地址后缀,最后的地址为/user/用户识别码/msg
    public static final String P2PPUSHPATH = "/msg";
}

接收前端消息实体

package com.dmsdbj.itoo.vo;
public class WiselyMessage {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

后台发送消息实体

package com.dmsdbj.itoo.vo;

public class WiselyResponse {
    private String responseMessage;
    public WiselyResponse(String responseMessage){
        this.responseMessage = responseMessage;
    }
    public String getResponseMessage() {
        return responseMessage;
    }
    public void setResponseMessage(String responseMessage) {
        this.responseMessage = responseMessage;
    }
}

配置websocket

package com.dmsdbj.itoo.config;

import com.dmsdbj.itoo.utils.Constant;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@Configuration
// @EnableWebSocketMessageBroker注解用于开启使用STOMP协议来传输基于代理(MessageBroker)的消息,这时候控制器(controller)
// 开始支持@MessageMapping,就像是使用@requestMapping一样。
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        //注册一个Stomp的节点(endpoint),并指定使用SockJS协议。
        stompEndpointRegistry.addEndpoint(Constant.WEBSOCKETPATH).withSockJS();
    }
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //服务端发送消息给客户端的域,多个用逗号隔开
        registry.enableSimpleBroker(Constant.WEBSOCKETBROADCASTPATH, Constant.P2PPUSHBASEPATH);
        //定义一对一推送的时候前缀
        registry.setUserDestinationPrefix(Constant.P2PPUSHBASEPATH);
        //定义websoket前缀
        registry.setApplicationDestinationPrefixes(Constant.WEBSOCKETPATHPERFIX);
    }
}

采用JPA方法进行操作数据库DAO层

package com.dmsdbj.itoo.repository;
import com.dmsdbj.itoo.entity.TbUser;
import org.springframework.data.jpa.repository.JpaRepository;
public interface TbUseRepository extends JpaRepository<TbUser,Long> {}

sql语句

CREATE TABLE `tb_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL COMMENT '用户名',
  `password` varchar(32) DEFAULT NULL COMMENT '密码,加密存储',
  `phone` varchar(20) DEFAULT NULL COMMENT '注册手机号',
  `email` varchar(50) DEFAULT NULL COMMENT '注册邮箱',
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`) USING BTREE,
  UNIQUE KEY `phone` (`phone`) USING BTREE,
  UNIQUE KEY `email` (`email`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=18333611875 DEFAULT CHARSET=utf8 COMMENT='用户表';

TbUser实体类

package com.dmsdbj.itoo.entity;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.io.Serializable;
@Entity
@Data
public class TbUser implements Serializable {
    @Id
    private Long id;
    private String username;
    private String password;
    private String phone;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
}

Service实现类

@Service
public class TbUseServiceImpl implements TbUseService {

    @Autowired
    TbUseRepository repository;
    
/**
     * 从数据库查询用户,给订阅者发送消息
     * @return
     */
    @Override
    public List<Long> send2Users() {
         return repository.findAll().stream().map(s -> s.getId()).collect(Collectors.toList());

    }
}

Service接口类

public interface TbUseService {
 List<Long> send2Users();
}

Façade实现类

@Service
public class TbUseFacadeImpl implements TbUseFacade {

    @Autowired
    TbUseService tbUseService;

@Override
    public List<Long> send2Users() {
       return tbUseService.send2Users();
    }
}

Façade接口类

public interface TbUseFacade {
List<Long> send2Users();
}

Controller类

@Controller
public class SendInfoController {
    @Reference
    TbUseFacade tbUseFacade;
    @Autowired
    private SimpMessagingTemplate template;
    @Resource
    WebSocketService webSocketService;
    @MessageMapping(Constant.FORETOSERVERPATH)//@MessageMapping和@RequestMapping功能类似,用于设置URL映射地址,浏览器向服务器发起请求,需要通过该地址。
//    @SendTo(Constant.PRODUCERPATH)//如果服务器接受到了消息,就会对订阅了@SendTo括号中的地址传送消息。
    public WiselyResponse say(WiselyMessage message) throws Exception {
        List<Long> userId = tbUseFacade.send2Users();
        userId.forEach(s->{
            template.convertAndSendToUser(s.toString(),Constant.P2PPUSHPATH, new WiselyResponse(message.getName()));
        });
        return null;
    }
}

前端页面
sendws.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>Spring Boot+WebSocket+广播式</title>

</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">貌似你的浏览器不支持websocket</h2></noscript>
<div>
    <div>
        <button id="connect" onclick="connect();">连接</button>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>
    </div>
    <div id="conversationDiv">
        <label>输入要发送的消息</label><input type="text" id="name" />
        <button id="sendName" onclick="sendName();">发送</button>
        <p id="response"></p>
        <p id="response1"></p>
    </div>
</div>
<!--<script th:src="@{sockjs.min.js}"></script>
<script th:src="@{stomp.min.js}"></script>
<script th:src="@{jquery.js}"></script>-->
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script th:inline="javascript">
    var stompClient = null;
    //此值有服务端传递给前端,实现方式没有要求
    //var userId = [[${userId}]];
    var userId = 'd892bf12bf7d11e793b69c5c8e6f60fb';

    function setConnected(connected) {
        document.getElementById('connect').disabled = connected;
        document.getElementById('disconnect').disabled = !connected;
        document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
        $('#response').html();
    }

    function connect() {
        var socket = new SockJS('/endpointWisely'); //1连接SockJS的endpoint是“endpointWisely”,与后台代码中注册的endpoint要一样。
        stompClient = Stomp.over(socket);//2创建STOMP协议的webSocket客户端。
        stompClient.connect({}, function(frame) {//3连接webSocket的服务端。
            setConnected(true);
            console.log('开始进行连接Connected: ' + frame);
        });
    }


    function disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        }
        setConnected(false);
        console.log("Disconnected");
    }

    function sendName() {
        var name = $('#name').val();
        console.log(name);
        //通过stompClient.send()向地址为"/welcome"的服务器地址发起请求,与@MessageMapping里的地址对应。因为我们配置了registry.setApplicationDestinationPrefixes(Constant.WEBSOCKETPATHPERFIX);所以需要增加前缀/ws-push/
        stompClient.send("/ws-push/welcome", {}, JSON.stringify({ 'name': name }));
    }
</script>
</body>
</html>

subws.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>Spring Boot+WebSocket+广播式</title>

</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">貌似你的浏览器不支持websocket</h2></noscript>
<div>
    <div>
        <button id="connect" onclick="connect();">连接</button>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>
    </div>
    <div id="conversationDiv">
        <label>输入你的userId</label><input type="text" id="userId" />
        <button id="subMsg" onclick="subMsg();">订阅消息</button>
        <p id="response"></p>
        <p id="response1"></p>
    </div>
</div>
<!--<script th:src="@{sockjs.min.js}"></script>
<script th:src="@{stomp.min.js}"></script>
<script th:src="@{jquery.js}"></script>-->
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script th:inline="javascript">
    var stompClient = null;
    //此值有服务端传递给前端,实现方式没有要求
    //var userId = [[${userId}]];

    function setConnected(connected) {
        document.getElementById('connect').disabled = connected;
        document.getElementById('disconnect').disabled = !connected;
        document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
        $('#response').html();
    }

    function connect() {
        var socket = new SockJS('/endpointWisely'); //1连接SockJS的endpoint是“endpointWisely”,与后台代码中注册的endpoint要一样。
        stompClient = Stomp.over(socket);//2创建STOMP协议的webSocket客户端。
        stompClient.connect({}, function(frame) {//3连接webSocket的服务端。
            setConnected(true);
            console.log('开始进行连接Connected: ' + frame);
            //4通过stompClient.subscribe()订阅服务器的目标是'/topic/getResponse'发送过来的地址,与@SendTo中的地址对应。
            /*stompClient.subscribe('/topic/getResponse', function(respnose){
                showResponse(JSON.parse(respnose.body).responseMessage);
            });*/

        });
    }


    function subMsg() {
        var userId = $('#userId').val();
        console.log(userId);
        $.ajax({
            type: 'GET',
            url: '/user/insert?userId='+userId,
            //data: userId,
            success: function (data) {
                console.log(data);
                //4通过stompClient.subscribe()订阅服务器的目标是'/user/' + userId + '/msg'接收一对一的推送消息,其中userId由服务端传递过来,用于表示唯一的用户,通过此值将消息精确推送给一个用户
                stompClient.subscribe('/user/' + userId + '/msg', function(respnose){
                    console.log(respnose);
                    showResponse1(JSON.parse(respnose.body).responseMessage);
                });
            }
        });
    }

    function disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        }
        setConnected(false);
        console.log("Disconnected");
    }

    function showResponse1(message) {
        var response = $("#response1");
        response.html(message);
    }
</script>
</body>
</html>

实战应用

①启动springboot-dubbo-service的主启动类“SpringbootDubboServiceApplication”
②启动springboot-dubbo-web的主启动类“SpringbootDubboWebApplication”
③访问
http://localhost:9000/sendws 发送消息方
http://localhost:9000/subws 订阅消息方
④分别进行连接操作
订阅方输入订阅方的连接账号userid,后期就可以接收发送端发送的消息
连接后的结果展示
在这里插入图片描述

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

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

相关文章

【服务器使用基础】---华为云云耀云服务器实例使用实践

&#x1f996;我是Sam9029&#xff0c;一个前端 Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-JS学习,CSS学习,Vue-2领域博主 **&#x1f431;‍&#x1f409;&#x1f431;‍&#x1f409;恭喜你&#xff0c;若此文你认为写的不错&#xff0c;不要吝啬你的赞扬&#xff0c;求…

肖sir__linux详解__003(vim命令)

linux 文本编辑命令 作用&#xff1a;用于编辑一个文件 用法&#xff1a;vim 文件名称 或者vi &#xff08;1&#xff09;编辑一个存在的文档 例子&#xff1a;编辑一个file1文件 vim aa &#xff08;2&#xff09;编辑一个文件不存在&#xff0c;会先创建文件&#xff0c;再…

Python-图像拼接神器-stitching

多幅图像的拼接 采用这个包&#xff0c;图像拼接结果很好~ 代码只需要三四行 import stitching import cv2imgs ["data/test02/1Hill.jpg","data/test02/2Hill.jpg","data/test02/3Hill.jpg",] stitcher stitching.Stitcher() panorma stit…

postgresql-多表连接

postgresql-多表连接 内连接查询左外连接查询右外连接查询全外连接查询交叉连接查询简写 总结 内连接查询 内连接用于返回两个表中匹配的数据行&#xff0c;使用关键字INNER JOIN表示&#xff0c;也可以简写成JOIN&#xff1b; selecte.first_name ,d.department_id fromcps…

无涯教程-JavaScript - POISSON函数

POISSON函数取代了Excel 2010中的POISSON.DIST函数。 描述 该函数返回泊松分布。泊松分布的常见应用是预测特定时间的事件数。 语法 POISSON(x,mean,cumulative)争论 Argument描述Required/OptionalXThe number of events.RequiredMeanThe expected numeric value.Require…

leecode学习(1)

一、题目 给定一个数组nums和一个目标值target,请你再该数组中找出和为目标值的那两个数&#xff0c;并返回数组的下标&#xff0c;你可以假设输入只会对应一个答案&#xff0c;但是数组的同一个元素不能使用两次。 二、解题思路 目的就是要求出两数之和等于目标值嘛。 就是…

探索树堆Treap和红黑树的优势和劣势

探索树堆Treap和红黑树的优势和劣势 一、背景知识二、树堆&#xff08;Treap&#xff09;的介绍三、红黑树&#xff08;RB-Tree&#xff09;的介绍四、树堆&#xff08;Treap&#xff09;与红黑树&#xff08;RB-Tree&#xff09;的比较总结 博主简介 &#x1f4a1;一个热爱分享…

王道考研数据结构

文章目录 C 环境准备官方文档环境准备在线运行VSCode 环境报错解决 绪论线性表顺序表链表错题 栈、队列和数组栈队列栈的应用之中缀转后缀特殊矩阵用数组压缩存储错题 串模式匹配之暴力和KMP 树与二叉树二叉树树和森林哈夫曼树和哈夫曼编码并查集错题 图图的基本概念图的存储及…

LeetCode494. 目标和

494. 目标和 文章目录 [494. 目标和](https://leetcode.cn/problems/target-sum/)一、题目二、题解方法一&#xff1a;目标和路径计数算法方法二&#xff1a;01背包方法三&#xff1a;01背包一维数组 一、题目 给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个…

IPC进程间通信及示例代码

一. 什么是进程通信 进程通信&#xff08; InterProcess Communication&#xff0c;IPC&#xff09;就是指进程之间的信息交换。实际上&#xff0c;进程的同步与互斥本质上也是一种进程通信&#xff08;这也就是待会我们会在进程通信机制中看见信号量和 PV 操作的原因了&#x…

Ubuntu18.04使用Systemback制作系统镜像并还原

系列文章目录 文章目录 系列文章目录前言一、下载Systemback工具二、制作系统镜像到U盘三、安装制作系统 前言 在Ubuntu系统中开发项目时&#xff0c;有时会希望将项目移植到另外一台计算机&#xff08;如工控机等&#xff09;上进行部署&#xff0c;通常会在新计算机中安装Ub…

Qt +VTK+Cmake 编译和环境配置(第二篇,中级篇, 重新编译)

1.下载VTK和Cmake 这里不介绍了。我的VTK 8.2.0 cmake 3.27.4 就是不服这编译器了。重新来一次 打开Cmake&#xff0c;把VTK源文件路径和目标路径设置一下&#xff08;目标路径自己设置&#xff0c;随意&#xff09; 点击Configure&#xff1a;。 点击下一步 选择好 Qt的gcc…

国际版阿里云/腾讯云:弹性高性能计算E-HPC入门概述

入门概述 本文介绍E-HPC的运用流程&#xff0c;帮助您快速上手运用弹性高性能核算。 下文以创立集群&#xff0c;在集群中安装GROMACS软件并运转水分子算例进行高性能核算为例&#xff0c;介绍弹性高性能核算的运用流程&#xff0c;帮助您快速上手运用弹性高性能核算。运用流程…

selenium 自动化测试——环境搭建

安装python&#xff0c;并且使用pip命令安装 selenium pip3 install selenium 然后尝试第一次使用selenium 完成一个简单的测试自动化脚本 from selenium import webdriver from selenium.webdriver.common.by import By import timedriver webdriver.Chrome() driver.get(…

A 股个股资金流排行 API 数据接口

A 股个股资金流排行 API 数据接口 全量股票资金流排名&#xff0c;多时间区间&#xff0c;全量A股数据。 1. 产品功能 支持所有A股资金流数据查询&#xff1b;每日定时更新数据&#xff1b;支持多时间段查询&#xff1b;超高的查询效率&#xff0c;数据秒级返回&#xff1b;数…

【问大家】电商问答数据的采集与深度分析

1. 引言 电商运营多年&#xff0c;功能越来越完善&#xff0c;我们发现当您购买过该商品之后&#xff0c;在消息-互动这里会看到别的网友提问的有关该商品的问题&#xff0c;这个功能叫问大家。 问大家模块可以说填补了宝贝评价部分的短板&#xff0c;评价部分单向传播属性较…

CSS中你不得不知道的css优先级

在我们定义css样式时&#xff0c;经常出现两个或更多规则应用在同一元素上&#xff0c;这时就会出现优先级的问题。其实css为每一种基础选择器都分配了一个权重。 我们简化理解&#xff1a; CSS权重计算&#xff1a; 最顶层&#xff1a;!important 权重值&#xff1a;…

【Django】让SQLite数据库中表名支持重命名的方法

修改了数据库表名之后&#xff0c;更新数据库时跳错&#xff1a; django.db.utils.NotSupportedError: Renaming the japi_api_info table while in a transaction is not supported on SQLite < 3.26 because it would break referential integrity. Try adding atomic F…

无涯教程-JavaScript - PERCENTILE函数

PERCENTILE函数替代Excel 2010中的PERCENTILE.INC函数。 描述 该函数返回范围中值的第k个百分位数。您可以使用此功能建立接受阈值。 语法 PERCENTILE (array,k)争论 Argument描述Required/OptionalArrayThe array or range of data that defines relative standing.Requi…

230920-部署Gradio到已有FastAPI及服务器中

1. 官方例子 run.py from fastapi import FastAPI import gradio as grCUSTOM_PATH "/gradio"app FastAPI()app.get("/") def read_main():return {"message": "This is your main app"}io gr.Interface(lambda x: "Hello, …