赞
踩
WebSocket 协议是一种用于实时通信、全双工的网络协议。它运行在传输层之上,通常基于 TCP 连接。相较于传统的 HTTP 协议,WebSocket 协议能够在单个连接上进行双向通信
,实现数据的实时交互
,因此在实时性较高的应用中表现得尤为出色。
WebSocket 协议是一种允许服务器和客户端之间进行全双工、实时通信的协议
。它克服了 HTTP 协议的请求-响应
模式,通过在客户端和服务器之间建立持久性的连接,实现了数据的实时传输。传统的 HTTP 连接每次请求都需要建立和关闭,而 WebSocket 连接则能够一直保持开放状态,避免了频繁的连接建立和关闭开销。
WebSocket协议的特点包括:
WebSocket 协议和 HTTP 协议之间有密切的关系,WebSocket 连接的建立需要通过 HTTP 握手来协商
,其基本流程如下:
Upgrade
字段,表示希望升级到WebSocket连接。下图展示了建立 WebSocket 连接的基本过程:
WebSocket 的握手是指在建立 WebSocket 连接时,客户端和服务器之间通过 HTTP 协议进行交互,以协商升级连接到 WebSocket 协议。WebSocket的握手过程允许服务器和客户端确认其支持WebSocket协议,并进行连接的升级。
以下是 WebSocket 握手的基本步骤:
Sec-WebSocket-Key
字段。示例请求头部:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Key
字段,并生成一个响应密钥。如果验证通过,服务器会返回一个 HTTP 响应,表示升级到 WebSocket 协议。示例响应头部:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
需要注意的是,Sec-WebSocket-Key
和 Sec-WebSocket-Accept
字段的计算是基于一种安全验证的机制。Sec-WebSocket-Key
是客户端随机生成的一个字符串,服务器会使用这个字符串进行一定的计算,然后生成Sec-WebSocket-Accept
字段,以确保握手请求的合法性。
WebSocket 握手过程的成功完成意味着客户端和服务器之间已经建立了一个 WebSocket 连接,双方可以在这个连接上进行实时的数据传输。握手是 WebSocket 通信的起始点,之后的数据交换将遵循 WebSocket 的数据帧格式进行。
WebSocket 协议解决了传统 HTTP 协议在实时性、延迟和数据开销等方面存在的问题。通过保持持久性连接和全双工通信,WebSocket 协议实现了实时数据的传输,适用于需要及时通知和数据更新的应用,如:
总之,WebSocket 协议弥补了 HTTP 协议在实时性通信方面的不足,为实时应用提供了高效、低延迟的解决方案。在接下来的内容中,我们将深入探讨 WebSocket 协议的数据格式以及在 Spring Boot 项目中的应用。
WebSocket协议的数据帧格式是用于在WebSocket连接上传输数据的基本单位。每个数据帧包含了控制信息和有效负载(Payload Data)。以下是WebSocket数据帧的格式及其各个字段的详细说明:
FIN (1 bit):表示消息是否是最后一个数据帧。若为1,则表示是消息的最后一个帧;若为0,则还有后续数据帧。
RSV1, RSV2, RSV3 (各占 1 bit):保留位,通常设置为0,用于未来的扩展。
Opcode (4 bits):指定数据帧的类型。常见的类型有:
MASK (1 bit):表示Payload Data是否经过掩码加密。客户端发送给服务器的数据帧需要加密,所以MASK位为1。
Payload length (7 bits, 7+16 bits, 或 7+64 bits):表示Payload Data的长度。
Extended payload length (16或64 bits):仅在Payload length为126或127时出现,用于表示实际的Payload Data长度。
Masking-key (32 bits):仅在MASK位为1时出现,用于解码Payload Data。Masking-key是一个32位的随机数。
Payload Data:有效负载数据。如果MASK位为1,则需要对Payload Data进行掩码解密,使用Masking-key进行解码。
WebSocket数据帧的格式允许在同一个连接上传输不同类型的数据,并且可以通过掩码保护数据的安全性。不同的Opcode类型用于标识数据的用途,例如文本数据、二进制数据、连接关闭、心跳检测等。
引入 WebSocket 依赖非常简单,只需要在创建 SpringBoot 项目的时候,勾选上 WebSocket 就行了:
要使用 WebSocket 的相关功能,需要继承 TextWebSocketHandler
类来实现我们自己的 MyWebSocketHandler
类,我创建了一个websocket
包,在里面建立一个类 MyWebSocketHandler
:
然后需要重写里面的方法。这里我重新了四个方法:
这四个方法分别代表的含义:
afterConnectionEstablished
方法:在WebSocket连接成功建立后被自动调用。
handleTextMessage
方法:在WebSocket接收到文本消息时被自动调用。常常用于收到的消息进行转发。
handleTransportError
方法:在连接出现异常时被自动调用。
afterConnectionClosed
方法:在连接正常关闭后被自动调用。
通过实现这些方法,可以控制 WebSocket 连接的行为,包括连接的建立、消息的处理、异常的处理以及连接的关闭。这使得我们能够在应用中实现 WebSocket 通信的各种逻辑。
WebSocket 代码样例:
此处只是对连接建立、收到的消息、连接断开、异常等进行了简单的打印操作。
@Service public class MyWebSocketHandler extends TextWebSocketHandler { /** * 这个方法会在 WebSocket 连接成功后,被自动调用 * * @param session WebSocket 连接中对应的会话 * @throws Exception 异常信息 */ @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { System.out.println("TestWebSocketComponent 连接成功!"); } /** * 这个方法会在 WebSocket 收到消息的时候,被自动调用 * * @param session WebSocketSession * @param message 收到消息的值 * @throws Exception 异常信息 */ @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { System.out.println("TestWebSocketComponent 收到消息!" + message.toString()); // session 是一个会话,其中记录了通信双方是谁(session 中就持有了 WebSocket 的通信连接) session.sendMessage(message); } /** * 这个方法会在连接出现异常的时候,被自动调用 * * @param session WebSocketSession * @param exception 异常信息 * @throws Exception 异常信息 */ @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { System.out.println("TestWebSocketComponent 连接异常了!"); } /** * 这个方法会在连接正常关闭后,被自动调用 * * @param session WebSocketSession * @param status 关闭的状态 * @throws Exception 异常信息 */ @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { System.out.println("TestWebSocketComponent 连接关闭!"); } }
要想使我们刚才创建的 MyWebSocketHandler
能够在建立 WebSocket 连接之后能够被调用,还需要对其进行配置,即注册该类与资源路径的映射关系。
config
包,然后在里面创建一个类 WebSocketConfig
。WebSocketConfigurer
接口,并重写 registerWebSocketHandlers
方法。注意,需要添加 @Configuration
表示其是一个配置类,并且储存到 Spring 中;另外还需添加 @EnableWebSocket
注解,表示启用WebSocket 支持,使应用能够处理 WebSocket 连接。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
/**
* 通过这个方法,把刚才创建好的 Handler 类注册到具体的路径中
*
* @param registry WebSocketHandlerRegistry
*/
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
}
}
MyWebSocketHandler
与一个具体的请求路径关联。@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private MyWebSocketHandler myWebSocketHandler; /** * 通过这个方法,把刚才创建好的 Handler 类注册到具体的路径中 * * @param registry WebSocketHandlerRegistry */ @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { // 当浏览器通过 WebSocket 请求的路径是 '/test' 的时候,就会调用到 MyWebSocketHandler 这个类中的方法 registry.addHandler(myWebSocketHandler, "/test"); } }
当建立了 WebSocket 连接并且请求的路径是 /test
的时候,就会调用到 MyWebSocketHandler
这个类中的方法。通过这个配置,将WebSocket 的处理逻辑和路径连接起来,使得 WebSocket 连接能够在应用中得到正确处理和响应。
编写一个用于测试 WebSocket 连接的简单前端页面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>测试 WebSocket 的使用</title> </head> <body> <input type="text" id="message"> <button id="send-button">发送</button> <script> // 创建一个 WebSocket 实例,连接到服务器的 /test 路径 let websocket = new WebSocket("ws://localhost:8081/test"); // WebSocket 连接建立成功的回调函数 websocket.onopen = function () { console.log("WebSocket 连接成功!"); } // WebSocket 收到消息的回调函数 websocket.onmessage = function (message) { console.log("WebSocket 收到消息:" + message.data); } // WebSocket 连接断开的回调函数 websocket.onclose = function () { console.log("WebSocket 连接断开!"); } // WebSocket 连接异常的回调函数 websocket.onerror = function () { console.log("WebSocket 连接异常!"); } // 获取页面元素 let messageInput = document.querySelector("#message"); let sendButton = document.querySelector('#send-button'); // 发送按钮的点击事件 sendButton.onclick = function(){ console.log("WebSocket 发送消息:" + messageInput.value); websocket.send(messageInput.value); // 向服务器发送消息 } </script> </body> </html>
这段代码创建了一个简单的 HTML 页面,其中包含一个输入框、一个发送按钮和一些用于 WebSocket 连接的 JavaScript 代码。具体来说:
ws://localhost:8081/test
路径。这与之前在WebSocketConfig
中注册的路径相对应。websocket.send(messageInput.value)
将用户输入的消息发送给服务器。这个前端页面用于与之前创建的 WebSocket 处理器进行通信,可以在浏览器中打开这个页面,然后在控制台中观察 WebSocket 连接状态和消息传输情况。
启动SpringBoot项目,在浏览器中通过 http://localhost:8081/test.html
进行访问:
此时可以发现成功建立了 WebSocket 连接,并且可以通过抓包观察请求和响应。
下面是在建立 WebSocket 连接时,通过 Fiddle 抓包获取到的具体请求和响应:
请求:
响应:
页面中输入 “hello world”,然后发送:
此时可以看到,前端页面中成功通过 WebSocket 发送了消息。
后端输出:
发现此时后端也成功收到了刚才发送的消息。后续就可以将这个收到的消息进行转发,从而实现实时通信了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。