你还在使用 ajax 轮询吗?试试 WebSocket 让后端主动推送消息

概述

应用场景:

  • 消息推送,替代 ajax 轮询。
  • 在线聊天

WebSocket 是 HTML5 提供的在单个 TCP 链接上进行全双工通讯的协议。

客户端请求

  • Connection: upgrade 表示客户端希望升级连接
  • Upgrade: websocket 表示希望升级到 websocket 协议
Request URL: ws://127.0.0.1:8080/webSocket/88274.2421418903
Request Method: GET
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: Uzqa3dqq8zeBv2Ol4nxOaQ==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

服务端响应

Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: Fl7B8k4erUi8bempzvN91WtKtqk=
Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15

HTML5 中的 WebSocket

WebSocket 的属性

  • ws.readyState 当前连接状态
    • 0 连接未建立
    • 1 连接已建立,可以正常通信
    • 2 连接正在关闭
    • 3 连接已关闭或连接不能打开
  • ws.bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数

WebSocket 的事件

  • ws.onopen 建立连接后的回调函数
  • ws.onmessage 收到服务端消息的回调函数
  • ws.onerror 通信错误的回调函数
  • ws.onclose 连接关闭的回调函数

WebSocket 的方法

  • ws.send(data) 发送数据
  • ws.close() 关闭连接

使用

// 创建链接
var ws = new WebSocket(url, [protocol] );
// 建立连接后的回调函数
ws.onopen = function (event) {
};
// 收到服务端消息的回调函数
ws.onmessage = function (event) {
};
// 通信错误的回调函数
ws.onerror = function (event) {
};
// 连接关闭的回调函数
ws.onclose = function (event) {
}

// 发送数据
function send() {
    ws.send(JSON.stringify(message));
}

// 关闭连接
function closeWebSocket() {
    ws.close();
}

问题 1

Failed to construct ‘WebSocket’: The URL’s scheme must be either ‘ws’ or ‘wss’. ‘localhost’ is not allowed.

url 协议不要忽略 wswss 分别对应 httphttps 的区别

在 springboot 中访问静态页面

index.html 文件放置位置 resource/static/index.html

直接访问地址:localhost:8080/index.html

springboot 在 resource 文件夹下有两个默认文件夹

  • static 存放静态页面
  • templates 存放动态页面,需要配合 thymeleaf 使用

Java 支持 WebSocket

添加 spring-boot-starter-websocket 依赖

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

定义监听端点,和监听事件(与前端的监听事件相同)

@ServerEndpoint("/webSocket/{sid}")
@Component
public class WebSocketServer {
    // 事件
     @OnOpen
     public void onOpen(Session session, @PathParam("sid") String sid) {}
     @OnMessage
     public void onMessage(Session session, String message) {}
     @OnError
     public void onError(Session session, Throwable error) {}
     @OnClose
     public void onClose() {}
    // 方法
    private Session session;    // 客户端连接对象
    public void fun(){
        // 发送消息
        this.session.getBasicRemote().sendText(message);
        // 关闭连接
        this.session.close();
    }
}

问题 1

index.html:22 WebSocket connection to ‘ws://127.0.0.1:8080/webSocket/10000’ failed: Error during WebSocket handshake: Unexpected response code: 404

在使用 springboot 内置的 Tomcat 容器时,需要注入 ServerEndpointExporter,这个 bean 中的 registerEndpoints() 方法会将 @ServerEndpoint 注解的 bean 添加到 serverContainer 容器中。

ServerEndpointExporter 是由 spring-boot-starter-websocket 提供的,并且这个项目的子项目中包含 starter-web,所以单独做一个 websocket 时只需要依赖 starter-websocket 即可

@Configuration
public class WebSocketConfig {
    /**
     * 使用内置 tomcat 需要定义 bean
     * 外置则不需要,否则会报错
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

如果还是失败,需要检查是否将这个类注册为 bean。需要添加 @Component 注解,@ServerEndpoint 不会生成一个 bean,它是 Tomcat 中的注解,表示服务监听端点的地址。

@ServerEndpoint("/webSocket/{sid}")
@Component
public class WebSocketServer {}

因为 ServerEndpointExporter 注册时是从所有 bean 中查找

protected void registerEndpoints() {
    String[] endpointBeanNames = context.getBeanNamesForAnnotation(ServerEndpoint.class);
}

问题 2

连接错误 导致 连接断开

在服务端的 onMessage 方法中抛出未捕获异常会触发 onError 事件,然后触发 onClose 事件。最终也会触发客户端的 onClose 事件。

客户端 onMessage 方法中异常不会触发其它监听事件。

案例源码

码云:源码参考传送门

  • 实现当前在线人数展示
  • 参考 WebController 中的 sendGroup 可以改造为客户端主动消息推送
  • 参考 群发消息可以改造为群聊和单独聊
    在这里插入图片描述

参考:

  • 10
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值