SpringBoot集成WebSocket

  • 如果网页不刷新,服务端有新消息如何推送到浏览器?

    • 解决方案,采用轮询的方式。即:通过js不断的请求服务器,查看是否有新数据,如果有,就获取到新数据。

      这种解决方法是否存在问题呢?

      当然是有的,如果服务端一直没有新的数据,那么js也是需要一直的轮询查询数据,这就是一种资源的浪费。

      那么,有没有更好的解决方案? 有!那就是采用WebSocket技术来解决。

  • 什么是WebSocket
    • WebSocket HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。 WebSocket是真正实现了全双工通信的服务器向客户端推的互联网技术。 它是一种在单个TCP接上进行全双工通讯协议。Websocket通信协议与2011年倍IETF定为标准RFC 6455Websocket APIW3C定为标准。
      • 专业词语自行百度吧.
  • httpwebsocket的区别
    • http
      • http协议是短连接,因为请求之后,都会关闭连接,下次重新请求数据,需要再次打开链接。
    • websocket
      • WebSocket协议是一种长链接,只需要通过一次请求来初始化链接,然后所有的请求和响应都是通过这个TCP链接进行通讯。
  • 搭建项目:pom
    • <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
      
          <!--spring boot的支持-->
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.1.0.RELEASE</version>
          </parent>
      
          <packaging>war</packaging>
      
          <dependencies>
              <!--<dependency>-->
                  <!--<groupId>javax</groupId>-->
                  <!--<artifactId>javaee-api</artifactId>-->
                  <!--<version>7.0</version>-->
                  <!--<scope>provided</scope>-->
              <!--</dependency>-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-websocket</artifactId>
              </dependency>
          </dependencies>
      
          <build>
              <plugins>
                  <!-- java编译插件 -->
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-compiler-plugin</artifactId>
                      <version>3.2</version>
                      <configuration>
                          <source>1.8</source>
                          <target>1.8</target>
                          <encoding>UTF-8</encoding>
                      </configuration>
                  </plugin>
                  <!-- 配置Tomcat插件 -->
                  <plugin>
                      <groupId>org.apache.tomcat.maven</groupId>
                      <artifactId>tomcat7-maven-plugin</artifactId>
                      <version>2.2</version>
                      <configuration>
                          <port>8082</port>
                          <path>/</path>
                      </configuration>
                  </plugin>
              </plugins>
          </build>
      </project>

       

  • websocket的相关注解说明

    • @ServerEndpoint("/websocket/{uid}")
          申明这是一个websocket服务
          需要指定访问该服务的地址,在地址中可以指定参数,需要通过{}进行占位
      @OnOpen
          用法:public void onOpen(Session session, @PathParam("uid") String uid) throws
          IOException{}
          该方法将在建立连接后执行,会传入session对象,就是客户端与服务端建立的长连接通道
          通过@PathParam获取url申明中的参数 比如前面请求的uid
      @OnClose
          用法:public void onClose() {}
          该方法是在连接关闭后执行
      @OnMessage
          用法:public void onMessage(String message, Session session) throws IOException {}
          该方法用于接收客户端发来的消息
          message:发来的消息数据
          session:会话对象(也是通道)
      发送消息到客户端
          用法:session.getBasicRemote().sendText("你好");
          通过session进行发送。

       

  • 测试案例:

    • import javax.websocket.*;
      import javax.websocket.server.PathParam;
      import javax.websocket.server.ServerEndpoint;
      import java.io.IOException;
      
      @ServerEndpoint("/websocket/{uid}")
      public class MyWebSocket {
      
          @OnOpen
          public void onOpen(Session session, @PathParam(value = "uid")String uid) throws IOException {
              System.out.println("websocket已经连接" + session);
              // 给客户端响应,欢迎登陆(连接)系统
              session.getBasicRemote().sendText(uid+",你好!欢迎登陆系统");
          }
      
          @OnClose
          public void onClose(Session session){
              System.out.println("websocket已经关闭" + session);
          }
      
          @OnMessage
          public void onMessage(String message, Session session) throws IOException {
              System.out.println("收到客户端发来的消息 --> " + message);
      
              try {
                  Thread.sleep(200);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
      
              //给客户端一个反馈
              session.getBasicRemote().sendText("消息已收到");
          }
      
      }
      

       

  • 测试工具:

    • 1.chrome插件,Simple WebSocket Client

    • 2.在线工具 百度搜索: WebSocket测试工具 很多自己选一个就行 (推荐)

  • js代码: 使用websocket不需要再引入js或者组件. 因为浏览器内置了websocket对象.

    • const socket = new WebSocket("ws://localhost:8082/websocket/1");
      socket.onopen = (ws) => {
          console.log("建立连接!", ws);
      }
      socket.onmessage = (ws) => {
          console.log("接收到消息 >> ", ws.data);
      }
      socket.onclose = (ws) => {
          console.log("连接已断开!", ws);
      }
      socket.onerror = (ws) => {
          console.log("发送错误!", ws);
      }// 2秒后向服务端发送消息
      setTimeout(() => {
          socket.send("发送一条消息试试");
      }, 2000);
      // 5秒后断开连接
      setTimeout(() => {
          socket.close();
      }, 5000);

       

  • SpringBoot整合WebSocket

    • pom文件中javaee-api 去掉. 因为springboot内部会继承Tomcat的包, 会和这个包冲突

    • 启动类上不需要加什么特殊的注解.

    • 主需要在具体的类上加  @EnableWebSocket 注解启动websocket

  • Spring中,处理消息的具体业务逻辑需要实现WebSocketHandler接口,继承TextWebSocketHandler就可以了

    • import org.springframework.stereotype.Component;
      import org.springframework.web.socket.CloseStatus;
      import org.springframework.web.socket.TextMessage;
      import org.springframework.web.socket.WebSocketSession;
      import org.springframework.web.socket.handler.TextWebSocketHandler;
      
      import java.io.IOException;
      
      @Component
      public class MyHandler extends TextWebSocketHandler {
      
          @Override
          public void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
              System.out.println("获取到消息 >> " + message.getPayload());
      
              // 向客户端发送消息
              session.sendMessage(new TextMessage("消息已收到"));
              //处理获取的参数
              if (message.getPayload().equals("10")) {
                  for (int i = 0; i < 10; i++) {
                      session.sendMessage(new TextMessage("消息 -> " + i));
                      try {
                          Thread.sleep(100);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
      
          @Override
          public void afterConnectionEstablished(WebSocketSession session) throws
                  Exception {
              Integer uid = (Integer) session.getAttributes().get("uid");
              session.sendMessage(new TextMessage(uid + ", 你好!欢迎连接到ws服务"));
          }
      
          @Override
          public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
                  throws Exception {
              System.out.println("断开连接!");
          }
      }
      

       

  • 编写配置类

    • import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.socket.WebSocketHandler;
      import org.springframework.web.socket.config.annotation.EnableWebSocket;
      import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
      import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
      
      @Configuration
      @EnableWebSocket
      public class WebSocketConfig implements WebSocketConfigurer {
      
          @Autowired
          private MyHandler myHandler;
      
          @Autowired
          private MyHandshakeInterceptor myHandshakeInterceptor;
      
          @Override
          public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
              //注册myHandler , 并给请求的路径.
              registry.addHandler(this.myHandler, "/ws")
                      //允许跨域请求
                      .setAllowedOrigins("*");
          }
      }
      

       

  • websocket拦截器

    • Spring中提供了websocket拦截器,可以在建立连接之前写些业务逻辑

    • import org.springframework.http.server.ServerHttpRequest;
      import org.springframework.http.server.ServerHttpResponse;
      import org.springframework.stereotype.Component;
      import org.springframework.web.socket.WebSocketHandler;
      import org.springframework.web.socket.server.HandshakeInterceptor;
      
      import java.util.Map;
      
      @Component
      public class MyHandshakeInterceptor implements HandshakeInterceptor {
      
          /**
           * 握手之前,若返回false,则不建立链接
           *
           * @param request
           * @param response
           * @param wsHandler
           * @param attributes
           * @return
           * @throws Exception
           */
          @Override
          public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse
                  response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws
                  Exception {
              //将用户id放入socket处理器的会话(WebSocketSession)中
              attributes.put("uid", 1001);
              System.out.println("开始握手。。。。。。。");
              return true;
          }
      
          @Override
          public void afterHandshake(ServerHttpRequest request, ServerHttpResponse
                  response, WebSocketHandler wsHandler, Exception exception) {
              System.out.println("握手成功啦。。。。。。");
          }
      }
      

       

    • 将拦截器添加到websocket服务中

      • 在配置类上添加@bean注解 即:

        • import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.web.socket.WebSocketHandler;
          import org.springframework.web.socket.config.annotation.EnableWebSocket;
          import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
          import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
          
          @Configuration
          @EnableWebSocket
          public class WebSocketConfig implements WebSocketConfigurer {
          
              @Autowired
              private MyHandler myHandler;
          
              @Autowired
              private MyHandshakeInterceptor myHandshakeInterceptor;
          
              @Override
              public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
                  //注册myHandler 并分配请求地址
                  registry.addHandler(this.myHandler, "/ws")
                          //允许跨域请求
                          .setAllowedOrigins("*")
                          //启用拦截器
                          .addInterceptors(this.myHandshakeInterceptor);
              }
          }
          

           

        • 请求测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值