• WebSocket实例


      之前讲过用java写长轮询,长轮询的好处就是客户端能够及时的获取到服务端的变更。但是本质上还是客户端去捞数据。

    现在有一种更好的后端向前端推数据的方式,那就是websocket。本文就通过实例,展示下如何写websocket程序。

       

    •   准备

      需要引入两个websocket的maven坐标

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
                <version>2.7.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.java-websocket</groupId>
                <artifactId>Java-WebSocket</artifactId>
                <version>1.4.0</version>
            </dependency>

      

    • 客户端

      基础类

    @Slf4j
    public class BaseWebsocketClient extends WebSocketClient {
        //客户端标识
        private String clientName;
        //客户端连接状态
        private boolean isConnect = false;
    
        public BaseWebsocketClient(URI serverUri, Map<String, String> httpHeaders,
                                   String clientName) {
            super(serverUri, new Draft_6455(), httpHeaders, 0);
            this.clientName = clientName;
        }
    
        @Override
        public void onOpen(ServerHandshake serverHandshake) {
            log.info("------ {} onOpen ------{}", clientName);
        }
    
        @Override
        public void onMessage(String s) {
            log.info("------ {} onMessage ------{}", s);
        }
        /***检测到连接关闭之后,会更新连接状态以及尝试重新连接***/
        @Override
        public void onClose(int i, String s, boolean b) {
            log.info("------ {} onClose ------{}", clientName, b);
            setConnectState(false);
    //        recontact();
        }
        /***检测到错误,更新连接状态***/
        @Override
        public void onError(Exception e) {
            log.info("------ {} onError ------{}", clientName, e);
            setConnectState(false);
        }
    
        public void setConnectState(boolean isConnect) {
            this.isConnect = isConnect;
        }
    
        public boolean getConnectState(){
            return this.isConnect;
        }
    
    }

      

    •   实现类

    @Slf4j
    public class DeviceWebsocketClient extends BaseWebsocketClient{
    
        private static final String ACS_CTRL_RESULT = "deviceWebsocketClient";
        private static final String SUBSCRIBE = "subscribe";
        /*这个订阅格式是实现约定好的,可以具体情况具体分析*/
        private String sendStr = "{\n" +
                "    \"method\": \"subscribe\",\n" +
                "    \"params\": \"device\"\n" +
                "}";
    
        public DeviceWebsocketClient(URI serverUri, Map<String, String> httpHeaders) {
            super(serverUri, httpHeaders, ACS_CTRL_RESULT);
        }
    
        @Override
        public void onOpen(ServerHandshake serverHandshake) {
            log.info("------ {} onOpen ------", ACS_CTRL_RESULT);
            this.send(sendStr);
            setConnectState(true);
        }
    
        @Override
        public void onMessage(String msg) {
            log.info("-------- receive acs ctrl result: " + msg + "--------");
            System.out.println("receive server message " + msg);
        }
    }

      

    •   客户端的bean

    @Service
    @Slf4j
    @Order(value=100)
    public class DeviceWebsocketClientService {
    
        @PostConstruct
        public void start() {
            try {
                log.info("start to receive device data");
                URI uri = new URI("ws://localhost:8080/websocket/10086");
                Map<String, String> httpHeaders = new HashMap<>(4);
                httpHeaders.put("Origin", "http://" + uri.getHost());
                DeviceWebsocketClient deviceWebsocketClient = new DeviceWebsocketClient(uri, httpHeaders);
                deviceWebsocketClient.connect();
            }catch (Exception e){
                log.error("start to receive device data failed", e);
            }
        }
    }

      

    •   服务端

      特别注意,如果使用的是SpringBoot内嵌tomcat一定要加这段代码

      如果是打成war包独立部署,则可以不要

    @Configuration
    @ConditionalOnWebApplication
    public class WebSocketConfig {
        @Bean
        public ServerEndpointExporter  endpointExporter() {
            return new ServerEndpointExporter ();
        }
    
    }
    @Component
    @Slf4j
    @ServerEndpoint("/websocket/{userId}")
    @Order(1)
    public class WebSocketServer {
        //与某个客户端的连接会话,需要通过它来给客户端发送数据
        private Session session;
        private static final CopyOnWriteArraySet<WebSocketServer> webSockets = new CopyOnWriteArraySet<>();
        // 用来存在线连接数
        private static final Map<String, Session> sessionPool = new HashMap<String, Session>();
        /**
         * 链接成功调用的方法
         */
        @OnOpen
        public void onOpen(Session session, @PathParam(value = "userId") String userId) {
            try {
                this.session = session;
                webSockets.add(this);
                sessionPool.put(userId, session);
                log.info("websocket消息: 有新的连接,总数为:" + webSockets.size());
            } catch (Exception e) {
            }
        }
        /**
         * 收到客户端消息后调用的方法
         */
        @OnMessage
        public void onMessage(String message) {
    
            log.info("websocket消息: 收到客户端消息:" + message);
        }
        /**
         * 此为单点消息
         */
        public void sendOneMessage(String userId, String message) {
            Session session = sessionPool.get(userId);
            if (session != null && session.isOpen()) {
                try {
                    log.info("websocket消: 单点消息:" + message);
                    session.getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        public void start() {
            log.info("websocket service startup");
            try {
                for (int i=0;i<10;i++) {
                    sendOneMessage("10086", "message " + i);
                }
            }catch (Exception e){
                log.error("start to receive device data failed", e);
            }
        }
    }
    •   测试代码

    @RestController
    @Slf4j
    @SpringBootApplication
    public class MaskController {
    
        @Autowired
        private WebSocketServer webSocketServer;
    
        @RequestMapping("/trigger")
        public void triggerWebSocket(HttpServletRequest request, HttpServletResponse response) {
    
            webSocketServer.start();
        }
  • 相关阅读:
    问题集
    第04次作业-树
    06-图
    05-查找
    04-树
    03-栈和队列
    02-线性表
    01-抽象数据类型
    C语言--总结报告
    C语言--函数嵌套
  • 原文地址:https://www.cnblogs.com/juniorMa/p/16579231.html
Copyright © 2020-2023  润新知