WebSocket

简介
1.1 什么是WebSocket
WebSocket 是一种协议,用于在Web应用程序和服务器之间建立 实时
、双向
、持久化
的通信连接。它通过一个单一的 TCP
连接提供了持久化连接,这使得Web应用程序可以更加实时地传递数据。WebSocket 协议最初是由W3C开发,并于2011年成为标准。
补充说明:
WebSocket 是通过 HTTP 协议的升级机制 来建立的,但连接一旦建立,就脱离了 HTTP 协议,成为纯粹的 TCP 连接,这种持久化连接减少了因不断建立和关闭连接所带来的延迟。
1.2 WebSocket的优劣势
- 优势:
- **实时性:**由于WebSocket的持久化连接,它可以实现实时的数据传输,避免了Web应用程序需要不断地发起请求以获取最新数据的情况。特别适合实时更新的应用场景,如聊天应用、股票行情和在线游戏等。
- **双向通信:**WebSocket协议支持双向通信,这意味着服务器可以主动向客户端发起数据,而不需要客户端发送请求。这种双向通信是
全双工
的,允许服务器和客户端在任意时间互相发送消息。 - **减少网络负载:**由于WebSocket的持久化连接,它可以减少HTTP请求的次数,从而减少网络的负载。 WebSocket 仅在最初建立连接时会有一次握手请求,之后的数据传输都通过该连接完成,避免了频繁的请求开销。
- 劣势:
- **需要浏览器和服务器都支持:**WebSocket是一种相对新的技术,需要浏览器和服务器都支持。一些旧的浏览器和服务器可能不支持WebSocket。【目前的主流浏览器和服务器都已支持】
- **需要额外的开销:**WebSocket需要在服务器上维护长时间的连接,对于每个客户端都要保留一定的资源,则需要额外的开销,包括内存和CPU。特别是对于大规模连接的场景,可能需要较高的硬件资源支持。
- **安全问题:**由于WebSocket允许服务器主动向客户端发送数据,可能会存在安全问题。服务器必须保证只向合法的客户端发送数据。因为WebSocket 在建立连接后不再通过 HTTP 头部进行每次身份验证,因此服务器在实现中需要确保连接的安全性,例如通过加密连接(wss://)以及身份验证、IP 过滤等方法。
WebSocket的基本概念
2.1 WebSocket的协议
WebSocket 是一种基于TCP的协议,用于在客户端和服务端之间建立持久连接,并且可以在这个连接上实时地交换数据。WebSocket协议有自己的握手过程,用于建立连接,也有自己的数据传输格式。
- 握手过程:WebSocket的连接通过一个HTTP升级请求启动。客户端发送带有
Upgrade
头的HTTP请求,服务器接收到请求后返回状态码101 Switching Protocols
来确定协议升级成功并切换到 WebSocket 协议。握手过程中,客户端和服务器会协商协议版本、支持的子协议、拓展选项等。一旦握手完成,连接将保持打开状态,不再使用HTTP
请求-响应模式。 - **数据帧结构:**WebSocket定义了独特的数据帧结构,用于更高效地携带数据和减少通信开销。数据帧中包含标记位,用于表示数据是否分片、数据的类型(文本或二进制)等。数据帧的结构能够适应大数据的流式传输需求。
- **双向通信:**WebSocket协议使用全双工通信,支持客户端和服务器在任意时间向对方发送数据,而不需要等待对方的请求。这种双向通信允许实时数据的推送和快速响应,是WebSocket协议的一大优势。
- 支持二进制和文本数据:WebSocket支持文本和二进制数据的传输,可以根据实际应用需求在两者之间自由转换。这使得WebSocket能够适应更多场景。
总之,WebSocket协议是一种可靠、高效、双向且持久的通信协议,特别适用于需要实时通信的Web应用,如在线游戏、股票行情、实时聊天和在线客服系统等场景。
2.2 WebSocket 的生命周期
WebSocket 生命周期描述了 WebSocket 连接从创建到关闭的过程,包括以下四个主要阶段:
连接建立阶段(Connection Establishment):客户端发送 WebSocket 握手请求,服务器接收请求并发送握手响应。如果握手成功,则建立 WebSocket 连接,并切换到 TCP 长连接状态。此过程通过
Upgrade
请求头实现 HTTP 到 WebSocket 的协议转换。连接开放阶段(Connection Open):握手完成后,WebSocket 连接进入开放状态。此时,客户端和服务器之间的连接已建立并可以互相发送数据。进入开放状态时,会触发
onopen
事件(通常在客户端的 WebSocket 实例上触发),以指示连接已准备好进行通信。连接关闭阶段(Connection Closing):连接关闭可以由客户端或服务器发起,通过发送一个关闭帧(Close Frame)来通知对方准备关闭连接。关闭帧中可包含关闭原因的代码和描述,提供了关闭连接的上下文。
连接关闭完成阶段(Connection Closed):当关闭过程完成时,WebSocket 连接彻底关闭,客户端和服务器之间的交互终止。在客户端上会触发
onclose
事件,通常用于进行清理操作和资源释放。
补充说明:
WebSocket 连接可能在任何时候意外关闭,例如因网络问题、服务器崩溃或超时而断开。因此,客户端和服务器需要监听
onclose
和onerror
事件,并在连接断开时执行相应的恢复或重连逻辑,以保证应用的可靠性和稳定性。
HTTP和WebSocket的生命周期图:
WebSocket和HTTP协议的主要区别:
- WebSocket通过持久化的连接实现了双向实时通信
- HTTP是基于请求/响应的模型,适用于较少的交互
2.3 WebSocket的消息格式
- 消息头:
- **FIN:**一个 1 位的标志位,指示消息是否是完善的。值为 1 表示这是一条完整的消息,值为 0 表示该消息只是分段消息的一部分。
- **RSV1、RSV2、RSV3:**这三个位目前未使用,通常为0,它们预留用于将来的拓展。
- **Opcode:**表示消息的类型,常见的值有:
0x1
:表示文本消息(UTF-8编码)0x2
:表示二进制消息(图片或者文件数据等)0x8
:表示连接关闭消息0x9
:表示Ping消息0xA
:表示Pong消息
- Mask:一个 1 位的标志位,指示消息是否已经经过掩码处理。客户端发送的消息总是需要掩码,而服务器接收消息时不需要
- **Payload length:**表示消息体的长度。这个字段会根据消息的长度变化,可能是7位、16位或64位长度字段,具体取决于消息体的大小
- **Masking key:**当
Mask
为 1 时,这个 32 位的字段将用于对消息体进行掩码加密。服务器使用该密钥来解码消息体,仅在消息需要解密时出现
- 消息体:
- 这部分包含了实际的传输数据,可以是文本(例如UTF-8编码的字符串)或二进制数据(如图像、视频或文件)
2.4 WebScoket的API
WebSocket API 是用于在 Web应用程序中创建和管理 WebSocket 连接的接口集合,WebSocket API 由浏览器原生支持,无需使用额外的 JavaScript库或框架,可以直接在 JavaScript 中使用。以下是一些常用的 WebSocket API:
- **WebSocket 构造函数:**WebSocket构造函数用于创建 WebSocket 对象。它接受一个 URL 作为参数,表示要连接的 WebSocket 服务器地址。通常,WebSocket 协议的 URL 使用
ws://
(非加密)或wss://
(加密)开头,类似于 HTTP 协议的 URL。
1 | let ws = new WebSocket('ws://example.com/ws'); |
- **WebSocket.send( ):**WebSocket.send( ) 方法用于向服务器发送数据。它接受一个参数,表示要发送的数据。数据可以是字符串、Blob对象、ArrayBuffer对象。
1 | ws.send('Hello, server!'); |
- **WebSocket.onopen:**WebSocket.onopen事件在WebSocket连接成功建立时触发。
1 | ws.onopen = function() { |
- **WebSocket.onmessage:**WebSocket.onmessage事件在接收到服务器发送的消息时触发。它的 event对象包含一个data属性,表示接收到的数据。
1 | ws.onmessage = function(event) { |
- **WebSocket.onerror:**WebSocket.onerror事件在WebSocket连接出现错误时触发。通过这个事件可以处理连接错误。
1 | ws.onerror = function(event) { |
- **WebSocket.onclose:**WebSocket.onclose事件在web Socket连接被关闭时触发。可以用来处理连接关闭后的清理工作。
1 | ws.onclose = function() { |
Java中使用WebSocket
Maven依赖:
1 | <dependency> |
3.1 使用Java WebSocket API 编写 WebSocket 服务端
示例代码:
1 | import javax.websocket.*; |
这个示例代码定义了一个名为“echo”的WebSocket端点,它会监听客户端发来的消息,并将处理后的消息返回给客户端。具体来说,它使用了@ServerEndpoint
注解来指定WebSocket端点的URL,使用 @OnOpen 、@OnMessage、@OnClose 和 @OnError 注解来定义 WebSocket 事件处理器。
要使用这个 WebSocket 服务端,我们需要部署它到一个支持 WebSocket 的 Web 容器中。例如,我们可以使用 Tomcat 8 或以上版本来运行它。部署完成后,我们可以使用任何支持 WebSocket 的客户端来连接这个服务端,发送消息并接收服务器的响应。【这个WebSocket 客户端通常需要我们自己实现】
HTML/JavaScript 的 WebSocket 客户端代码示例:
1 |
|
这个客户端使用了 WebSocket 构造函数来创建一个 WebSocket 对象,并指定连接的URL为我们之前部署的服务端的URL。它使用了 WebSocket 的事件处理器来处理 WebSocket 事件,例如当 WebSocket 连接成功建立时,他会像服务器发送一条消息,并在收到服务器的响应时打印出消息的内容。
3.2 使用 Java WebSocket API 编写 WebSocket 客户端
示例代码:
1 | import javax.websocket.*; |
3.3 使用Spring Boot编写WebSocket服务端
Maven依赖
1 | <dependency> |
配置WebSocket
应用程序中,需要配置WebSocket。创建一个新的Java类,并添加注解@ServerEndpoint("/websocket")
,这将指定WebSocket服务端的端点为/websocket
,也可以自行修改。
在此类中,需要实现几个方法:
1 | /** |
配置WebSocket支持
最后,需要配置Spring Boot以支持WebSocket。
1 | /** |
WebSocket的消息格式
4.1 文本消息和二进制消息
文本消息是普通的Unicode文本字符串。当WebSocket连接建立时,客户端和服务器可以通过发送文本消息来互相交换信息。服务器可以使用Session对象的getBasicRemote()
方法来向客户端发送文本消息,客户端可以使用WebSocket的send()
方法来向服务器发送文本消息。
getBasicRemote()具体用法:
sendText(String message)
: 发送一个文本消息给客户端。sendBinary(ByteBuffer data)
: 发送二进制数据到客户端。flush()
: 刷新缓冲区,确保数据发送。close()
: 关闭 WebSocket 连接
下面是向客户端发送文本消息的示例代码:
1 | session.getBasicRemote().sendText("Hello, client!"); |
二进制消息可以是任意类型的数据,包括图像、音频、视频等。要向客户端发送二进制消息,服务器可以使用Session对象的getBasicRemote()
方法,将消息作为ByteBuffer对象发送。客户端可以使用WebSocket的send()方法来向服务器发送二进制消息。
下面是向客户端发送二进制消息的示例代码:
1 | byte[] data = // binary data |
请注意,尽管文本消息和二进制消息在格式上有所不同,但它们都是通过WebSocket发送的消息类型,因此客户端和服务器都需要能够处理这两种类型的消息。
4.2 Ping和Pong消息
WebSocket还支持Ping和Pong消息类型,用于检测WebSocket连接是否仍然处于活动状态。Ping消息由客户端发送到服务器,Pong消息由服务器发送回客户端作为响应。如果客户端在一段时间内没有收到Pong消息,则它可以假定WebSocket连接已断开,并关闭连接。
要发送Ping消息,请使用Session对象的getBasicRemote()
方法,并将Ping消息作为ByteBuffer对象发送。客户端可以使用WebSocket的sendPing()
方法来向服务器发送Ping消息。
下面是向客户端发送Ping消息的示例代码:
1 | ByteBuffer pingMessage = ByteBuffer.wrap(new byte[] { 8, 9, 10 }); |
要接收Pong消息,请在您的WebSocket处理程序中实现onPong()
方法。当您的WebSocket服务器接收到Pong消息时,它将自动调用此方法,并将接收到的Pong消息作为ByteBuffer对象传递给它。
下面是实现onPong()方法的示例代码:
1 |
|
请注意,Ping和Pong消息通常用于WebSocket连接的健康检查。如果您希望在WebSocket连接中使用此功能,则应定期发送Ping消息并等待Pong消息的响应。
4.3 关闭消息
WebSocket还支持关闭消息类型,用于关闭WebSocket连接。关闭消息可以由客户端或服务器发起,并且可以携带一个可选的状态码和关闭原因。当WebSocket连接关闭时,客户端和服务器都应该发送一个关闭消息以结束连接。
要发送关闭消息,请使用Session对象的getBasicRemote()
方法,并调用它的sendClose()
方法。关闭消息可以携带一个可选的状态码和关闭原因。如果您不希望发送状态码或关闭原因,则可以将它们设置为0和null。
下面是向客户端发送关闭消息的示例代码:
1 | session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Closing from client.")); |
要处理接收到的关闭消息,请在您的WebSocket处理程序中实现onClose()
方法。当您的WebSocket服务器接收到关闭消息时,它将自动调用此方法,并将接收到的状态码和关闭原因传递给它。
下面是实现onClose()
方法的示例代码:
1 |
|
请注意,客户端和服务器都应该发送关闭消息以结束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 的性能和可伸缩性。
- Title: WebSocket
- Author: Lu
- Created at : 2024-07-16 11:22:05
- Updated at : 2024-07-16 12:45:03
- Link: https://lusy.ink/2024/07/16/Websocket/
- License: This work is licensed under CC BY-NC-SA 4.0.