WebSocket详解:服务器客户端双向通信技术
WebSocket详解:服务器客户端双向通信技术
WebSocket是一种在基于TCP连接上进行全双工通信的协议,它允许服务器和客户端之间进行双向实时通信。本文将详细介绍WebSocket的基本概念、客户端和服务端的API使用方法,以及如何在Spring Boot项目中实现WebSocket通信。
1. 消息推送常用方式介绍
轮询
浏览器以指定的时间间隔向服务器发出HTTP请求,服务器实时返回数据给浏览器。
长轮询
浏览器发出ajax请求,服务器端接收到请求后,会阻塞请求直到有数据或者超时才返回。
SSE
server-sent-event:服务器发送事件
SSE是在服务器和客户端之间打开一个单向通道,服务器通向客户端。
服务器响应的不再是一次性的数据包,而是
text/event-stream
类型的数据流信息。
服务器有数据变更时,将数据流式传输到客户端。
2. WebSocket
2.1 介绍
WebSocket是一种在基于TCP连接上进行全双工通信的协议。
说明:
- 全双工:允许数据在两个方向上同时传输。
- 半双工:允许数据在两个方向上传输,但是同一个时间段内只允许一个方向上传输。
2.2 客户端API
websocket对象创建
let ws = new WebSocket(URL);
URL说明
- 格式:协议://ip地址:端口/访问路径
- 协议:协议名称为
ws
websocket对象相关事件
事件 事件处理程序 描述
open ws.onopen 连接建立时
message ws.onmessage 客户端接受到服务器发送到数据时触发
close ws.onclose 连接关闭时触发
error ws.onerror 发生错误时触发
websocket对象提供的方法
send()
:通过websocket对象调用该方法发送数据给服务端。
<script>
let ws = new WebSocket("ws://localhost:8080/chat")
ws.onopen = function (){
}
ws.onmessage = function (evt) {
console.log(evt)
}
ws.onclose = function () {
}
ws.onerror = function (){
}
</script>
2.3 服务端API
Tomcat的7.0.5版本开始支持websocket,并且实现了Java websocket规范。
Java websocket应用由一系列的
Endpoint
组成。
Endpoint
是一个java对象,代表WebSocket链接的一端,对于服务端,我们可以视为处理具体websocket消息的接口。
我们可以通过两种方式定义Endpoint:
- 第一种是编程式,即继承类
javax.websocket.Endpoint
并实现其方法。 - 第二种是注解式,即定义一个POJO,并添加
@ServerEndpoint
相关注解。
Endpoint
实例在WebSocket握手时创建,并在客户端与服务端链接过程中有效,最后在链接关闭时结束。在Endpoint接口中明确定义了与其生命周期相关的方法,规范实现者确保生命周期的各个阶段调用实例的相关方法。生命周期方法如下:
方法 描述 注解
onOpen() 当开启一个新的会话时调用,该方法是客户端与服务器端握手成功后调用的方法 @OnOpen
onClose() 当会话关闭时调用 @OnClose
onError() 当连接过程异常时调用 @OnError
服务器端接受客户端数据
编程式
通过添加
MessageHandler
消息处理器来接收消息
注解式
在定义
Endpoint
时,通过
@OnMessage
注解指定接收消息的方法
服务器端推送数据到客户端
发送消息则由
RemoteEndpoint
完成,其实例由
Session
维护。
发送消息有2种方式
- 通过
session.getBasicRemote
获取同步消息发送的实例,然后调用其
sendXXX()
方法发送消息。 - 通过
session.getAsyncRemote
获取异步消息发送实例,然后调用其
sendXXX()
方法发送消息。
@ServerEndpoint("/chat")
@Component
public class ChatEndpoint {
@OnOpen
public void onOPen(Session session,EndPointConfig config){
}
@OnMessage
public void onMessage(String message){
}
@OnClose
public void onClose(Session session){
}
}
3. 总结
新建SpringBoot项目,导入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
编写配置类,扫描所有添加
@ServerEndpoint
注解的
Bean
:
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
编写配置类,用户获取
HttpSession
对象
@Configuration
public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
HttpSession session = (HttpSession) request.getHttpSession();
// 将HttpSession对象存储到配置对象中
sec.getUserProperties().put(HttpSession.class.getName(), session);
}
}
在
@ServerEndpoint
注解中引入配置器
@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfigurator.class)
创建
ChatEndPoint
@Component
@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfigurator.class)
public class ChatEndpoint {
private static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();
private HttpSession httpSession;
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
}
public void broadcastAllUser(){
}
@OnMessage
public void onMessage(String message, Session session) {
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
}
}
服务器向客户端发送消息:
session.getAsyncRemote().sendText("...");
客户端向服务器发送消息:
let ws = new WebSocket("ws://localhost:8080/chat")
ws.send("xxx");