👉 这是一个或许对你有用的社群
🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料:
👉这是一个或许对你有用的开源项目
国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。
功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:
  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本 

一、  简介

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允许服务器主动向客户端发送数据,可能会存在安全问题。服务器必须保证只向合法的客户端发送数据。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

二、 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。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

三、 在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"
)

publicclassEchoServer
{


@OnOpen
publicvoidonOpen(Session session)
{

        System.out.println(
"WebSocket 连接已经建立。"
);

    }


@OnMessage
publicvoidonMessage(String message, Session session)throws IOException 
{

        System.out.println(
"收到客户端消息:"
 + message);

        session.getBasicRemote().sendText(
"服务器收到消息:"
 + message);

    }


@OnClose
publicvoidonClose()
{

        System.out.println(
"WebSocket 连接已经关闭。"
);

    }


@OnError
publicvoidonError(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>
<metacharset="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
publicclassEchoClient
{


private
 Session session;


@OnOpen
publicvoidonOpen(Session session)
{

        System.out.println(
"WebSocket 连接已经建立。"
);

this
.session = session;

    }


@OnMessage
publicvoidonMessage(String message, Session session)
{

        System.out.println(
"收到服务器消息:"
 + message);

    }


@OnClose
publicvoidonClose()
{

        System.out.println(
"WebSocket 连接已经关闭。"
);

    }


@OnError
publicvoidonError(Throwable t)
{

        System.out.println(
"WebSocket 连接出现错误:"
 + t.getMessage());

    }


publicvoidconnect(String url)throws Exception 
{

        WebSocketContainer container = ContainerProvider.getWebSocketContainer();

        container.connectToServer(
this
new
 URI(url));

    }


publicvoidsend(String message)throws IOException 
{

        session.getBasicRemote().sendText(message);

    }


publicvoidclose()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"
)

publicclassWebSocketServer
{


@OnOpen
publicvoidonOpen(Session session)
{

        System.out.println(
"Connection opened: "
 + session.getId());

        sessions.add(session);

    }


@OnMessage
publicvoidonMessage(Session session, String message)throws IOException 
{

        System.out.println(
"Received message: "
 + message);

        session.getBasicRemote().sendText(
"Server received: "
 + message);

    }


@OnClose
publicvoidonClose(Session session)
{

        System.out.println(
"Connection closed: "
 + session.getId());

        sessions.remove(session);

    }


privatestaticfinal
 Set<Session> sessions = Collections.synchronizedSet(
new
 HashSet<Session>());

}

处理WebSocket消息
@OnMessage方法中,可以处理WebSocket客户端发送的消息,并向客户端发送响应。下面是一个简单的示例代码:
@OnMessage
publicvoidonMessage(Session session, String message)throws IOException 
{

    System.out.println(
"Received message: "
 + message);

    session.getBasicRemote().sendText(
"Server received: "
 + message);

}

在此代码中,我们简单地打印出收到的消息,并向客户端发送响应。
关闭WebSocket连接
@OnClose方法中,可以删除连接并做一些清理工作。下面是一个示例代码:
@OnClose
publicvoidonClose(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
publicclassWebSocketConfigimplementsWebSocketConfigurer
{

@Override
publicvoidregisterWebSocketHandlers(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(
newbyte
[] { 
8
9
10
 });

session.getBasicRemote().sendPing(pingMessage);

要接收Pong消息,请在您的WebSocket处理程序中实现onPong()方法。当您的WebSocket服务器接收到Pong消息时,它将自动调用此方法,并将接收到的Pong消息作为ByteBuffer对象传递给它。
下面是实现onPong()方法的示例代码:
@OnMessage
publicvoidonPong(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
publicvoidonClose(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 的性能和可伸缩性。

欢迎加入我的知识星球,全面提升技术能力。
👉 加入方式,长按”或“扫描”下方二维码噢
星球的内容包括:项目实战、面试招聘、源码解析、学习路线。
文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
继续阅读
阅读原文