WebSocket 是一种在 Web 应用程序中实现实时、双向通信的技术。它允许客户端和服务器之间建立持久性的连接,以便可以在两者之间双向传输数据。
以下是 WebSocket 的一些关键特点和工作原理:
0.特点:
-
双向通信:WebSocket 允许服务器和客户端之间进行双向通信,无需客户端发起请求。
-
实时性:WebSocket 提供了实时性通信的能力,使得服务器可以立即将数据推送给客户端,而无需等待客户端发起请求。
-
持久性连接:与传统的 HTTP 请求-响应模式不同,WebSocket 的连接是持久性的,一旦建立连接,它将保持打开状态,直到其中一方主动关闭连接。
-
低延迟:由于 WebSocket 使用单一的 TCP 连接,相较于传统的轮询或长轮询方式,它能够减少通信的延迟。
-
节省带宽:WebSocket 的头部较小,且在通信过程中不需要重复的 HTTP 头部,因此可以节省带宽。
1.工作原理:
-
握手阶段:客户端发起 WebSocket 握手请求,请求头部包含一些特定的字段,如 Upgrade 和 Connection 等。服务器收到请求后,如果支持 WebSocket 协议,会返回 101 状态码,表示协议切换成功,然后连接升级为 WebSocket 连接。
-
建立连接:建立连接后,服务器和客户端可以通过发送消息进行通信。每个消息都被分割成一个或多个帧进行传输。
-
数据传输:WebSocket 数据以帧的形式传输。帧可以是文本、二进制数据或控制帧。文本帧用于传输文本数据,而二进制帧用于传输二进制数据。控制帧用于控制连接,例如关闭连接或心跳检测。
-
保持活动状态:WebSocket 连接可以保持活动状态,无需在每次通信后重新建立连接。服务器和客户端可以周期性地发送心跳消息来保持连接的活跃状态。
-
关闭连接:任何一方都可以通过发送关闭帧来关闭连接。关闭帧包含一个状态码和一个可选的关闭消息。收到关闭帧后,双方都应该关闭连接。
WebSocket 在现代 Web 应用程序中被广泛应用于实时通信、在线游戏、实时协作等场景,它提供了一种高效且可靠的双向通信方式,为开发者提供了更多创新的可能性。
2. 基于 Java 实现 WebSocket
- 服务端的类
- 监听连接——@ServerEndpoint
- 连接成功——@OnOpen
- 连接关闭——@OnClose
- 收到消息状态——@OnMessage
(一)方式一:通过注解实现
实现了一个基于WebSocket的双向通信系统
其中服务器端配置了 WebSocket 并实现了消息处理和定时推送
客户端页面则使用 JavaScript 创建WebSocket连接 并实现消息发送和接收。
1.项目依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.WsServerEndpoint
/**
* 监听webSocket地址 /myWs
* 即:监听谁连接了 客户端
*/
/**
* 定义了一个 WebSocket 服务器端点的类。它使用了 @ServerEndpoint 来指定 WebSocket 的端点路径为 "/myWs"。
*
* 这个类也使用了 @Component 注解,将这个类标记为 Spring 的组件,以便 Spring 自动扫描并注册它。
*
* 另外,使用了 @EnableScheduling 注解用于开启定时任务功能。
*/
@ServerEndpoint("/myWs") // 监听连接
@Component
@Slf4j
@EnableScheduling
public class WsServerEndpoint {
static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
//连接建立时 进行的操作
//@OnOpen: 用于标记连接成功时执行的方法,将建立的 Session 对象存储在 sessionMap 中,并记录日志表示 WebSocket 已连接。
@OnOpen // 连接成功
public void onOpen(Session session){
sessionMap.put(session.getId(), session);
log.info("websocket is open");
}
// 收到客户端消息时 进行的操作
//@OnMessage: 用于标记接收到消息时执行的方法,打印收到的消息并返回一条确认消息。
@OnMessage // 收到消息状态
public String OnMessage(String text){
log.info("收到了一条消息:"+text);
return "已收到你的消息";
}
// 连接关闭的时候 执行的操作
//@OnClose: 用于标记连接关闭时执行的方法,从 sessionMap 中移除对应的 Session 对象,并记录日志表示 WebSocket 已关闭。
@OnClose
public void onClose(Session session){
sessionMap.remove(session.getId());
log.info("websocket is close");
}
// 服务端想给 客户端发送数据的话
// ===》 定时任务实现 (每隔多少秒执行一次)
//@Scheduled: 使用定时任务(通过 @Scheduled 注解)每隔两秒向所有的客户端发送一条消息。
@Scheduled(fixedRate = 2000)
public void sendMsg() throws IOException {
for (String key:sessionMap.keySet()){
sessionMap.get(key).getBasicRemote().sendText("心跳");
}
}
}
3.WebSocketConfig
/**
* 定义了一个 Spring @Configuration 类,并且使用了 @Bean 注解向 Spring 容器注册了一个 ServerEndpointExporter 的实例
*
* 这个 ServerEndpointExporter 实例是用于在 Spring Boot 中配置和注册 WebSocket 服务器端点的组件。
*/
@Configuration
public class WebSocketConfig {
// 将依赖包中的一些 依赖 注入为Bean
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
4.ws.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ws client</title>
</head>
<body>
</body>
<!--通过 JavaScript 创建了一个 WebSocket 客户端连接到指定路径 "/myWs"(与服务器端点匹配)。
当连接成功建立时,会发送一条消息 "hello,phdvb";
同时当接收到消息时,将消息数据打印到控制台。-->
<script>
let ws = new WebSocket("ws://localhost:8080/myWs")
//当连接打开的时候 想服务端发送消息
ws.onopen = function() {
ws.send("hello,phdvb")
}
ws.onmessage = function(message) {
console.log(message.data)
}
</script>
</html>
实现效果
(二)方式二:通过Spring类+接口实现
3.基于websocket实现多人聊天室
- 实现的功能
- 进入聊天室
- 群聊功能,任何人说话,所有人都能接收到提醒
- 退出群聊
4.websocket的应用场景
解决怎样的经典问题?
Q: 由于Http协议,只能由浏览器向服务器发送请求,服务器无法直接向浏览器发送请求
常见的提点方案:浏览器以轮询的方式向服务器发送请求
轮询的缺陷:浪费带宽,实时性差,导致服务器压力较大
PC端二维码支付:
https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay
/chapter2_7_2.shtml