springboot使用websocket

在这里插入图片描述
我们都知道 http 协议只能浏览器单方面向服务器发起请求获得响应,服务器不能主动向浏览器推送消息。想要实现浏览器的主动推送有下面几种种主流实现方式:

  • 轮询是客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。其缺点也很明显:连接数会很多,一个接受,一个发送。而且每次发送请求都会有Http的Header,会很耗流量,也会消耗CPU的利用率。
    优点:实现简单,无需做过多的更改
    缺点:轮询的间隔过长,会导致用户不能及时接收到更新的数据;轮询的间隔过短,会导致查询请求过多,增加服务器端的负担

  • 长轮询长轮询是对轮询的改进版,客户端发送HTTP给服务器之后,看有没有新消息,如果没有新消息,就一直等待。当有新消息的时候,才会返回给客户端。在某种程度上减小了网络带宽和CPU利用率等问题。由于http数据包的头部数据量往往很大(通常有400多个字节),但是真正被服务器需要的数据却很少(有时只有10个字节左右),这样的数据包在网络上周期性的传输,难免对网络带宽是一种浪费。
    优点:比 Polling 做了优化,有较好的时效性
    缺点:保持连接会消耗资源; 服务器没有返回有效数据,程序超时。

  • iframe流方式是在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间创建一条长连接,服务器向iframe传输数据(通常是HTML,内有负责插入信息的javascript),来实时更新页面。
    优点:消息能够实时到达;浏览器兼容好
    缺点:服务器维护一个长连接会增加开销;IE、chrome、Firefox会显示加载没有完成,图标会不停旋转。

哲学三问

WebSocket是什么

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

springBoot使用WebSocket

springboot 使用 websocket 有两种方式,一种是实现简单的 websocket,另外一种是实现STOMP协议

  1. 依赖引入

要使用 websocket 关键是@ServerEndpoint这个注解,该注解是 javaee 标准中的注解,tomcat7 及以上已经实现了,如果使用传统方法将 war 包部署到 tomcat 中,只需要引入如下 javaee 标准依赖即可:

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-api</artifactId>
  <version>7.0</version>
  <scope>provided</scope>
</dependency>

如使用 springboot 内置容器,无需引入,springboot 已经做了包含。我们只需引入如下依赖即可:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    <version>1.5.3.RELEASE</version>
    <type>pom</type>
</dependency>

  1. 注入 Bean
    首先注入一个ServerEndpointExporterBean,该 Bean 会自动注册使用@ServerEndpoint 注解申明的 websocket endpoint。代码如下:
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

  1. 申明 endpoint
    建立MyWebSocket.java类,在该类中处理 websocket 逻辑
@ServerEndpoint(value = "/websocket") //接受websocket请求路径
@Component  //注册到spring容器中
public class MyWebSocket {


    //保存所有在线socket连接
    private static Map<String,MyWebSocket> webSocketMap = new LinkedHashMap<>();

    //记录当前在线数目
    private static int count=0;

    //当前连接(每个websocket连入都会创建一个MyWebSocket实例
    private Session session;

    private Logger log = LoggerFactory.getLogger(this.getClass());
    //处理连接建立
    @OnOpen
    public void onOpen(Session session){
        this.session=session;
        webSocketMap.put(session.getId(),this);
        addCount();
        log.info("新的连接加入:{}",session.getId());
    }

    //接受消息
    @OnMessage
    public void onMessage(String message,Session session){
        log.info("收到客户端{}消息:{}",session.getId(),message);
        try{
            this.sendMessage("收到消息:"+message);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //处理错误
    @OnError
    public void onError(Throwable error,Session session){
        log.info("发生错误{},{}",session.getId(),error.getMessage());
    }

    //处理连接关闭
    @OnClose
    public void onClose(){
        webSocketMap.remove(this.session.getId());
        reduceCount();
        log.info("连接关闭:{}",this.session.getId());
    }

    //群发消息

    //发送消息
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    //广播消息
    public static void broadcast(){
        MyWebSocket.webSocketMap.forEach((k,v)->{
            try{
                v.sendMessage("这是一条测试广播");
            }catch (Exception e){
            }
        });
    }

    //获取在线连接数目
    public static int getCount(){
        return count;
    }

    //操作count,使用synchronized确保线程安全
    public static synchronized void addCount(){
        MyWebSocket.count++;
    }

    public static synchronized void reduceCount(){
        MyWebSocket.count--;
    }
}

  1. 客户的实现
    客户端使用 h5 原生 websocket,部分浏览器可能不支持。代码如下:
<html>
  <head>
    <title>websocket测试</title>
    <meta charset="utf-8" />
  </head>

  <body>
    <button onclick="sendMessage()">测试</button>
    <script>
      let socket = new WebSocket("ws://localhost:8080/websocket");
      socket.onerror = err => {
        console.log(err);
      };
      socket.onopen = event => {
        console.log(event);
      };
      socket.onmessage = mess => {
        console.log(mess);
      };
      socket.onclose = () => {
        console.log("连接关闭");
      };

      function sendMessage() {
        if (socket.readyState === 1) socket.send("这是一个测试数据");
        else alert("尚未建立websocket连接");
      }
    </script>
  </body>
</html>

实现STOMP协议

  1. 配置 Spring启用 WebSocket和 STOMP消息传递。
@EnableWebSocketMessageBroker
package com.kl.websocket;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket")
                .setAllowedOrigins("http://localhost:8976")
                .addInterceptors()
                .withSockJS();
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值