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的应用

 

 

posted @ 2019-07-15 19:51  果咩  阅读(2951)  评论(0编辑  收藏  举报