• springboot WebSocket的使用


    1、前言

    在springboot中开发websock相关功能,报Error during WebSocket handshake Unexpected response code 404

    2、原因分析

    发现是websocket绑定终结点错误,需要添加WebSocketConfig

    package com.ruoyi.framework.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;
    
    @Configuration
    public class WebSocketConfig {
    
        /**
         * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
         */
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }
    

    还需ShiroConfig权限通过

    filterChainDefinitionMap.put("/ws/**", "anon");
    

    3、补充websock和smartSocket结合使用

    package com.ruoyi.project.front.socket;
    
    import org.smartboot.socket.MessageProcessor;
    import org.smartboot.socket.StateMachineEnum;
    import org.smartboot.socket.transport.AioQuickServer;
    import org.smartboot.socket.transport.AioSession;
    import org.springframework.stereotype.Component;
    
    import javax.websocket.*;
    import javax.websocket.server.ServerEndpoint;
    import java.io.IOException;
    import java.util.concurrent.CopyOnWriteArraySet;
    import java.util.concurrent.ExecutionException;
    
    @Component
    @ServerEndpoint("/ws/webSocket")
    public class AudioWebSocket implements MessageProcessor<String> {
        //███████████████ websocket ████████████████████████████████████████████████████████████████████████████████████
    
        //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
        /**
         * 在线数量
         */
        private static int onlineCount = 0;
    
        /**
         * 获取在线数量
         */
        public static synchronized int getOnlineCount() {
            return onlineCount;
        }
    
        /**
         * 增加在线数量
         */
        public static synchronized void addOnlineCount() {
            AudioWebSocket.onlineCount++;
        }
    
        /**
         * 减去在线数量
         */
        public static synchronized void subOnlineCount() {
            AudioWebSocket.onlineCount--;
        }
    
        /**
         * websocket集合
         */
        //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
        private static CopyOnWriteArraySet<AudioWebSocket> webSocketSet = new CopyOnWriteArraySet<>();
        /**
         * webSocketSession
         */
        //与某个客户端的连接会话,需要通过它来给客户端发送数据 websocket
        private Session webSocketSession;
    
        @OnOpen
        public void onOpen(Session session) throws IOException, ExecutionException, InterruptedException {
            System.out.println("websocket connect");
            this.webSocketSession = session;
            webSocketSet.add(this);     //加入set中
            addOnlineCount();           //在线数加1
        }
    
        @OnClose
        public void onClose() {
            webSocketSet.remove(this);  //从set中删除
            subOnlineCount();           //在线数减1
        }
    
        @OnError
        public void onError(Session session, Throwable error) {
            error.printStackTrace();
        }
    
        @OnMessage
        public void onMessage(String message, Session session) {
            System.out.println("来自客户端的消息:" + message);
            // if (message.startsWith("CC")) {
            //     Timer timer = new Timer();
            //     timer.schedule(new TimerTask() {
            //         @Override
            //         public void run() {
            //             sendMessageToAllClient(SimpleDateFormat.getDateInstance().format(new Date()) + "222");
            //         }
            //     }, 0, 1000);
            // }
        }
    
        public synchronized void sendMessageToAllClient(String message) {
            for (AudioWebSocket item : webSocketSet) {
                try {
                    item.webSocketSession.getBasicRemote().sendText(message);
                } catch (IOException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    
    
        //███████████████ AioSession ████████████████████████████████████████████████████████████████████████████████████
    
        public static void aioStart() {
            try {
                aioQuickServer = new AioQuickServer<>(12000, new SinglechipFormProtocol(), new AudioWebSocket());
                aioQuickServer.setReadBufferSize(2048);
                aioQuickServer.start();
                System.out.println("通信模块 服务启动 ");
            } catch (Exception e) {
                e.printStackTrace();
                // LogHelper.info("通信模块  socket服务关闭   ");
                aioQuickServer.shutdown();
            }
        }
    
        public static void defend() {
            new Thread(() -> {
                aioStart();
            }).start();
        }
    
        /**
         * 服务端变量
         */
        public static AioQuickServer<String> aioQuickServer;
        //与某个客户端的连接会话,需要通过它来给客户端发送数据
        /**
         * aioSession
         */
        private static AioSession<String> aioSession;
    
    
        @Override
        public void process(AioSession<String> aioSession, String s) {
            System.out.println("aioSession  " + s);
            sendMessageToAllClient(s);
        }
    
        @Override
        public void stateEvent(AioSession<String> aioSession, StateMachineEnum stateMachineEnum, Throwable throwable) {
            switch (stateMachineEnum) {
                case NEW_SESSION:
                    this.aioSession = aioSession;
                    System.out.println("通信模块 NEW_SESSION state:" + stateMachineEnum);
                    break;
                case SESSION_CLOSED:
                    System.out.println("通信模块 SESSION_CLOSED state:" + stateMachineEnum);
                    this.aioSession = null;
                    break;
                default:
                    System.out.println("通信模块 other state:" + stateMachineEnum + " " + throwable.toString());
                    break;
            }
        }
    }
    
    

    SinglechipFormProtocol

    package com.ruoyi.project.front.socket;
    
    import org.smartboot.socket.Protocol;
    import org.smartboot.socket.transport.AioSession;
    
    import java.nio.ByteBuffer;
    
    public class SinglechipFormProtocol implements Protocol<String> {
    
    	private static final int INT_LENGTH = 4;
    
    	@Override
    	public String decode(ByteBuffer readBuffer, AioSession<String> session, boolean bool) {
    		// TODO Auto-generated method stub
    		int length = readBuffer.limit();
    
    		if (readBuffer.remaining() < INT_LENGTH) {
    			return null;
    		}
    
    		byte[] bytes = new byte[length];
    		readBuffer.get(bytes, 0, length);
    
    		String retStr = new String(bytes);
    
    		// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    
    		// retStr = sdf.format(new Date())+ ","+ retStr;
    
    		return retStr;
    	}
    
    	@Override
    	public ByteBuffer encode(String str, AioSession<String> session) {
    		// TODO Auto-generated method stub
    		byte[] dd = str.getBytes();
    		ByteBuffer b = ByteBuffer.allocate(INT_LENGTH);
    		b.put(dd);
    		b.flip();
    		return b;
    	}
    }
    
    

    pom.xml

            <dependency>
                <groupId>org.smartboot.socket</groupId>
                <artifactId>aio-core</artifactId>
                <version>1.3.12</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
            </dependency>
    
  • 相关阅读:
    CODING DevOps 系列课程重磅来袭!
    CODING 敏捷实战系列加餐课:CODING 做敏捷这一年
    IdentityServer4 QuickStart 授权与自定义Claims
    .NET项目升级:可为空引用
    ASP.NET CORE WEBAPI文件下载
    xunit测试无法找到testhost或没有可用测试的问题解决方法
    强迫症福利--收起.NET程序的dll来
    使用高性能Pipelines构建.NET通讯程序
    ASP.NET Core使用TopShelf部署Windows服务
    .NET Standard库引用导致的FileNotFoundException探究
  • 原文地址:https://www.cnblogs.com/kikyoqiang/p/14203483.html
Copyright © 2020-2023  润新知