简单的websocket demo

使用 websocket 有两种方式:1 是使用 sockjs,2 是使用 h5 的标准。使用 Html5 标准自然更方便简单,所以记录的是配合 h5 的使用方法。

  1. pom

    <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
  2. 使用 @ServerEndpoint 创立 websocket endpoint
      首先要注入 ServerEndpointExporter,这个 bean 会自动注册使用了 @ServerEndpoint 注解声明的 Websocket endpoint。要注意,如果使用独立的 servlet 容器,而不是直接使用 springboot 的内置容器,就不要注入 ServerEndpointExporter,因为它将由容器自己提供和管理。

    @Configuration
    public class WebSocketConfig {
    
     @Bean
     public ServerEndpointExporter serverEndpointExporter(){
         return new ServerEndpointExporter();
     }
    }

    接下来就是写 websocket 的具体实现类,直接上代码:

    @Component
    @ServerEndpoint("/websocket")
    public class MyWebSocket {
     //与某个客户端的连接会话,需要通过它来给客户端发送数据
     private Session session;
     //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
     private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
    
     @OnOpen
     public void onOpen(Session session){
         this.session = session;
         webSocketSet.add(this);
         System.out.println("创建连接:第"+webSocketSet.size());
     }
    
     @OnClose
     public void onClose(){
         webSocketSet.remove(this);
         System.out.println("关闭连接");
     }
    
     @OnMessage
     public void onMessage(String message){
         System.out.println("接受客户端消息:"+message);
     }
    
     /**
      * 群发消息
      */
     public void sendMessage(String message){
         for (MyWebSocket myWebSocket : webSocketSet){
             try {
                 myWebSocket.session.getBasicRemote().sendText(message);
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
     //单发消息可以在群发的基础上改造
    }

    使用 springboot 的唯一区别是要 @Component 声明下,而使用独立容器是由容器自己管理 websocket 的,但在 springboot 中连容器都是 spring 管理的。
    虽然 @Component 默认是单例模式的,但 springboot 还是会为每个 websocket 连接初始化一个 bean,所以可以用一个静态 set 保存起来。

  3. 前端代码

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
     <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;" name="viewport" />
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title>主页</title>
    </head>
    <body>
    <script type="text/javascript" th:inline="javascript">
     var websocket = null;
     if('WebSocket' in window){
         //127.0.0.1:8080 是的项目请求地址 websocket是对应@ServerEndpoint("/websocket")里的值 合起来就是请求websocket地址
         websocket = new WebSocket('ws://127.0.0.1:8080/websocket');
     }else{
         alert("该浏览器不支持websocket!")
     }
     websocket.onopen = function (event) {
         console.log("建立连接");
     }
     websocket.onclose = function (event) {
         console.log("关闭连接");
     }
     websocket.onmessage = function (event) {
         console.log("接受消息:"+event.data);
         //模拟发送信息给服务端
         websocket.send("我已经接受到信息啦!")
     }
     websocket.onerror = function (event) {
         console.log("出现了错误")
     }
     window.onbeforeunload = function () {
         websocket.onclose();
     }
    </script>
    </body>
    </html>
  4. 测试方法

    @RestController
    @RequestMapping("/testWebSocketSend")
    public class TestWebSocketSendController {
     @Autowired
     private MyWebSocket myWebSocket;
     @GetMapping("send")
     public String send(@RequestParam("message") String message){
         //todo 其他业务
         //websocket服务
         myWebSocket.sendMessage(message);
         return "成功";
     }
    }

    打开主页面 127.0.0.1:8080/index.html
    建立连接
    浏览器请求 127.0.0.1:8080/testWebSocketSend/se... 我之前写的 controller 地址
    接受服务端发来的消息
    服务端接受客户端的消息打印
    服务端接受客户端的消息

本作品采用《CC 协议》,转载必须注明作者和本文链接