java后端使用websocket实现与客户端之间接收及发送消息

2023-11-06

客户端请求websocket接口,连接通道=》我这边业务成功客户端发消息=》客户端自动刷新。

接口:ws://localhost:8080/websocket/xx



import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @author Administrator
 */
@Configuration
public class WebSocketConfig implements WebSocketConfigurer {

    
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}
import cn.hutool.core.util.StrUtil;
import xx.Constant;
import xx.utils.JwtHelper;
import xx.utils.RedisUtil;
import xx.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@Slf4j
@ServerEndpoint("/websocket/{key}")
public class WebSocketService {
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    private static CopyOnWriteArraySet<WebSocketService> webSockets = new CopyOnWriteArraySet<>();
    //用来存放每个客户端对应的websocket对象
    private static Map<String,Session> sessionPool = new HashMap<>();
    private String userIdStr;
    private static WebSocketService webSocketService;
    @PostConstruct
    public void init(){
        webSocketService = this;
    }

    /**
     * 连接成功后调用的方法
     * @param session
     * @param key
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("key") String key){
        //key作为前端传给后端的token
        this.session = session;
        //从token中获取到的userId当作区分websocket客户端的key
        String userIdStr = JwtHelper.verifyTokenAndGetUserId(key);
        this.userIdStr = userIdStr;
        sessionPool.put(userIdStr,session);
        if (sessionPool.get(userIdStr) == null){
            webSockets.add(this);
        }
        webSockets.add(this);
        log.info("websocket连接成功");
        log.info("websocket有新的连接,连接总数为"+webSockets.size());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(){
        sessionPool.remove(userIdStr);
        webSockets.remove(this);
        log.info("websocket连接关闭");
    }

    /**
     * 收到客户端消息后调用的方法,根据业务要求进行处理,这里就简单地将收到的消息直接群发推送出去
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message){
        String key = Constant.BID_SUCCESS+userIdStr;
        if (StrUtil.isNotBlank(message)){
            if(message.equals("heartbeat")){
                sendTextMessage(key,"已连接");
            }else{
                RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
                //根据业务条件判断发送消息
                String result = String.valueOf(redisUtil.get(key));
                if (StrUtil.isNotBlank(result) && result.equals(Constant.SUCCESS)){
                    sendAllMessage("ok");
                    //发送之后删除key
                    redisUtil.delete(key);
                }
            }
        }
        log.info("WebSocket收到客户端消息:"+message);
    }

    /**
     * 实现服务器主动推送消息
     * @param key
     * @param message
     */
    private void sendTextMessage(String key, String message) {
        Session session = sessionPool.get(key);
        if (session != null){
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public void sendAllMessage(String message){
        log.info("websocket消息全部人员消息:"+message);
        for (WebSocketService apiWebSocketService:webSockets){
            try{
                if (apiWebSocketService.session.isOpen()){
                    apiWebSocketService.session.getAsyncRemote().sendText(message);
                }
            }catch (Exception e){
                e.printStackTrace();
                log.error("全部人员发送消息失败:",e.getMessage());
            }
        }
    }
    /**
     * 发生错误时的回调函数
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session,Throwable error){
        log.error("发生错误");
        error.printStackTrace();
    }

}

经测试,成功

如果是线上服务器连接,则需要在nginx里配置websocket相关内容,再重启nginx,代码如下

server {
        listen       443 ssl;
        server_name  api.kadecard.com;
        ssl_certificate      cert.pem;
        ssl_certificate_key  cert.key; 
         ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;
 
       # ssl_ciphers  HIGH:!aNULL:!MD5;
       # ssl_prefer_server_ciphers  on;
    
        ssl_ciphers  ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers  on;
        
        location / {
           proxy_pass http://xx:7878/;
     
        }    
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    
    #websocket 这里的websocket是后端websocket接口的接口名称 
      location /websocket {
            rewrite ^/ws$ / break;
            rewrite ^/ws(.*)$ $1 break;
            proxy_pass http://127.0.0.1:7878;
            proxy_http_version 1.1;    #websoket必须要使用的协议,http 1.1
            proxy_set_header Upgrade $http_upgrade;  #要使用websocket协议时,响应http升级请求
            proxy_set_header Connection "upgrade";
            proxy_set_header X-real-ip $remote_addr;
             proxy_set_header X-Forwarded-For $remote_addr;
        proxy_read_timeout 600s;
      }
    }

本地连接的时候用的是ws://,因为是http链接,但是如果是服务器连接,且支持https连接方式,则就需要用wss://的连接方式。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java后端使用websocket实现与客户端之间接收及发送消息 的相关文章

  • 抽象超类的默认接口方法

    可以说我有以下结构 abstract class A abstract boolean foo interface B default boolean foo return doBlah class C extends A implemen
  • 按下按钮时清除编辑文本焦点并隐藏键盘

    我正在制作一个带有编辑文本和按钮的应用程序 当我在 edittext 中输入内容然后单击按钮时 我希望键盘和焦点在 edittext 上消失 但我似乎无法做到这一点 我在 XML 中插入了这两行代码 android focusable tr
  • Spring Data:限制自定义查询的结果

    在我的 Spring 数据存储库中 我 必须 使用自定义查询 Query注解 我知道我可以限制这样的命名查询中的结果数量 Iterable
  • JPA:如何将字符串持久保存到数据库字段中,输入 MYSQL Text

    需求是用户可以写文章 所以我选择typeText为了contentmysql数据库内的字段 我怎样才能转换Java String into MySQL Text 干得好Jim Tough Entity public class Articl
  • 根据哈希值确认文件内容

    我需要 检查完整性 content文件数量 文件将写入 CD DVD 可能会被复制多次 这个想法是识别正确复制的副本 在从 Nero 等中删除它们之后 我对此很陌生 但快速搜索表明Arrays hashCode byte http down
  • ASM之前看一下maxStack指令吗?

    我正在尝试使用 ASM 库将字节代码转换为不同的格式 这可以使用 MethodVisitor 来完成 就像这个简单的测试代码一样 return new MethodVisitor ASM7 Override public void visi
  • Spring 术语中命令、表单、业务和实体对象之间的区别?

    我试图理解这些对象在松散耦合系统方面的差异 业务对象与实体对象相同吗 我可以使用 MVC 中的业务或实体对象作为我的命令对象吗 命令对象与表单对象相同吗 只是寻找 Spring 术语和用法中对象类型的说明 我在 stackoverflow
  • 将 EditText 聚焦在设备上运行的 PopupWindow 中时出现异常

    我正在为 Android 开发一个弹出窗口 它正在工作 我在上面添加了一个 EditText 和一个按钮 当在 ADV 上运行时 它可以正常工作 而在设备上运行时 当我专注于 EditText 时 这会抛出一个奇怪的异常 android v
  • 在 JavaFX 中拖动未装饰的舞台

    我希望将舞台设置为 未装饰 使其可拖动且可最小化 问题是我找不到这样做的方法 因为我遇到的示例是通过插入到主方法中的方法来实现的 我想通过控制器类中声明的方法来完成此操作 就像我如何使用下面的 WindowClose 方法来完成此操作 这是
  • java.lang.IllegalArgumentException:addChild:子名称“/”不唯一

    java lang IllegalArgumentException addChild 子名称 不唯一 通过在 tomcat webapps 文件夹中启用和禁用 saml 单点登录来替换现有 war 文件时遇到此问题 我正在使用 apach
  • activemq 的优先级

    我们目前正在使用 JMS 和 activemq 5 5 1 开发一个应用程序 我们想为某些消息定义更高的优先级 这将使它们首先被消耗 设置生产者和消费者后 通过spring 3 1 JMSTemplate 优先级并不能完全发挥作用 事实上
  • Akka 和 spring 配置

    我正在尝试将 akka 与 spring 结合起来 但没有成功 基本上 我的应用程序似乎不习惯读取 akka 模式 具有架构的 service context xml 的一部分
  • 设置 JAVA_HOME 变量时出现问题

    所以我刚刚下载了 Android Studio 并尝试设置 JAVA HOME 变量以便我可以运行它 我使用的是 Windows 8 并按照我找到的所有说明进行操作 但无济于事 转到高级系统设置 gt 环境变量 然后使用包含我的 jre7
  • Apache Kafka 是否提供异步订阅回调 API?

    我的项目正在将 Apache Kafka 视为老化的基于 JMS 的消息传递方法的潜在替代品 为了让这个过渡尽可能的顺利 如果替代的排队系统 Kafka 有一个异步订阅机制那就更理想了 类似于我们当前项目使用的JMS机制MessageLis
  • 如何修改生成的SOAP请求?

    我正处于创建输出拦截器并从 SOAP 消息中获取 OuputStream 的阶段 但是 如何在将 SOAP 信封发送到端点之前对其进行修改呢 我想删除一些 xml 元素 一种方法是获取文档并通过 XSLT 转换运行它 您可以通过调用来获取拦
  • spring data jpa 过滤 @OneToMany 中的子项

    我有一个员工测试实体是父实体并且FunGroup信息子实体 这两个实体都是通过employeeId映射 我需要一种方法来过滤掉与搜索条件匹配的子实体 以便结果仅包含父实体和子实体 满足要求 员工测试类 Entity name Employe
  • WebCore::UserGestureIndicator::processingUserGesture 中的 EXC_BAD_ACCESS (SIGSEGV)

    我有一个使用 UIWebView 和 HTML5 websockets 构建的 iOS 应用程序 该应用程序经历了看似随机的崩溃 它发生在用户与其交互时以及在用户和应用程序之间没有发生交互的寿命测试期间 崩溃日志都有以下内容 Excepti
  • 如何隐藏或删除 Android HoneyComb 中的状态栏?

    如何隐藏或删除 Android HoneyComb 中的状态栏 每次运行应用程序时 我都会发现某些内容必须被状态栏覆盖 我尝试改变AndroidManifest xml 但没有任何改变 你不知道 它被认为是永久的屏幕装饰 就像电容式主页 菜
  • 将带有时区的 Joda-Time `DateTime` 转换为没有时区的 DateTime?

    Given a DateTime http www joda org joda time apidocs org joda time DateTime html例如2015 07 09T05 10 00 02 00 using 乔达时间 h
  • Java,如何管理线程读取socket(websocket)?

    我有一个 WebSocket 服务器 我的服务器创建一个新线程来处理新连接 该线程一直处于活动状态 直到 websocket 中断 我的问题 对于 1 000 000 个连接 我需要 1 000 000 个线程 我如何通过一个线程处理多个

随机推荐

  • STM32定时器-输入捕获

    定时器 输入捕获 输入捕获工作过程 一句话总结工作过程 通过检测TIMx CHx上的边沿信号 在边沿信号发生跳变 比如上升沿 下降沿 的时候 将当前定时器的值 TIMx CNT 存放到对应的捕获 比较寄存器 TIMxCCRx 里面 完成一次
  • 股票数据预处理

    数据导入 提示 注意是csv 还是xlsx文件 本文导入中证100指数 import pandas as pd data pd read excel data CSI100 xls dtype 股票代码 Stkcd str 注意设置代码格式
  • OSI和TCP/IP

    OSI和TCP IP是两种不同的计算机网络协议体系 OSI协议体系 即开放式系统互联通信参考模型 Open Systems Interconnection 是一种抽象的理论网络体系结构 由国际标准化组织 ISO 制定 这个体系结构包括七层
  • 使用msys2 mingw64编译gcc

    我们在msys2中使用pacman安装的GCC默认是不能显示中文帮助的 而Linux下是可以显示中文的 这是因为在编译msys2 Mingw在编译GCC时 是没有打开 enable nls开关的 为了让其可以显示中文 可以尝试自己编译GCC
  • 60-200-050-使用-命令-MySQL explain命令

    文章目录 1 explain使用方法 2 ID 3 select type 4 table 5 Type 5 0 null 5 1 system 5 2 const 5 3 eq ref 5 4 ref 5 5 range 5 6 inde
  • linux mysql cpu 查看工具_Linux常用系统分析工具-CPU

    TOP top命令可以实时动态地查看系统的整体运行情况 是一个综合了多方信息监测系统性能和运行信息的实用工具 TOP命令的可选参数和其对应的含义如下 c 显示完整的命令 d 屏幕刷新时间间隔 i 设置时间间隔 u 指定用户名 p 指定进程
  • LDA模型训练与得到文本主题、困惑度计算(含可运行案例)

    文章目录 模块一 训练LDA模型 模块二 困惑度计算 模块三 得到一段文本的主题 全部代码及案例 可直接运行 首先使用gensim库 pip install gensim 模块一 训练LDA模型 import gensim pip inst
  • 07-微信小程序-注册页面-模块化

    07 微信小程序 注册页面 文章目录 注册页面 使用 Page 构造器注册页面 参数Object 初始数据 案例代码 生命周期回调函数 组件事件处理函数 setData 案例代码 生命周期 模块化 注册页面 对于小程序中的每个页面 都需要在
  • [Python从零到壹] 三十五.图像处理基础篇之OpenCV绘制各类几何图形

    欢迎大家来到 Python从零到壹 在这里我将分享约200篇Python系列文章 带大家一起去学习和玩耍 看看Python这个有趣的世界 所有文章都将结合案例 代码和作者的经验讲解 真心想把自己近十年的编程经验分享给大家 希望对您有所帮助
  • HTML CSS 盒模型 +background 使用

    HTML 盒模型 box model 和background 部分内容来自菜鸟教程 所有HTML元素可以看作盒子 在CSS中 box model 这一术语是用来设计和布局时使用 CSS盒模型本质上是一个盒子 封装周围的HTML元素 它包括
  • 写一个函数,用户输入一个数判断是否是素数

    写一个函数 用户输入一个数判断是否是素数 function get num if num 1 num 0 return num 不是质数 for var i 2 i lt num i if num i 0 return num 不是质数 r
  • oracle生成标准uuid,Oracle 生成uuid方法

    近日 遇到朋友问及如何生成UUID 是 通用唯一识别码 Universally Unique Identifier 方法 其实数据中是支持的 Oracle中生成跨系统的唯一识别符UUID非常方便 比生成序列还简单 直接用sys guid 就
  • Stable Doodle:简单涂鸦一键变成艺术品

    摘要 Stable Doodle 是一款使用 AI 技术将简单涂鸦转化为艺术品的应用 它可以帮助用户快速 轻松地创作出精美的图画 本教程将介绍 Stable Doodle 的基本使用方法 正文 Stable Doodle 的使用非常简单 只
  • 2080ti,驱动安装,CUDA安装,CUDNN安装,CUDA10.1的不兼容问题

    发现问题 哎呦 我跑的是CPU 不是GPU 解决问题 Tensorflow目前不支持CUDA10 1 继续解决问题 哎呦 驱动被不小心删掉了 那就全部重新安装吧 最终解决好了 跑起来了 看似GPU 其实跑的是CPU 之前写了代码发现 能跑起
  • 分布式版本控制VS集中式版本控制

    集中式版本控制 诸如CVS SVN等 都有一个集中管理的服务器 保存所有的文件修订版本 而协同工作的人们都通过客户端连接到这台服务器 取出最新的文件或者提交更新 如上图所示 A B C为三位开发者 这是A将代码拉到本地进行开发 这个时候A开
  • raise_for_status()方法

    raise for status 方法 理解Response类非常重要 Response这样的一个对象返回了所有的网页内容 那么它也提供了一个方法 叫raise for status 这个方法是专门与异常打交道的方法 该方法有这样一个有趣的
  • 如何模拟弱网条件 - 限流, 丢包, 延迟和抖动

    Abstract 弱网条件的模拟 Authors Walter Fan Category learning note Status v1 0 Updated 2023 02 16 License CC BY NC ND 4 0 概述 在网络
  • 什么是车联网

    要弄清车联网是什么 还要从许多年前说起 比尔 盖茨等人曾提出物联网的概念 即 万物相连的互联网 物联网是要将各种信息传感设备与互联网结合起来从而形成一个的巨大的网络 简单来说 就是给各种各样的事物连上互联网 然后人们可以通过某个设备远程监控
  • 如何高效定位网络丢包问题?

    引言 本期分享一个比较常见的网络问题 丢包 例如我们去ping一个网站 如果能ping通 且网站返回信息全面 则说明与网站服务器的通信是畅通的 如果ping不通 或者网站返回的信息不全等 则很可能是数据被丢包了 类似情况想必大家都不陌生 针
  • java后端使用websocket实现与客户端之间接收及发送消息

    客户端请求websocket接口 连接通道 我这边业务成功客户端发消息 客户端自动刷新 接口 ws localhost 8080 websocket xx import org springframework web socket conf