[JAVA] java后端+前端使用WebSocket实现消息推送的详细流程

2328 0
Honkers 2022-11-9 09:24:26 | 显示全部楼层 |阅读模式
目录

    前言创建WebSocket的简单实例操作流程
      1.引入Websocket依赖2.创建配置类WebSocketConfig3.创建WebSocketServer4.websocket调用
    总结



前言

在项目的开发时,遇到实现服务器主动发送数据到前端页面的功能的需求。实现该功能不外乎使用轮询和websocket技术,但在考虑到实时性和资源损耗后,最后决定使用websocket。现在就记录一下用Java实现Websocket技术吧~
Java实现Websocket通常有两种方式:1、创建WebSocketServer类,里面包含open、close、message、error等方法;2、利用Springboot提供的webSocketHandler类,创建其子类并重写方法。我们项目虽然使用Springboot框架,不过仍采用了第一种方法实现。

创建WebSocket的简单实例操作流程


1.引入Websocket依赖
  1. <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket -->
  2.         <dependency>
  3.             <groupId>org.sringframework.boot</groupId>
  4.             <artifactId>spring-boot-starter-websocket</artifactId>
  5.             <version>2.7.0</version>
  6.         </dependency>
复制代码
2.创建配置类WebSocketConfig
  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.socket.server.standard.ServerEndpointExporter;
  4. /**
  5. * 开启WebSocket支持
  6. */
  7. @Configuration
  8. public class WebSocketConfig {
  9.     @Bean
  10.     public ServerEndpointExporter serverEndpointExporter() {
  11.         return new ServerEndpointExporter();
  12.     }
  13. }
复制代码
3.创建WebSocketServer

在websocket协议下,后端服务器相当于ws里面的客户端,需要用@ServerEndpoint指定访问路径,并使用@Component注入容器
@ServerEndpoint:当ServerEndpointExporter类通过Spring配置进行声明并被使用,它将会去扫描带有@ServerEndpoint注解的类。被注解的类将被注册成为一个WebSocket端点。所有的配置项都在这个注解的属性中
( 如:@ServerEndpoint(“/ws”) )
下面的栗子中@ServerEndpoint指定访问路径中包含sid,这个是用于区分每个页面
  1. import com.alibaba.fastjson.JSONObject;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.util.StringUtils;
  5. import javax.websocket.*;
  6. import javax.websocket.server.PathParam;
  7. import javax.websocket.server.ServerEndpoint;
  8. import java.io.IOException;
  9. import java.net.Socket;
  10. import java.util.*;
  11. import java.util.concurrent.ConcurrentHashMap;
  12. /**
  13. * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
  14. * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
  15. */
  16. @ServerEndpoint("/notice/{userId}")
  17. @Component
  18. @Slf4j
  19. public class NoticeWebsocket {
  20.     //记录连接的客户端
  21.     public static Map<String, Session> clients = new ConcurrentHashMap<>();
  22.     /**
  23.      * userId关联sid(解决同一用户id,在多个web端连接的问题)
  24.      */
  25.     public static Map<String, Set<String>> conns = new ConcurrentHashMap<>();
  26.     private String sid = null;
  27.     private String userId;
  28.     /**
  29.      * 连接成功后调用的方法
  30.      * @param session
  31.      * @param userId
  32.      */
  33.     @OnOpen
  34.     public void onOpen(Session session, @PathParam("userId") String userId) {
  35.         this.sid = UUID.randomUUID().toString();
  36.         this.userId = userId;
  37.         clients.put(this.sid, session);
  38.         Set<String> clientSet = conns.get(userId);
  39.         if (clientSet==null){
  40.             clientSet = new HashSet<>();
  41.             conns.put(userId,clientSet);
  42.         }
  43.         clientSet.add(this.sid);
  44.         log.info(this.sid + "连接开启!");
  45.     }
  46.     /**
  47.      * 连接关闭调用的方法
  48.      */
  49.     @OnClose
  50.     public void onClose() {
  51.         log.info(this.sid + "连接断开!");
  52.         clients.remove(this.sid);
  53.     }
  54.     /**
  55.      * 判断是否连接的方法
  56.      * @return
  57.      */
  58.     public static boolean isServerClose() {
  59.         if (NoticeWebsocket.clients.values().size() == 0) {
  60.             log.info("已断开");
  61.             return true;
  62.         }else {
  63.             log.info("已连接");
  64.             return false;
  65.         }
  66.     }
  67.     /**
  68.      * 发送给所有用户
  69.      * @param noticeType
  70.      */
  71.     public static void sendMessage(String noticeType){
  72.         NoticeWebsocketResp noticeWebsocketResp = new NoticeWebsocketResp();
  73.         noticeWebsocketResp.setNoticeType(noticeType);
  74.         sendMessage(noticeWebsocketResp);
  75.     }
  76.     /**
  77.      * 发送给所有用户
  78.      * @param noticeWebsocketResp
  79.      */
  80.     public static void sendMessage(NoticeWebsocketResp noticeWebsocketResp){
  81.         String message = JSONObject.toJSONString(noticeWebsocketResp);
  82.         for (Session session1 : NoticeWebsocket.clients.values()) {
  83.             try {
  84.                 session1.getBasicRemote().sendText(message);
  85.             } catch (IOException e) {
  86.                 e.printStackTrace();
  87.             }
  88.         }
  89.     }
  90.     /**
  91.      * 根据用户id发送给某一个用户
  92.      * **/
  93.     public static void sendMessageByUserId(String userId, NoticeWebsocketResp noticeWebsocketResp) {
  94.         if (!StringUtils.isEmpty(userId)) {
  95.             String message = JSONObject.toJSONString(noticeWebsocketResp);
  96.             Set<String> clientSet = conns.get(userId);
  97.             if (clientSet != null) {
  98.                 Iterator<String> iterator = clientSet.iterator();
  99.                 while (iterator.hasNext()) {
  100.                     String sid = iterator.next();
  101.                     Session session = clients.get(sid);
  102.                     if (session != null) {
  103.                         try {
  104.                             session.getBasicRemote().sendText(message);
  105.                         } catch (IOException e) {
  106.                             e.printStackTrace();
  107.                         }
  108.                     }
  109.                 }
  110.             }
  111.         }
  112.     }
  113.     /**
  114.      * 收到客户端消息后调用的方法
  115.      * @param message
  116.      * @param session
  117.      */
  118.     @OnMessage
  119.     public void onMessage(String message, Session session) {
  120.         log.info("收到来自窗口"+this.userId+"的信息:"+message);
  121.     }
  122.     /**
  123.      * 发生错误时的回调函数
  124.      * @param error
  125.      */
  126.     @OnError
  127.     public void onError(Throwable error) {
  128.         log.info("错误");
  129.         error.printStackTrace();
  130.     }
  131. }
复制代码
封装了一个发送消息的对象可以直接使用
  1. import io.swagger.annotations.ApiModel;
  2. import io.swagger.annotations.ApiModelProperty;
  3. import lombok.Data;
  4. @Data
  5. @ApiModel("ws通知返回对象")
  6. public class NoticeWebsocketResp<T> {
  7.     @ApiModelProperty(value = "通知类型")
  8.     private String noticeType;
  9.     @ApiModelProperty(value = "通知内容")
  10.     private T noticeInfo;
  11. }
复制代码
4.websocket调用

一个用户调用接口,主动将信息发给后端,后端接收后再主动推送给指定/全部用户
  1. @RestController
  2. @RequestMapping("/order")
  3. public class OrderController {
  4.         @GetMapping("/test")
  5.     public R test() {
  6.             NoticeWebsocket.sendMessage("你好,WebSocket");
  7.         return R.ok();
  8.     }
  9. }
复制代码
前端WebSocket连接
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>SseEmitter</title>
  6. </head>
  7. <body>
  8. <div id="message"></div>
  9. </body>
  10. <script>
  11. var limitConnect = 0;
  12. init();
  13. function init() {
  14. var ws = new WebSocket('ws://192.168.2.88:9060/notice/1');
  15. // 获取连接状态
  16. console.log('ws连接状态:' + ws.readyState);
  17. //监听是否连接成功
  18. ws.onopen = function () {
  19.     console.log('ws连接状态:' + ws.readyState);
  20.     limitConnect = 0;
  21.     //连接成功则发送一个数据
  22.     ws.send('我们建立连接啦');
  23. }
  24. // 接听服务器发回的信息并处理展示
  25. ws.onmessage = function (data) {
  26.     console.log('接收到来自服务器的消息:');
  27.     console.log(data);
  28.     //完成通信后关闭WebSocket连接
  29.     // ws.close();
  30. }
  31. // 监听连接关闭事件
  32. ws.onclose = function () {
  33.     // 监听整个过程中websocket的状态
  34.     console.log('ws连接状态:' + ws.readyState);
  35. reconnect();
  36. }
  37. // 监听并处理error事件
  38. ws.onerror = function (error) {
  39.     console.log(error);
  40. }
  41. }
  42. function reconnect() {
  43.     limitConnect ++;
  44.     console.log("重连第" + limitConnect + "次");
  45.     setTimeout(function(){
  46.         init();
  47.     },2000);
  48. }
  49. </script>
  50. </html>
复制代码
项目启动,打开页面后控制台打印连接信息


调用order/test方法后前端打印推送消息内容


这样,就可以接口或者ws调用网址的方式进行websocket的通信啦~
如果没有前端页面也可以使用在线WebSocket测试



总结

到此这篇关于java后端+前端使用WebSocket实现消息推送的文章就介绍到这了,更多相关javaWebSocket实现消息推送内容请搜索中国红客联盟以前的文章或继续浏览下面的相关文章希望大家以后多多支持中国红客联盟!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4004
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行