• springboot搭建一个简单的websocket的实时推送应用


     

    说一下实用springboot搭建一个简单的websocket 的实时推送应用

    websocket是什么

    WebSocket是一种在单个TCP连接上进行全双工通信的协议

    我们以前用的http协议只能单向的浏览器给服务器发请求,然后服务器再去相应返回数据。

    websocket呢就是可以服务器主动给浏览器发数据

    优点

    较少的控制开销

    更强的实时性

    保持连接状态

    更好的二进制的支持

    支持扩展

    更换的压缩效果

    pom文件

    springboot项目的话只需要下面这个依赖就可以了

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

    springboot的项目搭建

    这里看我上一篇文章就不在多说了

    我们需要去注入ServerEndpointExporter

    这是一个检测类型的bean 检测带注释@ServerEndpoint的bean并注册它们

    稍后我们可以讲一下@configuration @bean 和@component @autowired 等注解

      package com.gzh;
      ​
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.socket.server.standard.ServerEndpointExporter;
      ​
      /**
       * @Configuration
       * @bean
       * 表示给spring容器注入bean  他们两个一般一起使用
       * 他们和@component的区别是他们使用了动态代理cglib 所以每次调用都会返回同一个实例
       */
      @Configuration
      public class WebSocketConfig {
      ​
          /**
           * 给spring容器注入这个ServerEndpointExporter对象
           * 相当于xml:
           * <beans>
           *     <bean id="serverEndpointExporter" class="org.springframework.web.socket.server.standard.ServerEndpointExporter"/>
           * </beans>
           *
           * 检测所有带有@serverEndpoint注解的bean并注册他们。
           * @return
           */
          @Bean
              public  ServerEndpointExporter serverEndpointExporter(){
              System.out.println("我被注入了");
              return  new ServerEndpointExporter();
          }
      }

    下面使我们的websocket类

    下面一共写了五种方法

    建立通信

    关闭通信

    接收消息

    发送消息

    异常


      package com.gzh;
      ​
      import org.springframework.stereotype.Component;
      ​
      import javax.websocket.*;
      import javax.websocket.server.ServerEndpoint;
      import java.io.IOException;
      import java.util.concurrent.CopyOnWriteArraySet;
      ​
      /**
       * @Component  将类注入到容器
       * @ServerEndpoint  前端通过这个url进行访问通信 建立连接
       */
      @ServerEndpoint("/websocket")
      @Component
      public class MyWebSocket {
      ​
          //存放websocket 的线程安全的无序的集合
          private  static  CopyOnWriteArraySet<MyWebSocket> websocket = new CopyOnWriteArraySet<MyWebSocket>();
      ​
          private Session session;
      ​
          public static CopyOnWriteArraySet<MyWebSocket> getWebsocket() {
              return websocket;
          }
      ​
          public static void setWebsocket(CopyOnWriteArraySet<MyWebSocket> websocket) {
              MyWebSocket.websocket = websocket;
          }
      ​
          public Session getSession() {
              return session;
          }
      ​
          public void setSession(Session session) {
              this.session = session;
          }
      ​
          /**
           * 连接建立成功调用的方法
           * */
          @OnOpen
          public void onOpen(Session session) {
              this.session = session;
              websocket.add(this);     //加入set中
             // addOnlineCount();           //在线数加1
               System.out.println("进入onOpen方法");
              try {
                  sendMessage("连接已建立成功.");
              } catch (Exception e) {
                  System.out.println("IO异常");
              }
          }
      ​
      ​
          /**
           * 关闭通信连接
           * @param session
           */
          @OnClose
          public void onClose(Session session){
              //关闭连接后将此socket删除
              websocket.remove(this);
              System.out.println("进入onClose方法");
          }
      ​
          /**
           * 获取客户端发来的信息
           */
          @OnMessage
          public void onMessage(String message){
              System.out.println("进入onMessage方法; message = " + message);
          }
      ​
          /**
           * 给客户端推送信息
           */
          public void sendMessage(String message) throws IOException {
              System.out.println("进入sendMessage方法");
              this.session.getBasicRemote().sendText(message);
          }
      ​
          /**
           * 异常方法
           */
          @OnError
          public void onError(Session session, Throwable error){
              System.out.println("进入error方法");
              error.printStackTrace();
          }
      ​
      }
      ​

     

     

    这里呢我们稍微分析下这个方法里面的东西

    • 注解@ServerEndpoint

      package javax.websocket.server;
      ​
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
      import javax.websocket.Decoder;
      import javax.websocket.Encoder;
      import javax.websocket.server.ServerEndpointConfig.Configurator;
      ​
      @Retention(RetentionPolicy.RUNTIME)
      @Target({ElementType.TYPE})
      public @interface ServerEndpoint {
          //注释类应映射到的URI或URI模板(必写的参数)。
          String value();
          
          //子协议
          String[] subprotocols() default {};
      ​
          //解码器   输入websocket消息 输出java对象
          Class<? extends Decoder>[] decoders() default {};
      ​
          //编码器  输入java对象 输出websocket
          Class<? extends Encoder>[] encoders() default {};
      ​
          //配置器
          Class<? extends Configurator> configurator() default Configurator.class;
      }
      ​
    • CopyOnWriteArraySet(看完就知道我们为什么要用它了)

      • java.util.concurrent包下 俗称 JUC

      • 线程安全的无序的集合,可以将它理解成线程安全的HashSet

      • Set:不可重复,检索元素效率低下,删除和插入效率高,

      • List:可重复,和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低

    • @onopen @onclose等注解

      • 和前台websocket.onerror等方法相对应

    我们的主方法执行服务

    这里我们每三秒启动个线程

    package com.gzh;
      ​
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      ​
      /**
       * @SpringBootApplication   标注一个主程序,说明这是一个springboot应用
       */
      @SpringBootApplication
      public class SpringBootMainApplication {
      ​
          //编写主程序方法
          public static void main(String[] args) {
              SpringApplication.run(SpringBootMainApplication.class);
      ​
              for (int i = 0; i <= 10 ; i++){
                  try {
                      Thread.sleep(3000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  MyThread myThread = new MyThread();
                  Thread thread = new Thread(myThread);
                  thread.start();
              }
      ​
          }
      }
      ​

     

    线程类

    在线程的run方法里让他去发送消息

     
      package com.gzh;
      ​
      import java.io.IOException;
      import java.util.concurrent.CopyOnWriteArraySet;
      ​
      public class MyThread implements  Runnable{
      ​
          @Override
          public void run() {
      ​
              CopyOnWriteArraySet<MyWebSocket> websocket = MyWebSocket.getWebsocket();
              for (MyWebSocket myWebSocket : websocket) {
                  try {
                      myWebSocket.sendMessage("我要发消息了"+ Math.random());
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      ​

     

    html前端

    这里我们只需要更改我们的地址路径就即可

    <!DOCTYPE HTML>
      <html>
      <head>
          <title>My WebSocket</title>
      </head>
    <body>
      Welcome<br/>
      <input id="text" type="text" /><button onclick="send()">Send</button>    <button onclick="closeWebSocket()">Close</button>
      <div id="message">
      </div>
      </body>
    <script type="text/javascript">
          var websocket = null;
      ​
          //判断当前浏览器是否支持WebSocket  ,主要此处要更换为自己的地址
          if('WebSocket' in window){
              websocket = new WebSocket("ws://localhost:8080/websocket");
          }
          else{
              alert('Not support websocket')
          }
      ​
          //连接发生错误的回调方法
          websocket.onerror = function(){
              setMessageInnerHTML("error");
          };
      ​
          //连接成功建立的回调方法
          websocket.onopen = function(event){
              setMessageInnerHTML("open");
          }
      ​
          //接收到消息的回调方法
          websocket.onmessage = function(event){
              setMessageInnerHTML(event.data);
          }
      ​
          //连接关闭的回调方法
          websocket.onclose = function(){
              setMessageInnerHTML("close");
          }
      ​
          //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
          window.onbeforeunload = function(){
              websocket.close();
          }
      ​
          //将消息显示在网页上
          function setMessageInnerHTML(innerHTML){
              document.getElementById('message').innerHTML += innerHTML + '<br/>';
          }
      ​
          //关闭连接
          function closeWebSocket(){
              websocket.close();
          }
      ​
          //发送消息
          function send(){
              var message = document.getElementById('text').value;
              websocket.send(message);
          }
      </script>
      </html>

     

    这里说一下我们的路径 ws 我们一般会看到wss 什么时候用哪个呢?

    1.Firefox环境下https不能使用ws连接

    2.chrome内核版本号低于50的浏览器是不允许https下使用ws链接

    3.Firefox环境下https下使用wss链接需要安装证书

    所以我们可以对这部分浏览器做个处理

     let protocol = location.protocol === 'https' 
          ? 'wss://localhost:8888' 
          : 'ws://localhost:8889';
      new WebSocket(protocol);
    以上就是springboot下实现简单的websocket的应用

     

     

    关注公众号,回复:java面试题,即可获得共几百页的java面试题

  • 相关阅读:
    hdu 1084(水题)
    hdu 1872(稳定排序)
    NOI 2008 志愿者招募 / bzoj 1061 (最小费用最大流)
    hdu 1019(LCM)
    hdu 1876(dp)
    Codeforces Round #171 (Div. 2)
    poj 3422(最小费用最大流)
    poj 3264(简单线段树)
    Codeforces Round #156 (Div. 2)
    Codeforces Round #169 (Div. 2)
  • 原文地址:https://www.cnblogs.com/guomie/p/11191041.html
Copyright © 2020-2023  润新知