WebSocket
常见的消息推送
轮询(Polling):在轮询中,客户端定期向服务器发送请求以获取更新。这种方法会导致频繁的请求和响应,即使数据没有变化,也会消耗带宽和服务器资源。
长轮询(Long Polling):长轮询是一种改进的轮询技术,客户端发送请求到服务器,服务器保持连接打开直到有新数据可用。一旦有新数据,服务器会立即响应客户端,然后客户端立即发起新请求。这种方法减少了不必要的轮询,但仍然会消耗资源。
SSE(Server-Sent Events):SSE 是一种基于 HTTP 的实时通信技术,允许服务器向客户端推送数据。通过单向连接,服务器可以异步地向客户端发送数据。SSE 是一种轻量级的实时通信方式,适用于需要服务器向客户端推送实时数据的场景。
WebSocket:WebSocket 是一种全双工通信协议,允许客户端和服务器之间建立持久连接,并进行实时、双向通信。与HTTP不同,WebSocket连接一旦建立,双方可以直接发送消息而无需每次请求。WebSocket适用于需要实时、双向通信的应用,如在线聊天、实时游戏等。
半双工(Full Duplex):允许数据在两个方向上传输,但是同一时间段内只允许一个方向上传输
全双工(Half Duplex):允许数据在两个方向上同时传输
示例图


WebSocket API
客户端(游览器)API
基于HTML5
// 创建对象
let ws = new WebSocket(URL);
格式:协议://ip地址/访问路径
协议:名为ws
事件
事件 | 处理程序 | 描述 |
---|---|---|
open | ws.onopen | 连接建立时触发 |
message | ws.onmessage | 接收到服务器消息时触发 |
close | ws.onclose | 连接关闭时触发 |
error | ws.onerror | 发生错误时触发 |
方法
方法 | 描述 |
---|---|
send() | 通过ws对象条用方法向服务端发送数据 |
示例
// 创建websocket
let ws = new WebSocket("ws://127.0.0.1:8080");
// 建立连接
ws.onopen = () => {
// 连接成功做出的事情
};
// 接收消息
ws.onmessage = (event) => {
// 通过 event.data 接收消息
};
// 断开连接
ws.onclose = () => {
// 断开连接做出的事情
};
// 发生错误
ws.onerror = () => {
// 发生错误做出的事情
};
// 发送信息
ws.send("hello");
服务端 API
Tomcat 从7.5版本开始支持WebSocket,并实现了Java webSocket规范
Java WebSocket应用由一系列的Endpoint组成;Endpoint 是一个java对象,代表WebSocket链接的一端,对于服务端,可以视为处理具体WebSocket消息的接口
两种方法实现Endpoint:继承和注解
继承:javax.websocket.Endpoint
注解:定义一个POJO,添加@ServerEndpoint
Endpoint实例在WebSocket握手时创建,并在客户端与服务端链接过程中有效,最后在链接关闭时结束
生命周期方法
方法 | 注解 | 描述 |
---|---|---|
onOpen() | @OnOpen | 当开启一个新会话时调用 该方法是客户端与服务端握手成功后掉用 |
onClose() | @OnClose | 会话关闭时调用 |
onMessage() | @OnMessage | 前端传过来消息调用 |
onError() | @OnError | 连接异常时调用 |
接收客户端消息
实现:实现MessageHandler接口来接收消息
注解:在定义Endpoint时,通过@OnMessage注解指定接收消息的方法
向客户端发送消息
发送消息则由 RemoteEndpoint 完成,其实例由 Session 维护,两种发送方式
- 通过session.getBasicRemote 获取同步消息发送的实例,然后调用其 sendXxx() 方法发送消息
- 通过session.getAsyncRemote 获取异步消息发送的实例,然后调用其 sendXxx() 方法发送消息
示例
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
@ServerEndpoint("/myEndpoint")
@Component
public class MyEndpoint {
// 初始化时调用
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
}
// 接收客户端消息
@OnMessage
public void onMessage(String message) {
}
// 关闭连接时调用
@OnClose
public void onClose(Session session) {
}
}
SpringBoot整合配置
<!-- 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置类:
@Configuration // 配置类
public class WebSocketConfig {
@Bean
// 注入ServerEndpointExporter,该Bean会自动注册使用了@ServerEndpoint注解声明的websocket endpoint
public ServerEndpointExporter myServerEndpointExporter() {
return new ServerEndpointExporter();
}
}
每一个前端建立连接都会有一个session
注入Bean
使用注解
因为Spring管理的Bean是单例的,而WebSocket是多个对象,所以想要注入就得把被注入的实例变成静态的,让这个实例属于类如:private static ObjectMapper json;
这让只需要通过set方式注入即可
@Resource
public void setObjectMapper(ObjectMapper json) {
WS.json = json;
}
使用类
除了以上的注入方式之外还可以使用配置类进行注册
import jakarta.annotation.Nonnull;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @description: 用于在不是Spring管理的类中使用IOC容器数据
* @author: 曦暮流年
*/
@Component
public class SpringBeanContext implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(@Nonnull ApplicationContext applicationContext) throws BeansException {
SpringBeanContext.applicationContext = applicationContext;
}
/**
* 根据名称获取数据
*
* @param clazz bean类型
* @param <T> bean类型
* @return bean对象
*/
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
}
使用这种方法就可以直接容spring容器中获取实例注入