WebSocket

什么是 WebSocket

Websocket 是一种 协议 ,用于在 客户端服务器 之间建立持久的 双向通信连接 ,广泛应用于需要实时数据交换 的应用程序,例如在线聊天、实时游戏、股票行情等。是从 HTML5 开始支持的一种网页端和服务端保持长连接的 消息推送机制

WebSocket 协议 与 HTTP 协议有什么区别呢?

HTTP 协议通常使用在 "一问一答" 的应用场景中:

客户端向服务器发送一个 HTTP 请求,服务器给客户端返回一个 HTTP 响应

上述情况中,服务器属于被动的一方,若客户端不主动发起请求,服务器就无法主动给客户端响应

那,若需要服务器主动给客户端发送消息,该如何实现呢?

此时就需要 消息推送机制

消息推送 (Push Notification)是一种通信机制 ,允许服务器向客户端主动发送消息或通知,而无需客户端的请求。消息推送使得服务器可以在有重要信息或事件发生时,主动将消息推送给用户

当 user1 发送消息时,user1 的客户端就需要给服务器发送消息,告诉服务器需要将消息发送给 user2,此时,服务器就需要立即将消息转发给 user2,及时将消息同步到 user2 的客户端

此时,若基于 HTTP 来实现上述消息推送效果,就需要基于 "轮询" 的机制

(也就是说 user2 的客户端需要不停地向服务器发送请求)

由于 user2 并不知道 user1 什么时候发送消息,因此,只能不停的向服务器发送请求,直到 user1 发送消息,user2 才能获取到消息

显而易见,上述的 轮询 操作,开销较大,成本也较高

  • 若轮询间隔时间较长,user1 发送消息时,user2 不能及时获取到消息,及时性较差
  • 若轮询间隔时间较短,此时及时性得到了提升,但 user2 会浪费更多的机器资源(如带宽)

因此,消息推送机制 更适合实现服务器主动给客户端发送消息的场景,而 WebSocket是实现消息推送的主要方式

接下来,我们就来进一步学习 WebSocket 的报文格式

WebSocket 报文格式

WebSocket 协议是一个 应用层协议 ,其下层是基于TCP 协议

WebSocket报文主要由以下几个部分组成:

  1. 标识字段(FIN, RSV1, RSV2, RSV3)

  2. 操作码(Opcode)

  3. 掩码标识符(Mask)

  4. 数据长度(Payload length)

  5. 扩展数据(Extension data)

  6. Masking-key

  7. 负载数据(Payload data)

上述大部分内容,我们只需要了解即可,我们主要了解负载数据

负载数据(Payload data):实际传输的数据

其长度由 Payload length 指定,内容可以是文本、二进制数据或其他类型的数据,具体取决于帧的操作码

在学习了 WebSocket 的报文格式 后,我们继续学习 WebSocket 的握手过程,也就是建立连接的过程

WebSocket 的握手过程

WebSocket 的握手过程是建立 WebSocket 连接的关键步骤使用 HTTP 协议进行初始化, 但在成功建立连接后,通信会切换到 WebSocket 协议整个握手过程包括:客户端向服务器发起 HTTP 请求,服务器响应后,连接被升级为 WebSocket 连接

步骤:

1. 客户端发送 WebSocket 握手请求(HTTP 请求)

2. 服务器回应 WebSocket 握手响应(HTTP 响应)

3. 连接升级

客户端发送 WebSocket 握手请求

首先,客户端会尝试与服务器建立 WebSocket 连接,使用HTTP 请求 向服务器发送 WebSocket 握手请求,在这个请求中会带有特殊的 header 信息,表示客户端希望建立 WebSocket 连接,并告知服务器它支持 WebSocket 协议

客户端请求:

服务器回应 WebSocket 握手响应

若服务器支持 WebSocket 协议,并且准备好升级连接,服务器会返回一个 HTTP 101 状态码响应,表示协议切换成功。响应中会包含 WebSocket 协议相关信息

连接升级

当客户端收到服务器返回的 Sec-WebSocket-Accept 时,客户端就知道连接升级成功,WebSocket 连接已经建立。此时,HTTP 协议转化为 WebSocket 协议,连接升级完成,双方可以通过 WebSocket 协议进行双向通信

总而言之,WebSocket 握手实际上是一个基于 HTTP 的协议升级的过程:客户端发起 HTTP 请求,服务器确认并返回一个 HTTP 响应,协议成功切换为 WebSocket

在握手过程中,Sec-WebSocket-Key 和 Sec-WebSocket-Accept 的配对是 WebSocket 握手是否成功的重要验证机制,确保连接的安全性

当握手成功后,WebSocket 连接将保持打开状态,双方可以在连接上进行任意的数据交换,直到一方主动关闭连接

在了解了相关知识后,接下来,我们就可以实现 WebSocket 相关代码了

代码实现示例

服务器代码

由于 Spring 中内置了 WebSocket,因此可以直接使用

引入依赖

在创建项目时添加

代码编写

实现一个类并继承 TextWebSocketHandler类:并重写下面的方法

import org.springframework.web.socket.TextMessage;

import org.springframework.web.socket.WebSocketMessage;

import org.springframework.web.socket.WebSocketSession;

@Component

public class TextWebSocketHandler extends org.springframework.web.socket.handler.TextWebSocketHandler {

@Override

public void afterConnectionEstablished(WebSocketSession session) throws Exception {

super.afterConnectionEstablished(session);

}

@Override

protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {

super.handleTextMessage(session, message);

}

@Override

public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {

super.handleTransportError(session, exception);

}

@Override

public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {

super.afterConnectionClosed(session, status);

}

}
afterConnectionEstablished() :该方法在 WebSocket 连接成功建立后会被调用,可以在方法中进行初始化操作,如 进行身份验证

handleTextMessage():该方法是 TextWebSocketHandler 中最重要的方法,用于处理接收到的文本消息,对该方法进行重写,以在接收到客户端发送的文本消息时执行特定逻辑

handleTransportError():当 WebSocket 连接出现错误时,该方法会被调用,可以在方法中进行错误处理,如 关闭连接 或 记录错误日志

afterConnectionClosed():该方法在 WebSocket 连接关闭时会被调用,可以在方法中做一些清理工作,如 释放资源
package com.example.demo;

import org.springframework.stereotype.Component;

import org.springframework.web.socket.CloseStatus;

import org.springframework.web.socket.TextMessage;

import org.springframework.web.socket.WebSocketMessage;

import org.springframework.web.socket.WebSocketSession;

@Component

public class TextWebSocketHandler extends org.springframework.web.socket.handler.TextWebSocketHandler {

@Override

public void afterConnectionEstablished(WebSocketSession session) throws Exception {

System.out.println("websocket连接成功");

}

@Override

protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {

System.out.println("成功接受到消息"+message.getPayload());

session.sendMessage(message);

}

@Override

public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {

System.out.println("连接异常");

}

@Override

public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {

System.out.println("连接断开");

}

}

我们来进一步了解一下 TextWebSocketHandler

TextWebSocketHandler

TextWebSocketHandler 是 Spring WebSocket 模块中的一个类,用于处理 WebSocket 连接中的文本消息,继承自 WebSocketHandler 接口,专门处理文本消息,并提供了一些方法来处理连接、消息传输、断开等 WebSocket 相关事件

TextWebSocketHandler 中的主要方法:

handleTextMessage(WebSocketSession session, TextMessage message)

作用:

当客户端发送文本消息时,调用该方法处理接收到的文本消息

参数:

WebSocketSession session: 当前 WebSocket 会话的上下文,提供了与客户端的连接信息

TextMessage message:客户端发送的文本消息,包含消息的内容(通 message.getPayload() 方法获取)

afterConnectionEstablished(WebSocketSession session)

作用:

当 WebSocket 连接成功建立后,调用此方法,可以用来执行初始化操作,例如 记录日志、进行身份验证等

参数:

WebSocketSession session :当前的 WebSocket 会话信息

与 handleTextMessage 的区别:

afterConnectionEstablished 方法在 WebSocket 连接建立时调用 ,用于执行连接建立后的初始化任务;而 handleTextMessage 用于处理客户端发送的消息,用于实时交互的消息处理
afterConnectionClosed(WebSocketSession session, CloseStatus status)

作用:

当 WebSocket 连接被关闭时,调用此方法,可以用来进行资源清理、记录日志等相关操作

参数:

WebSocketSession session:当前的 WebSocket 会话信息

CloseStatus status:关闭连接的状态信息,包含关闭的原因等

handleTransportError(WebSocketSession session, Throwable exception)

作用:

当 WebSocket 连接出现错误时,调用此方法,可以用于错误处理、日志记录等相关操作

参数:

WebSocketSession session:当前的 WebSocket 会话信息

Throwable exception:发生的异常

配置 WebSocket

TextWebSocketHandler 只是 WebSocket 处理的一个部分,要想使用 WebSocket,还需要配置 WebSocket 接口,注册 WebSocket 端点

创建 WebSocketConfig 类来对 WebSocket 进行相关配置,并实现 WebSocketConfigurer 接口:

package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.net.http.WebSocket;
@Configuration
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
private TextWebSocketHandler textWebSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(textWebSocketHandler,"/test");
}
}

WebSocketConfigurer :

是 Spring WebSocket 中用于配置 WebSocket 的接口,包含一个方法 registerWebSocketHandlers(),用于注册 WebSocket 端点并设置相关配置。通过这个接口,可以将 WebSocket 处理器(如上述实现的 TestWebSocketHandler)与特定的 URL 端点进行绑定

@Configuration 注解

是 Spring Framework 中的一种标识注解,标识当前类作为配置类,用于替代传统的 XML 配置文件

@EnableWebSocket 注解:

用于启动 Spring 的 WebSocket 功能,告诉 Spring 容器需要在应用中配置 WebSocket 服务,允许通过 Spring 配置类来管理 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="submit">提交</button>

</body>

</html>

接下来,创建 WebSocket 实例,并挂载回调函数

<script>

var socket = new WebSocket("ws://127.0.0.1:8080/test");

socket.onopen = function(){

console.log("连接成功");

}

socket.onmessage = function(event){

console.log("收到消息",event.data);

}

socket.onclose = function(){

console.log("连接关闭");

}

socket.onerror = function(){

console.log("连接错误");

}

</script>

可以发现前端实现的回调函数,与后端实现的相关方法是相匹配的

分别用于:建立连接接收消息处理异常连接关闭

最后,实现点击事件:

var button = document.getElementById("submit");

button.onclick = function(){

var message = document.getElementById("message");

socket.send(message.value);

}

完整实现:

<!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="submit">提交</button>

<script>

var socket = new WebSocket("ws://127.0.0.1:8080/test");

socket.onopen = function(){

console.log("连接成功");

}

socket.onmessage = function(event){

console.log("收到消息",event.data);

}

socket.onclose = function(){

console.log("连接关闭");

}

socket.onerror = function(){

console.log("连接错误");

}

var button = document.getElementById("submit");

button.onclick = function(){

var message = document.getElementById("message");

socket.send(message.value);

}

</script>

</body>

</html>

相关推荐
不做菜鸟的网工1 小时前
OSPF NBMA 网络环境下的 Hub-and-Spoke
网络协议
计算机安禾2 小时前
【计算机网络】第14篇:TCP连接管理的有限状态机模型——三次握手与四次挥手的严格推导
网络·tcp/ip·计算机网络
Walter先生2 小时前
MCP行情数据接入配置踩坑全记录:从Claude Code到Zed八大客户端适配实战
后端·websocket·架构·实时行情数据源
b55t4ck2 小时前
Draytek vigo3910 工业路由器固件解密及其CVE-2024-23721漏洞分析
网络·物联网·网络安全
日取其半万世不竭3 小时前
Excalidraw 自建部署指南:白板协作工具完全私有化
服务器·网络·数据库
从零开始学习人工智能3 小时前
同文件同网络,curl 上传飞快,浏览器 HTTP/1.1 却慢到离谱?终于找到元凶!
网络·网络协议·http
程序员小白条3 小时前
别盲目卷算法!2026 程序员\&大学生,最稳的 AI 技术进阶路线全梳理
java·网络·人工智能·网络协议·http·面试
计算机安禾5 小时前
【计算机网络】第16篇:TCP流量控制——接收窗口调度的缓冲管理问题
网络·tcp/ip·计算机网络
中议视控5 小时前
网络中控系统通过推流软件实现可视化:RTSP,H265,WEB等推流
前端·网络