SpringBoot集成websocket作为客户端和服务端的代码案例

2024-07-11 1210阅读

websocket介绍:

WebSocket是一种在单个TCP连接上进行全双工通信的协议,允许服务端和客户端之间进行实时、双向的数据传输。

  • 长连接:WebSocket建立连接后,只要不关闭,会一直保持连接状态,使得服务器可以主动向客户端推送数据。
  • 双向通信:与HTTP请求-响应模式不同,WebSocket支持双向通信,即客户端和服务端都可以发送或接收数据。

    使用场景:

    在需要实时交互的场景中使用,例如聊天应用,实时数据推送,内容流式输出等。可实现实时向客户端进行数据推送。

    1、SpringBoot集成websocket作为服务端

    在前后端分离的项目中,前端作为websocket的客户端,后端服务作为websocket的服务端。

    实现步骤:

    1. 添加websocket整合包

          org.springframework.boot
          spring-boot-starter-websocket
      
      
    2. 编写websocket配置类,暴露WebSocket

      @Configuration
      public class WebSocketConfig {
          @Bean
          public ServerEndpointExporter serverEndpointExporter() {
              return new ServerEndpointExporter();
          }
      }
      
    3. 编写websocket服务监听程序及处理逻辑

      package com.houdehong.wsserver.controller;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.stereotype.Component;
      import javax.websocket.*;
      import javax.websocket.server.PathParam;
      import javax.websocket.server.ServerEndpoint;
      import java.io.IOException;
      import java.util.concurrent.ConcurrentHashMap;
      /**
       * @Author houdehong
       * @Date 2024/3/21 14:16
       * @Description
       **/
      @Component
      @ServerEndpoint("/api/ws/{sid}")
      @Slf4j
      public class WebSocketServer {
          private String sid;
          private static final ConcurrentHashMap SESSION_MAP = new ConcurrentHashMap();
          /**
           * 连接成功
           */
          @OnOpen
          public void onOpen(Session session, @PathParam("sid") String sid) {
              this.sid = sid;
              SESSION_MAP.put(sid, session);
              log.info("有新连接:sid:{},sessionId:{},当前连接数:{}", sid, session.getId(), SESSION_MAP.size());
          }
          /**
           * 连接关闭
           */
          @OnClose
          public void onClose(Session session) {
              SESSION_MAP.remove(this.sid);
              log.info("连接关闭,sid:{},session id:{}!当前连接数:{}", this.sid, session.getId(), SESSION_MAP.size());
          }
          /**
           * 收到消息
           */
          @OnMessage
          public void onMessage(String message, Session session) {
              log.info("收到消息:{},内容:{}", sid, message);
              if("ping".equals(message)){
                  try {
                      session.getBasicRemote().sendText("pong");
                  } catch (IOException e) {
                      log.error("onMessage 推送消息失败:{},内容:{}", sid, message);
                  }
              }else{
                  // 排除自己
                 // sendMeasureDataInfoExcludeSelf(message, sid);
                  // 发给所有客户端包括自己
                  sendMeasureDataInfo(message);
              }
          }
          /**
           * 连接错误
           */
          @OnError
          public void onError(Session session, Throwable error) {
              log.error("{} 发生错误", session.getId(), error);
          }
          /**
           * 群发消息
           */
          public void sendMeasureDataInfo(String message) {
              for (String sid : SESSION_MAP.keySet()) {
                  Session session = SESSION_MAP.get(sid);
                  try {
                      session.getBasicRemote().sendText(message);
                  } catch (IOException e) {
                      log.error("推送消息失败:{},内容:{}", sid, message);
                  }
                  log.info("推送消息:{},内容:{}", sid, message);
              }
          }
          /**
           * 群发消息,排除消息发起者
           * @param message
           * @param sidSelf
           */
          private void sendMeasureDataInfoExcludeSelf(String message, String sidSelf){
              for(String sid : SESSION_MAP.keySet()){
                  if(sidSelf.equals(sid)){
                      continue;
                  }
                  Session session = SESSION_MAP.get(sid);
                  try {
                      session.getBasicRemote().sendText(message);
                  } catch (IOException e) {
                      log.error("sendMeasureDataInfoExcludeSelf 推送消息失败:{},内容:{}", sid, message);
                  }
                  log.info("sendMeasureDataInfoExcludeSelf 推送消息:{},内容:{}", sid, message);
              }
          }
      }
      
    4. 测试

      至此,springboot整合websocket作为服务就搭建完成了,我们可以随便百度打开一个在线的websocket测试工具,请求一下试试,请求地址是ws://ip:port/api/ws/{sid}, 其中sid可以随便指定一个字符串,这里的sid主要是用来区分客户端,实际场景下可以在前端生成全局唯一的标识。

      图片:

      SpringBoot集成websocket作为客户端和服务端的代码案例控制台打印信息:

      SpringBoot集成websocket作为客户端和服务端的代码案例

    2、SpringBoot集成websocket作为客户端

    有时,我们需要调用第三方的websocket服务,然后将接收到的数据处理之后,或持久化到数据库,或是需要解析数据重新封装为前端需要的数据结构,这个时候我们就需要作为客户端来进行使用。

    实现步骤:

    1. 添加springboot服务作为客户端的依赖

          org.java-websocket
          Java-WebSocket
          1.3.8
      
      
    2. 继承WebSocketClient,重写方法,加入自己的逻辑

      @Slf4j
      public class MyWebSocketClient extends WebSocketClient {
          public MyWebSocketClient(URI serverUri) {
              super(serverUri);
          }
          @Override
          public void onOpen(ServerHandshake arg0) {
              log.info("------ WebSocketClient onOpen ------");
          }
          @Override
          public void onClose(int arg0, String arg1, boolean arg2) {
              log.info("------ WebSocket onClose ------{}",arg1);
          }
          @Override
          public void onError(Exception arg0) {
              log.error("------ WebSocket onError ------{}",arg0);
          }
          @Override
          public void onMessage(String response) {
              log.info("-------- 接收到服务端数据: " + response + "--------");
          }
      }
      
    3. 获取websocketClient实例

      在这里我写的wsServerUrl即为上面websocket作为服务端的地址

      @Component
      public class WebSocketClientConfigurer {
          private final String wsServerUrl = "ws://127.0.0.1:8081/api/ws/123wer";
          @Bean
          public WebSocketClient webSocketClient() {
              try {
                  MyWebSocketClient webSocketClient =
                          new MyWebSocketClient(new URI(wsServerUrl));
                  webSocketClient.connect();
                  return webSocketClient;
              } catch (URISyntaxException e) {
                  e.printStackTrace();
              }
              return null;
          }
      }
      
    4. 为了好演示我加了一个controller来进行调用

      @RestController
      @RequestMapping("ws-client")
      public class WebsocketClient {
          @Resource
          private MyWebSocketClient webSocketClient;
          @GetMapping("send/{message}")
          public void sendRequest(@PathVariable String message){
              webSocketClient.send(message);
          }
      }
      

    3. 测试

    至此,我们已经搭好了一个websocket的服务器和一个websocket的客户端,我们可以同时把这两个服务跑起来,做一个调用。

    注意:需要先启动服务端,再启动客户端,客户端会无法创建连接,报空指针异常。

    服务端启动:

    SpringBoot集成websocket作为客户端和服务端的代码案例客户端启动:

    SpringBoot集成websocket作为客户端和服务端的代码案例现在我们向客户端的controller请求一个消息:

    你可以用postman或者其他api工具,我现在图简单直接在浏览器发起:

    SpringBoot集成websocket作为客户端和服务端的代码案例客户端控制台日志:

    SpringBoot集成websocket作为客户端和服务端的代码案例服务端控制台日志:

    SpringBoot集成websocket作为客户端和服务端的代码案例OK,大功告成。

    整套源码包地址

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]