• springboot整合websocket


    一、引入maven依赖

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

    二、websocket配置类

    @Configuration
    public class WebSocketConfig {
    
    
        /**
         * ServerEndpointExporter 作用
         *
         * 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
         *
         * @return
         */
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }

    三、websocket业务代码

     1 @Slf4j
     2 @Component
     3 @ServerEndpoint("/websocket/{userId}")
     4 public class WebSocket {
     5 
     6     /**
     7      *  与某个客户端的连接对话,需要通过它来给客户端发送消息
     8      */
     9     private Session session;
    10 
    11     /**
    12      * 标识当前连接客户端的用户id
    13      */
    14     private String userId;
    15 
    16     /**
    17      *  用于存所有的连接服务的客户端,这个对象存储是安全的
    18      */
    19     private static ConcurrentHashMap<String,WebSocket> webSocketSet = new ConcurrentHashMap<>();
    20 
    21 
    22     /**
    23      * 开启连接
    24      * @param session
    25      * @param userId
    26      */
    27     @OnOpen
    28     public void onOpen(Session session, @PathParam(value = "userId") String userId){
    29         this.session = session;
    30         this.userId = userId;
    31         // userId是用来表示唯一客户端,如果需要指定发送,需要指定发送通过userId来区分
    32         webSocketSet.put(userId,this);
    33         log.info("[WebSocket] 连接成功,当前连接人数为:={}",webSocketSet.size());
    34     }
    35 
    36     /**
    37      * 退出连接
    38      */
    39     @OnClose
    40     public void onClose(){
    41         if (webSocketSet.containsKey(this.userId)){
    42             webSocketSet.remove(this.userId);
    43             log.info("[WebSocket] 退出成功,当前连接人数为:={}",webSocketSet.size());
    44         }
    45     }
    46 
    47     /**
    48      * 收到客户端消息后调用的方法
    49      * @param message
    50      */
    51     @OnMessage
    52     public void onMessage(String message, Session session) throws IOException {
    53        log.info("收到消息:{}-{}",session,message);
    54        session.getBasicRemote().sendText("收到了消息:"+message);
    55     }
    56 
    57 
    58     /**
    59      * 发送自定义消息
    60      * */
    61     public static void sendInfo(String message,@PathParam("userId") String userId) {
    62         if(StringUtils.isNotBlank(userId) && webSocketSet.containsKey(userId)){
    63             try {
    64                 webSocketSet.get(userId).session.getBasicRemote().sendText(message);
    65             } catch (IOException e) {
    66                log.error("消息发送失败:{}-{}-{}",userId,message,e.getMessage());
    67             }
    68         }else{
    69             log.error("用户{},不在线!",userId);
    70         }
    71     }
    View Code

    四、使用redis实现websocket的session共享

    由于websocket的session不能序列化,所以不能直接存储到redis中(而且就算可以存储到redi中,A系统产生的session可能也不能在B系统使用),因此可以采用rredis发布/订阅配置方式实现共享。

    4.1、redis发布/订阅配置

    @Configuration
    @EnableCaching
    public class RedisConfig {
    
        public static final  String TOPIC_PATTERN = "channel:websocket";
    
        @Autowired
        private RedisConnectionFactory connectionFactory;
    
        @Bean
        RedisMessageListenerContainer container(MessageListenerAdapter listenerAdapter) {
    
            RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(connectionFactory);
            // 可以添加多个 messageListener,配置不同的交换机
            container.addMessageListener(listenerAdapter, new PatternTopic(TOPIC_PATTERN));
            return container;
        }
    
        @Bean
        MessageListenerAdapter listenerAdapter(RedisReceiver receiver) {
           //监听消息的对象和方法
            return new MessageListenerAdapter(receiver, "onMessage");
        }
    }

    4.2、监听类

    @Slf4j
    @Component
    public class RedisReceiver implements MessageListener {
     // 字符串的分隔符
        public static final String DELIMITER ="✈";
    
        @Override
        public void onMessage(Message message, byte[] bytes) {
            byte[] body = message.getBody();
            if (body.length>0){
                String content = new String(body);
                if (content.contains(DELIMITER)){
                    String[] split = StringUtils.split(content, DELIMITER);
                    if (split.length == 2){
                        WebSocket.sendInfo(split[1],split[0]);
                    }
                }
            }
        }
    }

    4.3、编写测试接口

        @ApiOperation("测试webSocket")
        @PostMapping("testWebSocket")
        public R testWebSocket(@ApiParam("发送的消息") @RequestParam String message, @ApiParam("接收用户id")@RequestParam String userId){
            redisTemplate.convertAndSend(RedisConfig.TOPIC_PATTERN,userId+ RedisReceiver.DELIMITER+message);
            return R.ok();
        }

    备注:websocket的在线测试地址:http://www.websocket-test.com/

  • 相关阅读:
    Golang数组Array
    转:【专题六】UDP编程
    转:【专题五】TCP编程
    转:【专题四】自定义Web浏览器
    转:【专题三】自定义Web服务器
    转:【专题二】HTTP协议详解
    转:【专题一】网络协议简介
    转:[你必须知道的异步编程]C# 5.0 新特性——Async和Await使异步编程更简单
    转:[你必须知道的异步编程]——基于任务的异步模式
    转:[你必须知道的异步编程]——基于事件的异步编程模式
  • 原文地址:https://www.cnblogs.com/cq-yangzhou/p/14338211.html
Copyright © 2020-2023  润新知