当前位置:   article > 正文

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

springboot websocket 服务端

websocket介绍:

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

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

使用场景:

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

1、SpringBoot集成websocket作为服务端

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

实现步骤:

  1. 添加websocket整合包

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
  2. 编写websocket配置类,暴露WebSocket

    @Configuration
    public class WebSocketConfig {
    
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  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<String, Session> 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);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
  4. 测试

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

    图片:
    在这里插入图片描述控制台打印信息:
    在这里插入图片描述

2、SpringBoot集成websocket作为客户端

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

实现步骤:

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

    <dependency>
        <groupId>org.java-websocket</groupId>
        <artifactId>Java-WebSocket</artifactId>
        <version>1.3.8</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
  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 + "--------");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  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;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

3. 测试

至此,我们已经搭好了一个websocket的服务器和一个websocket的客户端,我们可以同时把这两个服务跑起来,做一个调用。
注意:需要先启动服务端,再启动客户端,客户端会无法创建连接,报空指针异常。
服务端启动:
在这里插入图片描述客户端启动:
在这里插入图片描述现在我们向客户端的controller请求一个消息:
你可以用postman或者其他api工具,我现在图简单直接在浏览器发起:

在这里插入图片描述客户端控制台日志:

在这里插入图片描述服务端控制台日志:
在这里插入图片描述OK,大功告成。
整套源码包地址

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/576613
推荐阅读
相关标签
  

闽ICP备14008679号