springboot集成websocket

简介: springboot集成websocket

springboot集成websocket

1. 前言

这里我们使用springboot搭建一个轻量级的websocket服务,同时提供4个入参。使用websocket服务可以轻松和微信小程序、支付宝小程序、网页就行双向通讯,非常实用方便。

wss地址

这里是我们自己搭建的中转服务websocket地址。

wss://xxxx.cn/netgate/auth/pid/sn/openid

参数说明

image.png

2. 引入依赖

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

3. 配置文件

新增WebSocketConfig.java的配置文件。用来初始化websocket

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer{
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
       registry.addHandler(webSocketHandler(),"/netgate/{auth}/{pid}/{sn}/{openid}")                          //注册Handler
           .addInterceptors(new WebSocketHandshakeInterceptor())           //注册Interceptor
           .setAllowedOrigins("*");                                 //注册Interceptor
//     //2.注册SockJS,提供SockJS支持(主要是兼容ie8)
//     String sockjs_url = "/sockjs/socketServer.do";                          //设置sockjs的地址
//     registry.addHandler(netgateHandler, sockjs_url)                         //注册Handler
//         .addInterceptors(new WebSocketHandshakeInterceptor())               //注册Interceptor
//         .withSockJS();                                                      //支持sockjs协议
    }
    @Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        container.setMaxTextMessageBufferSize(2*1024*1024);//8192*1024 1024*1024*1024
        container.setMaxBinaryMessageBufferSize(2*1024*1024);
        container.setAsyncSendTimeout(55000l);
        container.setMaxSessionIdleTimeout(55000l);//心跳
        return container;
    }
    @Bean
    public TextWebSocketHandler webSocketHandler() {
        return new NetgateHandler();
    }
}

4. Websocket握手过滤器

过滤器的作用主要是用来做连接接入的鉴权,和参数解析。

新增WebSocketHandshakeInterceptor.java文件

/**
 * Describes: WebSocket握手拦截器
 * Auth: Eric
 */
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
    private final static Logger LOGGER = LoggerFactory.getLogger(WebSocketHandshakeInterceptor.class);
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> attributes) throws Exception {
        if (request instanceof ServletServerHttpRequest) {
          String path = request.getURI().getPath();
          if(requestIsValid(path)){
            String[] params = getParams(path);
            attributes.put("WEBSOCKET_AUTH", params[0]);
            attributes.put("WEBSOCKET_PID", params[1]);
            attributes.put("WEBSOCKET_SN", params[2]);
            attributes.put("WEBSOCKET_OPENID", params[3]);
            attributes.put("WEBSOCKET_FIRSTONE","yes");
          }
        }
        System.out.println("================Before Handshake================");
        return true;
    }
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
      System.out.println("================After Handshake================");
      if(e!=null) e.printStackTrace();
      System.out.println("================After Handshake================");
    }
    private boolean requestIsValid(String url){
        //在这里可以写上具体的鉴权逻辑
      boolean isvalid = false;
      if(StringUtils.isNotEmpty(url)
          && url.startsWith("/netgate/")
          && url.split("/").length==6){
        isvalid = true;
      }
      return isvalid;
    }
    private String[] getParams(String url){
      url = url.replace("/netgate/","");
      return url.split("/");
    }
}

5. Websocket处理器

在这里可以做消息的接收和发送。

这里MqttGateway是springboot整合MQTT客户端的服务类。具体可以参考下一篇的springboot集成mqtt

新建NetgateHandler.java文件

/**
 * Websocket处理器
 */
@Component
public class NetgateHandler extends TextWebSocketHandler {
  @Autowired
  private MqttGateway mqttGateway;
    /*
   * 网关连接集合
   * 第一级:设备序列号 sn
   * 第二级:用户唯一标识 openid
   */
  private static ConcurrentHashMap<String,ConcurrentHashMap<String,WebSocketSession>> netgates = new ConcurrentHashMap<String,ConcurrentHashMap<String,WebSocketSession>>();
    /**
     * 处理前端发送的文本信息
     * js调用websocket.send时候,会调用该方法
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
      if(!session.isOpen()) {
      System.out.println("连接已关闭,不再处理该连接的消息!");
      return;
    }
      String mes = ObjectUtils.toString(message.getPayload(),"");
      String pid = session.getAttributes().get("WEBSOCKET_PID").toString();
      String sn = session.getAttributes().get("WEBSOCKET_SN").toString();
    if(message==null || "".equals(mes)){
      System.out.println(getSysDate()+"============接收到空消息,不予处理。");
    }else if(mes.length()==1){
      //心跳消息过滤掉
      return;
    }else {
      //转发成mqtt消息
      String topic = "pay/"+pid+"/server/"+sn;
      System.out.println(topic);
      System.out.println(getSysDate()+"============消息处理完成:"+mes);
      mqttGateway.sendToMqtt(topic,mes);
    }
    }
    /**
     * 当新连接建立的时候,被调用
     * 连接成功时候,会触发页面上onOpen方法
     *
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
      System.out.println(getSysDate()+"============正在初始化连接:"+session.getId());
        try {
            //初始化连接,把session存储起来
      this.initUsers(session);
    } catch (Exception e) {
      System.out.println(getSysDate()+"============初始化连接异常-开始:"+e.getMessage());
      e.printStackTrace();
      System.out.println(getSysDate()+"============初始化连接异常-结束:"+e.getMessage());
    }
        System.out.println(getSysDate()+"============初始化连接完成:"+session.getId());
    }
    /**
     * 当连接关闭时被调用
     *
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
      System.out.println(getSysDate()+"============正在关闭连接:"+session.getId()+",isOpen:"+session.isOpen()+";code:"+status.getCode());
      try {
        System.out.println("断开连接状态值"+status.getCode());
      this.removeSession(session);
    } catch (Exception e) {
      System.out.println(getSysDate()+"============关闭连接异常-开始:"+e.getMessage());
      e.printStackTrace();
      System.out.println(getSysDate()+"============关闭连接异常-结束:"+e.getMessage());
    }
      System.out.println(getSysDate()+"============正在关闭完成:"+session.getId()+",isOpen:"+session.isOpen()+";code:"+status.getCode());
    }
    /**
     * 传输错误时调用
     *
     * @param session
     * @param exception
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
      System.out.println(getSysDate()+"============发生传输错误:"+session.getId()+";session.isOpen():"+session.isOpen()+";exception:"+exception.getMessage());
      exception.printStackTrace();
      if (session.isOpen()) {
          //try { session.close(); } catch (Exception e) {e.printStackTrace();}
        }else {
          try {
          this.removeSession(session);
        } catch (Exception e) {
          System.out.println(getSysDate()+"============传输错误处理异常-开始:"+e.getMessage());
          e.printStackTrace();
          System.out.println(getSysDate()+"============传输错误处理异常-结束:"+e.getMessage());
        }
        }
      System.out.println(getSysDate()+"============错误处理结束:"+session.getId()+";session.isOpen():"+session.isOpen()+";exception:"+exception.getMessage());
    }
  public synchronized void sendMsgToNetgateSn(String sn, String msg)  {
    if(netgates.size()>0 && netgates.containsKey(sn) && !netgates.get(sn).isEmpty()){
      //获取EID对应的后台管理连接 多个
      for (WebSocketSession ws: netgates.get(sn).values()){
        System.out.println("对网关指令开始发送啦:sn="+sn+"消息内容"+msg);
        try {ws.sendMessage(new TextMessage(msg));} catch (IOException e) {System.out.println(getSysDate()+"发生了异常:"+e.getMessage());e.printStackTrace();continue;}
      }
    }
  }
  //连接接入的处理方法
  private synchronized void initUsers(WebSocketSession session){
    String pid = (String) session.getAttributes().get("WEBSOCKET_PID");
    String sn = (String) session.getAttributes().get("WEBSOCKET_SN");
    String openid = (String) session.getAttributes().get("WEBSOCKET_OPENID");
    if(StringUtils.isNotEmpty(pid) && StringUtils.isNotEmpty(sn) && StringUtils.isNotEmpty(openid)){
      ConcurrentHashMap<String,WebSocketSession> netgate = netgates.get(sn);
      if(netgate == null){
        netgate = new ConcurrentHashMap<String,WebSocketSession>();
      }
      WebSocketSession session_exist = netgate.get(sn);
      if(session_exist!=null) {
        System.out.println("检测到相同SN重复连接,SN:"+sn+",连接ID:"+session_exist.getId()+",准备清理失效的连接。。。");
        try {session_exist.close();} catch (IOException e) {e.printStackTrace();}
      }
      netgate.putIfAbsent(openid, session);
      netgates.put(sn,netgate);
    }
  }
  //连接被关闭时处理集合
  private synchronized void removeSession(WebSocketSession session){
    String sn = (String) session.getAttributes().get("WEBSOCKET_SN");
    String openid = (String) session.getAttributes().get("WEBSOCKET_OPENID");
    if(netgates.get(sn).containsKey(openid)) {
      WebSocketSession exist_session = netgates.get(sn).get(openid);
      //确保是同一个session 不是同一个session则不应该进行下一步的处理
      if(exist_session.getId()!=null && exist_session.getId().equals(session.getId())) {
        netgates.get(sn).remove(openid);
        System.out.println("有一网关连接关闭!SN:"+sn+",当前在线数量为"+netgates.get(sn).keySet().size());
      }else {
        System.out.println("检测到关闭session异常,程序中断处理,关闭sessionId:"+session.getId()+",当前实际sessionId:"+exist_session.getId());
      }
    }else {
      System.out.println("检测到关闭session异常,程序中断处理,系统中未找到对应的session,Sn="+sn+"openid="+openid);
    }
  }
  private String getSysDate() {
     SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
        return df.format(new Date());
  }
}
相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
18天前
|
开发框架 前端开发 网络协议
Spring Boot结合Netty和WebSocket,实现后台向前端实时推送信息
【10月更文挑战第18天】 在现代互联网应用中,实时通信变得越来越重要。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为客户端和服务器之间的实时数据传输提供了一种高效的解决方案。Netty作为一个高性能、事件驱动的NIO框架,它基于Java NIO实现了异步和事件驱动的网络应用程序。Spring Boot是一个基于Spring框架的微服务开发框架,它提供了许多开箱即用的功能和简化配置的机制。本文将详细介绍如何使用Spring Boot集成Netty和WebSocket,实现后台向前端推送信息的功能。
204 1
|
1月前
|
前端开发 JavaScript UED
探索Python Django中的WebSocket集成:为前后端分离应用添加实时通信功能
通过在Django项目中集成Channels和WebSocket,我们能够为前后端分离的应用添加实时通信功能,实现诸如在线聊天、实时数据更新等交互式场景。这不仅增强了应用的功能性,也提升了用户体验。随着实时Web应用的日益普及,掌握Django Channels和WebSocket的集成将为开发者开启新的可能性,推动Web应用的发展迈向更高层次的实时性和交互性。
70 1
|
1月前
|
Java Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
|
3天前
|
XML Java 数据库连接
SpringBoot集成Flowable:打造强大的工作流管理系统
在企业级应用开发中,工作流管理是一个核心组件,它能够帮助我们定义、执行和管理业务流程。Flowable是一个开源的工作流和业务流程管理(BPM)平台,它提供了强大的工作流引擎和建模工具。结合SpringBoot,我们可以快速构建一个高效、灵活的工作流管理系统。本文将探讨如何将Flowable集成到SpringBoot应用中,并展示其强大的功能。
19 1
|
13天前
|
JSON Java API
springboot集成ElasticSearch使用completion实现补全功能
springboot集成ElasticSearch使用completion实现补全功能
19 1
|
23天前
|
前端开发 Java C++
RSocket vs WebSocket:Spring Boot 3.3 中的两大实时通信利器
本文介绍了在 Spring Boot 3.3 中使用 RSocket 和 WebSocket 实现实时通信的方法。RSocket 是一种高效的网络通信协议,支持多种通信模式,适用于微服务和流式数据传输。WebSocket 则是一种标准协议,支持全双工通信,适合实时数据更新场景。文章通过一个完整的示例,展示了如何配置项目、实现前后端交互和消息传递,并提供了详细的代码示例。通过这些技术,可以大幅提升系统的响应速度和处理效率。
|
3天前
|
XML 存储 Java
SpringBoot集成Flowable:构建强大的工作流引擎
在企业级应用开发中,工作流管理是核心功能之一。Flowable是一个开源的工作流引擎,它提供了BPMN 2.0规范的实现,并且与SpringBoot框架完美集成。本文将探讨如何使用SpringBoot和Flowable构建一个强大的工作流引擎,并分享一些实践技巧。
15 0
|
28天前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
49 1
|
1月前
|
存储 前端开发 Java
Spring Boot 集成 MinIO 与 KKFile 实现文件预览功能
本文详细介绍如何在Spring Boot项目中集成MinIO对象存储系统与KKFileView文件预览工具,实现文件上传及在线预览功能。首先搭建MinIO服务器,并在Spring Boot中配置MinIO SDK进行文件管理;接着通过KKFileView提供文件预览服务,最终实现文档管理系统的高效文件处理能力。
241 11
|
2月前
|
XML Java 关系型数据库
springboot 集成 mybatis-plus 代码生成器
本文介绍了如何在Spring Boot项目中集成MyBatis-Plus代码生成器,包括导入相关依赖坐标、配置快速代码生成器以及自定义代码生成器模板的步骤和代码示例,旨在提高开发效率,快速生成Entity、Mapper、Mapper XML、Service、Controller等代码。
springboot 集成 mybatis-plus 代码生成器
下一篇
无影云桌面