spring中使用websocket

在spring中使用websocket,与前端通讯,实时推送发布消息给前端展示

1. 引入websocket,在pom.xml中加入:

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

2. 配置WebSocketConfig

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

3. 配置WebSocketServer,定义接收格式

@Slf4j
@ServerEndpoint(value = "/ws/{uid}")
@Component
public class WebSocketServer {
    private Session curSession;
    private String curUId;
    private static ConcurrentHashMap<String, WebSocketServer> websocketMap = new ConcurrentHashMap<>();
    private NoticeService noticeService;
    private static ApplicationContext applicationContext;
    public static void setApplicationContext(ApplicationContext applicationContext) {
        WebSocketServer.applicationContext = applicationContext;
    }

    //实现一个账户页面多开的时候,任然可以接收到消息
    @OnOpen
    public void onOpen(Session session, @PathParam("uid") Long uid) {
        this.curSession = session;
        String uidStr = uid.toString();
        Integer maxIndex = 1;
        for (String key : websocketMap.keySet()) {
            if (key.contains(uidStr)) {
                //相同的id情况下,接收到新链接的时候,获取最大index
                maxIndex = Integer.parseInt(key.split("_")[1]) + 1;
            }
        }
        uidStr = uidStr + "_" + maxIndex;
        websocketMap.put(uidStr, this);
        this.curUId = uidStr;
        noticeService = applicationContext.getBean(NoticeService.class);
        PageResult<NoticeReceiveResult> received = noticeService.received(uid);

        try {
            sendMessage(JSON.toJSONString(received));
        } catch (IOException e) {
            log.error("ws IO 异常");
        }
    }

    @OnClose
    public void onClose() {
        if (websocketMap.get(this.curUId) != null) {
            websocketMap.remove(this.curUId);
            log.info(curUId + "已经关闭");
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        if (StringUtils.isNoneBlank(message)) {
            for (WebSocketServer server : websocketMap.values()) {
                try {
                    server.sendMessage(message);
                } catch (IOException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送消息
     *
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        this.curSession.getBasicRemote().sendText(message);
    }

    /**
     * uid==null,发送给所有,否则发送给指定uid
     *
     * @param message
     * @param uid
     * @throws IOException
     */
    public static void sendInfo(String message, @PathParam("uid") Long uid) throws IOException {
        for (WebSocketServer server : websocketMap.values()) {
            try {
                if (uid == null) {
                    server.sendMessage(message);
                } else if (server.curUId.equals(uid)) {
                    server.sendMessage(message);
                }

            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
        }
    }
}

4. 配置启动项,否则websocket服务类中,定义的其他类将为null,因为没有上下文,无法拿到bean。此项目启动项的类为SpringApplication, WebSocketServer.setApplicationContext(context)

@SpringBootApplication
public class SpringTestApplication {

    private static final Log log = Log.get();

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringTestApplication.class, args);
        log.info(">>> " + SpringTestApplication.class.getSimpleName() + " is success!");
        WebSocketServer.setApplicationContext(context);
    }
}

5. 前端使用。前端使用vue,对于其他框架或者纯js也是通用的。

5.1 在mounted时,进行ws连接

  mounted() {
    this.openSocket()
    //若无法连接,将使用定时器,反复请求连接
    const timer = setInterval(() => {
      if (!this.socket || (this.socket && this.socket.readyState === 3)) {
        this.openSocket()
      } else if (this.socket && this.socket.readyState === 1) {
        clearInterval(timer)
      }
    }, 500)    
  }

5.2 使用

openSocket() {
      if (typeof WebSocket === 'undefined') {
        console.log('浏览器不支持websocket')
      } else {
        console.log('浏览器支持websocket')
        var wsUrl =  'http:localhost:8888/ws/' + this.currentUser.id
        wsUrl = wsUrl.replace('https', 'ws').replace('http', 'ws')
        this.socket = new WebSocket(wsUrl)
        this.socket.onopen = function() {
          console.log('ws已经连接')
        }
        var that = this //
        this.socket.onmessage = function(msg) {
          // 1.初始化数据
          that.InitTipData(JSON.parse(msg.data));//调用InitTipData,初始化数据
           console.log('ws接收消息:' + msg.data)          
        }
        this.socket.onclose = function() {
          console.log('ws关闭了')
        }
        this.socket.onerror = function() {
          console.log('ws 发送错误')
        }
        // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
        window.onbeforeunload = function() {
          try {
            this.socket.close()
          } catch (e) {
            console.log('ws error:' + e)
          }
        }
      }
    },
关于websocket鉴权以及其他常见问题,可以查考网上其他,例如:

https://blog.csdn.net/qq_38531706/article/details/118026034
https://blog.csdn.net/weixin_39263573/article/details/126276427?spm=1001.2014.3001.5501

其他问题:

https://blog.csdn.net/qq_41695498/article/details/104149455
https://blog.csdn.net/kshon/article/details/102522954

wss

https://blog.csdn.net/BHSZZY/article/details/123229338

https://blog.csdn.net/carrie7788/article/details/127517877
https://blog.csdn.net/KeepStruggling/article/details/105543449

posted @ 2022-12-22 16:13  daxiaxj  阅读(123)  评论(0编辑  收藏  举报