⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/bairo007/article/details/131779053 「青年老年程序员」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

一、 简介

1.1 什么是WebSocket

WebSocket是一种协议,用于在Web应用程序和服务器之间建立实时、双向的通信连接。它通过一个单一的TCP连接提供了持久化连接,这使得Web应用程序可以更加实时地传递数据。WebSocket协议最初由W3C开发,并于2011年成为标准。

1.2 WebSocket的优势和劣势

WebSocket的优势包括:

  • 实时性:由于WebSocket的持久化连接,它可以实现实时的数据传输,避免了Web应用程序需要不断地发送请求以获取最新数据的情况。
  • 双向通信:WebSocket协议支持双向通信,这意味着服务器可以主动向客户端发送数据,而不需要客户端发送请求。
  • 减少网络负载:由于WebSocket的持久化连接,它可以减少HTTP请求的数量,从而减少了网络负载。

WebSocket的劣势包括:

  • 需要浏览器和服务器都支持:WebSocket是一种相对新的技术,需要浏览器和服务器都支持。一些旧的浏览器和服务器可能不支持WebSocket。
  • 需要额外的开销:WebSocket需要在服务器上维护长时间的连接,这需要额外的开销,包括内存和CPU。
  • 安全问题:由于WebSocket允许服务器主动向客户端发送数据,可能会存在安全问题。服务器必须保证只向合法的客户端发送数据。

二、 WebSocket的基本概念

2.1 WebSocket的协议

WebSocket 协议是一种基于TCP的协议,用于在客户端和服务器之间建立持久连接,并且可以在这个连接上实时地交换数据。WebSocket协议有自己的握手协议,用于建立连接,也有自己的数据传输格式。

当客户端发送一个 WebSocket 请求时,服务器将发送一个协议响应以确认请求。在握手期间,客户端和服务器将协商使用的协议版本、支持的子协议、支持的扩展选项等。一旦握手完成,连接将保持打开状态,客户端和服务器就可以在连接上实时地传递数据。

WebSocket 协议使用的是双向数据传输,即客户端和服务器都可以在任意时间向对方发送数据,而不需要等待对方的请求。它支持二进制数据和文本数据,可以自由地在它们之间进行转换。

总之,WebSocket协议是一种可靠的、高效的、双向的、持久的通信协议,它适用于需要实时通信的Web应用程序,如在线游戏、实时聊天等。

2.2 WebSocket的生命周期

WebSocket 生命周期描述了 WebSocket 连接从创建到关闭的过程。一个 WebSocket 连接包含以下四个主要阶段:

  • 连接建立阶段(Connection Establishment):在这个阶段,客户端和服务器之间的 WebSocket 连接被建立。客户端发送一个 WebSocket 握手请求,服务器响应一个握手响应,然后连接就被建立了。
  • 连接开放阶段(Connection Open):在这个阶段,WebSocket 连接已经建立并开放,客户端和服务器可以在连接上互相发送数据。
  • 连接关闭阶段(Connection Closing):在这个阶段,一个 WebSocket 连接即将被关闭。它可以被客户端或服务器发起,通过发送一个关闭帧来关闭连接。
  • 连接关闭完成阶段(Connection Closed):在这个阶段,WebSocket 连接已经完全关闭。客户端和服务器之间的任何交互都将无效。

“

需要注意的是,WebSocket 连接在任何时候都可能关闭,例如网络故障、服务器崩溃等情况都可能导致连接关闭。因此,需要及时处理 WebSocket 连接关闭的事件,以确保应用程序的可靠性和稳定性。

下面是一个简单的 WebSocket 生命周期示意图:

在这个示意图中,客户端向服务器发送一个 WebSocket 握手请求,服务器响应一个握手响应,连接就被建立了。一旦连接建立,客户端和服务器就可以在连接上互相发送数据,直到其中一方发送一个关闭帧来关闭连接。在关闭帧被接收后,连接就会被关闭,WebSocket 连接关闭完成。

2.3 WebSocket的消息格式

WebSocket 的消息格式与 HTTP 请求和响应的消息格式有所不同。WebSocket 的消息格式可以是文本或二进制数据,并且 WebSocket 消息的传输是在一个已经建立的连接上进行的,因此不需要再进行 HTTP 请求和响应的握手操作。

WebSocket 消息格式由两个部分组成:消息头和消息体。

消息头包含以下信息:

  • FIN:表示这是一条完整的消息,一般情况下都是1。
  • RSV1、RSV2、RSV3:暂时没有使用,一般都是0。
  • Opcode:表示消息的类型,包括文本消息、二进制消息等。
  • Mask:表示消息是否加密。
  • Payload length:表示消息体的长度。
  • Masking key:仅在消息需要加密时出现,用于对消息进行解密。

消息体就是实际传输的数据,可以是文本或二进制数据。

2.4 WebSocket的API

WebSocket API 是用于在 Web 应用程序中创建和管理 WebSocket 连接的接口集合。WebSocket API 由浏览器原生支持,无需使用额外的 JavaScript 库或框架,可以直接在 JavaScript 中使用。

下面是一些常用的 WebSocket API:

WebSocket 构造函数:WebSocket 构造函数用于创建 WebSocket 对象。它接受一个 URL 作为参数,表示要连接的 WebSocket 服务器的地址。例如:

let ws = new WebSocket('ws://example.com/ws');

WebSocket.send() 方法:WebSocket.send()方法用于向服务器发送数据。它接受一个参数,表示要发送的数据。数据可以是字符串、Blob 对象或 ArrayBuffer 对象。例如:

ws.send('Hello, server!');

WebSocket.onopen 事件:WebSocket.onopen事件在 WebSocket 连接成功建立时触发。例如:

ws.onopen = function() {
console.log('WebSocket 连接已经建立。');
};

WebSocket.onmessage 事件:WebSocket.onmessage事件在接收到服务器发送的消息时触发。它的 event 对象包含一个 data 属性,表示接收到的数据。例如:

ws.onmessage = function(event) {
console.log('收到服务器消息:', event.data);
};

WebSocket.onerror 事件:WebSocket.onerror事件在 WebSocket 连接出现错误时触发。例如:

ws.onerror = function(event) {
console.error('WebSocket 连接出现错误:', event);
};

WebSocket.onclose 事件:WebSocket.onclose事件在 WebSocket 连接被关闭时触发。例如:

ws.onclose = function() {
console.log('WebSocket 连接已经关闭。');
};

以上是一些常用的 WebSocket API。

三、 在Java中使用WebSocket

依赖:

<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>

3.1 使用Java WebSocket API编写WebSocket服务端

下面是一个使用 Java WebSocket API 编写 WebSocket 服务端的示例代码:

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

@ServerEndpoint("/echo")
public class EchoServer {

@OnOpen
public void onOpen(Session session) {
System.out.println("WebSocket 连接已经建立。");
}

@OnMessage
public void onMessage(String message, Session session) throws IOException {
System.out.println("收到客户端消息:" + message);
session.getBasicRemote().sendText("服务器收到消息:" + message);
}

@OnClose
public void onClose() {
System.out.println("WebSocket 连接已经关闭。");
}

@OnError
public void onError(Throwable t) {
System.out.println("WebSocket 连接出现错误:" + t.getMessage());
}
}

这个示例代码定义了一个名为 “echo” 的 WebSocket 端点,它会监听客户端发来的消息,并将收到的消息返回给客户端。具体来说,它使用了@ServerEndpoint注解来指定 WebSocket 端点的 URL,使用了@OnOpen、@OnMessage、@OnClose和@OnError注解来定义 WebSocket 事件处理器。

要使用这个 WebSocket 服务端,我们需要部署它到一个支持 WebSocket 的 Web 容器中。例如,我们可以使用 Tomcat 8 或以上版本来运行它。在部署完成后,我们可以使用任何支持 WebSocket 的客户端来连接这个服务端,发送消息并接收服务器的响应。

例如,下面是一个简单的 HTML/JavaScript 客户端代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Demo</title>
<script>
var ws = new WebSocket('ws://localhost:8080/echo');
ws.onopen = function() {
console.log('WebSocket 连接已经建立。');
ws.send('Hello, server!');
};
ws.onmessage = function(event) {
console.log('收到服务器消息:', event.data);
};
ws.onerror = function(event) {
console.error('WebSocket 连接出现错误:', event);
};
ws.onclose = function() {
console.log('WebSocket 连接已经关闭。');
};
</script>
</head>
<body>
<h1>WebSocket Demo</h1>
</body>
</html>

这个客户端使用了 WebSocket 构造函数来创建一个 WebSocket 对象,并指定连接的 URL 为我们之前部署的服务端的 URL。它使用了 WebSocket 的事件处理器来处理 WebSocket 事件,例如当 WebSocket 连接成功建立时,它会向服务器发送一条消息,并在收到服务器的响应时打印出消息内容。

3.2 使用Java WebSocket API编写WebSocket客户端

下面是一个使用 Java WebSocket API 编写 WebSocket 客户端的示例代码:

import javax.websocket.*;
import java.io.IOException;
import java.net.URI;

@ClientEndpoint
public class EchoClient {

private Session session;

@OnOpen
public void onOpen(Session session) {
System.out.println("WebSocket 连接已经建立。");
this.session = session;
}

@OnMessage
public void onMessage(String message, Session session) {
System.out.println("收到服务器消息:" + message);
}

@OnClose
public void onClose() {
System.out.println("WebSocket 连接已经关闭。");
}

@OnError
public void onError(Throwable t) {
System.out.println("WebSocket 连接出现错误:" + t.getMessage());
}

public void connect(String url) throws Exception {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, new URI(url));
}

public void send(String message) throws IOException {
session.getBasicRemote().sendText(message);
}

public void close() throws IOException {
session.close();
}
}

3.3 使用Spring Boot编写WebSocket服务端

创建Spring Boot项目

首先,您需要创建一个新的Spring Boot项目。可以使用Spring Initializr创建一个新项目,添加依赖项。

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

配置WebSocket

应用程序中,需要配置WebSocket。创建一个新的Java类,并添加注释@ServerEndpoint("/websocket")。这将指定WebSocket服务端的端点。

在此类中,需要实现几个方法:

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/websocket")
public class WebSocketServer {

@OnOpen
public void onOpen(Session session) {
System.out.println("Connection opened: " + session.getId());
sessions.add(session);
}

@OnMessage
public void onMessage(Session session, String message) throws IOException {
System.out.println("Received message: " + message);
session.getBasicRemote().sendText("Server received: " + message);
}

@OnClose
public void onClose(Session session) {
System.out.println("Connection closed: " + session.getId());
sessions.remove(session);
}

private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());
}

处理WebSocket消息

在@OnMessage方法中,可以处理WebSocket客户端发送的消息,并向客户端发送响应。下面是一个简单的示例代码:

@OnMessage
public void onMessage(Session session, String message) throws IOException {
System.out.println("Received message: " + message);
session.getBasicRemote().sendText("Server received: " + message);
}

在此代码中,我们简单地打印出收到的消息,并向客户端发送响应。

关闭WebSocket连接

在@OnClose方法中,可以删除连接并做一些清理工作。下面是一个示例代码:

@OnClose
public void onClose(Session session) {
System.out.println("Connection closed: " + session.getId());
sessions.remove(session);
}

在此代码中,我们从连接池中删除连接,并打印出连接已关闭的消息。

配置WebSocket支持

最后,需要配置Spring Boot以支持WebSocket。创建一个新的Java类,并添加注释@Configuration和@EnableWebSocket。然后,需要覆盖方法registerWebSocketHandlers(),并指定WebSocket处理程序。下面是一个示例代码:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new WebSocketServer(), "/websocket").setAllowedOrigins("*");
}
}

在此代码中,我们创建了一个新的WebSocketServer对象,并将其添加到WebSocket处理程序中。我们还指定了WebSocket端点(/websocket)和允许的来源(*)。

四、 WebSocket的消息格式

4.1 文本消息和二进制消息

文本消息是普通的Unicode文本字符串。当WebSocket连接建立时,客户端和服务器可以通过发送文本消息来互相交换信息。服务器可以使用Session对象的getBasicRemote()方法来向客户端发送文本消息,客户端可以使用WebSocket的send()方法来向服务器发送文本消息。

下面是向客户端发送文本消息的示例代码:

session.getBasicRemote().sendText("Hello, client!");

二进制消息可以是任意类型的数据,包括图像、音频、视频等。要向客户端发送二进制消息,服务器可以使用Session对象的getBasicRemote()方法,将消息作为ByteBuffer对象发送。客户端可以使用WebSocket的send()方法来向服务器发送二进制消息。

下面是向客户端发送二进制消息的示例代码:

byte[] data = // binary data
ByteBuffer buffer = ByteBuffer.wrap(data);
session.getBasicRemote().sendBinary(buffer);

请注意,尽管文本消息和二进制消息在格式上有所不同,但它们都是通过WebSocket发送的消息类型,因此客户端和服务器都需要能够处理这两种类型的消息。

4.2 Ping和Pong消息

WebSocket还支持Ping和Pong消息类型,用于检测WebSocket连接是否仍然处于活动状态。Ping消息由客户端发送到服务器,Pong消息由服务器发送回客户端作为响应。如果客户端在一段时间内没有收到Pong消息,则它可以假定WebSocket连接已断开,并关闭连接。

要发送Ping消息,请使用Session对象的getBasicRemote()方法,并将Ping消息作为ByteBuffer对象发送。客户端可以使用WebSocket的sendPing()方法来向服务器发送Ping消息。

下面是向客户端发送Ping消息的示例代码:

ByteBuffer pingMessage = ByteBuffer.wrap(new byte[] { 8, 9, 10 });
session.getBasicRemote().sendPing(pingMessage);

要接收Pong消息,请在您的WebSocket处理程序中实现onPong()方法。当您的WebSocket服务器接收到Pong消息时,它将自动调用此方法,并将接收到的Pong消息作为ByteBuffer对象传递给它。

下面是实现onPong()方法的示例代码:

@OnMessage
public void onPong(Session session, ByteBuffer pongMessage) {
System.out.println("Received Pong message: " + pongMessage);
}

请注意,Ping和Pong消息通常用于WebSocket连接的健康检查。如果您希望在WebSocket连接中使用此功能,则应定期发送Ping消息并等待Pong消息的响应。

4.3 关闭消息

WebSocket还支持关闭消息类型,用于关闭WebSocket连接。关闭消息可以由客户端或服务器发起,并且可以携带一个可选的状态码和关闭原因。当WebSocket连接关闭时,客户端和服务器都应该发送一个关闭消息以结束连接。

要发送关闭消息,请使用Session对象的getBasicRemote()方法,并调用它的sendClose()方法。关闭消息可以携带一个可选的状态码和关闭原因。如果您不希望发送状态码或关闭原因,则可以将它们设置为0和null。

下面是向客户端发送关闭消息的示例代码:

session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Closing from client."));

要处理接收到的关闭消息,请在您的WebSocket处理程序中实现onClose()方法。当您的WebSocket服务器接收到关闭消息时,它将自动调用此方法,并将接收到的状态码和关闭原因传递给它。

下面是实现onClose()方法的示例代码:

@OnClose
public void onClose(Session session, CloseReason closeReason) {
System.out.println("Connection closed: " + closeReason.getCloseCode() + " - " + closeReason.getReasonPhrase());
}

请注意,客户端和服务器都应该发送关闭消息以结束WebSocket连接。如果只有一方发送了关闭消息,则另一方可能无法正确地关闭连接,并且可能需要等待超时才能释放资源。建议客户端和服务器在关闭连接时都发送关闭消息,以确保连接正确地关闭。

五、 WebSocket的性能

5.1 与传统的HTTP请求/响应模型比较

  • 双向通信性能更好:WebSocket协议使用单一的TCP连接,允许客户端和服务器在同一个连接上进行双向通信。这种实时的双向通信可以更快地传输数据,而不需要建立多个HTTP请求/响应连接。
  • 更小的网络流量:与HTTP相比,WebSocket协议需要更少的网络流量来维护连接,因为它不需要在每个请求/响应交换中发送头部信息。
  • 更低的延迟:WebSocket协议允许服务器主动向客户端推送消息,而不需要客户端先发送请求。这种实时通信可以减少响应延迟,并提高应用程序的性能。
  • 更好的服务器资源管理:由于WebSocket连接可以保持活动状态,服务器可以更好地管理客户端连接,减少服务器开销和处理时间。

WebSocket协议的性能比传统的HTTP请求/响应模型更好,特别是在实时通信和低延迟方面。WebSocket协议适用于需要实时通信和实时数据更新的应用程序,如在线聊天、多人游戏、实时监控等。

5.2 优化WebSocket的性能

  • 减少消息大小:WebSocket 传输的数据大小对性能有很大影响。尽量减少消息的大小,可以降低网络带宽和服务器负载。例如,可以使用二进制传输协议来代替文本传输,或使用压缩算法对消息进行压缩。
  • 使用CDN加速:使用 CDN(内容分发网络)可以将静态资源缓存到离用户更近的节点上,提高传输速度和性能。CDN 可以缓存 Websocket 的初始握手请求,避免不必要的网络延迟。
  • 使用负载均衡:WebSocket 服务可以使用负载均衡来分配并平衡多个服务器的负载。负载均衡可以避免单个服务器被过载,并提高整个服务的可伸缩性。
  • 优化服务端代码:WebSocket 服务端代码的性能也是关键因素。使用高效的框架和算法,避免使用过多的内存和 CPU 资源,可以提高服务端的性能和响应速度。
  • 避免网络阻塞:WebSocket 的性能也会受到网络阻塞的影响。当有太多的连接同时请求数据时,服务器的性能会下降。使用合适的线程池和异步 IO 操作可以避免网络阻塞,提高 WebSocket 服务的并发性能。

六、 WebSocket的扩展应用和未来发展方向

  • 更加完善的标准规范:WebSocket 标准规范还有很多可以优化的地方,未来可能会继续完善 WebSocket 的标准规范,以适应更加复杂的应用场景。
  • 更加安全的通信方式:由于 WebSocket 的开放性,使得它可能会受到一些安全威胁,未来可能会通过加密、身份验证等方式来增强 WebSocket 的安全性。
  • 更好的兼容性:WebSocket 协议需要在 HTTP 协议的基础上建立连接,因此可能会遇到兼容性问题,未来可能会通过技术手段来解决这些问题。
  • 更好的性能和可伸缩性:WebSocket 协议的性能和可伸缩性对于复杂的应用场景非常关键,未来可能会通过技术手段来进一步提高 WebSocket 的性能和可伸缩性。
文章目录
  1. 1. 一、 简介
    1. 1.0.1. 1.1 什么是WebSocket
    2. 1.0.2. 1.2 WebSocket的优势和劣势
  • 2. 二、 WebSocket的基本概念
    1. 2.0.1. 2.1 WebSocket的协议
    2. 2.0.2. 2.2 WebSocket的生命周期
    3. 2.0.3. 2.3 WebSocket的消息格式
    4. 2.0.4. 2.4 WebSocket的API
  • 3. 三、 在Java中使用WebSocket
    1. 3.0.1. 3.1 使用Java WebSocket API编写WebSocket服务端
    2. 3.0.2. 3.2 使用Java WebSocket API编写WebSocket客户端
    3. 3.0.3. 3.3 使用Spring Boot编写WebSocket服务端
      1. 3.0.3.1. 创建Spring Boot项目
      2. 3.0.3.2. 配置WebSocket
      3. 3.0.3.3. 处理WebSocket消息
      4. 3.0.3.4. 关闭WebSocket连接
      5. 3.0.3.5. 配置WebSocket支持
  • 4. 四、 WebSocket的消息格式
    1. 4.0.1. 4.1 文本消息和二进制消息
    2. 4.0.2. 4.2 Ping和Pong消息
    3. 4.0.3. 4.3 关闭消息
  • 5. 五、 WebSocket的性能
    1. 5.0.1. 5.1 与传统的HTTP请求/响应模型比较
    2. 5.0.2. 5.2 优化WebSocket的性能
  • 6. 六、 WebSocket的扩展应用和未来发展方向