一、WebSocket介绍
WebSocket 是一种在单个 TCP 连接上进行全双工通讯的协议,它使得客户端和服务器之间的数据交换变得更加简单、高效。WebSocket 协议在 2011 年由 IETF 标准化,并成为 HTML5 标准的一部分。WebSocket 使得客户端和服务器之间的数据交换变得更加简单、高效,允许服务端主动向客户端推送数据。
二、WebSocket使用场景
WebSocket 主要用于实时通信,如聊天、游戏、股票行情、实时监控等。WebSocket 协议的优势在于:
- 建立在 TCP 协议之上,相比 HTTP 协议,建立 WebSocket 连接的握手阶段更加简单,握手后数据传输更加高效。
- 通信双方可以主动推送数据,服务器向客户端推送数据,客户端也可以主动向服务器发送数据。
- 协议标识符是 ws(WebSocket)或 wss(WebSocket Secure),端口号默认是 80(ws)和 443(wss)。
三、WebSocket实现原理
WebSocket 协议在建立连接时,需要先在 HTTP 协议上完成一次握手,握手成功后,WebSocket 协议开始工作。
WebSocket 协议的通信过程如下:
- 客户端向服务器发起 WebSocket 连接请求,请求中包含了 HTTP 协议的请求行、请求头和请求体。
- 服务器收到 WebSocket 连接请求后,向客户端发送 HTTP 响应,响应中包含了 HTTP 协议的响应行、响应头和响应体。
- 客户端和服务器完成 WebSocket 握手,并开始 WebSocket 数据传输。
- WebSocket 数据传输过程中,服务器和客户端可以互相发送数据帧。
- WebSocket 连接一旦关闭,连接双方的 WebSocket 协议实现也会相应的关闭。
四、WebSocket框架
WebSocket 协议的实现框架有很多,如 Node.js 的 ws 模块、Java 的 javax.websocket 包、Python 的 aiohttp-websocket 库等。
五、Java WebSocket 使用
5.1 引入 WebSocket 依赖
WebSocket在spring boot项目中, 主要依赖spring-boot-starter-websocket包,该包包含了 WebSocket 相关的接口和注解。
- 在 pom.xml 文件中添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.2</version> <!-- 确保使用合适的版本 -->
</dependency>
<dependency>
<groupId>org.glassfish.tyrus.bundles</groupId>
<artifactId>tyrus-standalone-client</artifactId>
<version>1.20</version>
</dependency>
2.创建WebSocketConfig.class文件,配置WebSocket相关的bean:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyWebSocketHandler(), "/ws").setAllowedOrigins("*");
}
}
3.创建MyWebSocketHandler.class文件,实现WebSocketHandler接口:
@Component
public class MyWebSocketHandler extends TextWebSocketHandler {
private static final Logger logger = LoggerFactory.getLogger(MyWebSocketHandler.class);
private static final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();// 会话列表
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session); // 新连接加入会话列表
System.out.println("新客户端连接: " + session.getId());
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
System.out.println("收到客户端消息: " + payload);
// 处理客户端消息
session.sendMessage(new TextMessage("服务器收到: " + payload));
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session); // 连接关闭时移除会话
System.out.println("客户端断开连接: " + session.getId());
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
logger.error("WebSocket error on session {}: {}", session.getId(), exception.getMessage());
}
// 向所有客户端推送消息
public void sendToAll(String message) {
System.out.println("当前连接用户数: "+sessions.size());
for (WebSocketSession session : sessions) {
try {
session.sendMessage(new TextMessage(message));
System.out.println("服务端推送消息成功: " + message);
} catch (IOException e) {
System.err.println("推送消息失败: " + e.getMessage());
}
}
}
// 向指定客户端推送消息
public void sendTo(WebSocketSession session, String message) {
try {
session.sendMessage(new TextMessage(message));
System.out.println("服务端推送消息成功: " + message);
} catch (IOException e) {
System.err.println("推送消息失败: " + e.getMessage());
}
}
// 获取会话列表的副本
public List<WebSocketSession> getSessions() {
return new ArrayList<>(sessions); // 返回副本,避免外部修改
}
}
3.前端页面中,通过WebSocket连接到服务器,并接收服务器推送的消息:
var ws = new WebSocket("ws://localhost:8080/你的项目名/ws");
// 连接成功触发
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
// 监听消息
socket.addEventListener('message', function (event) {
// console.log('Message from server ', event.data);
// 解析后端消息
var jsonStr = event.data;
//*********
});
// 监听连接关闭事件
socket.addEventListener('close', function (event) {
console.log('连接已关闭,尝试重新连接...');
reconnect();
});
// 监听错误事件
socket.addEventListener('error', function (error) {
console.error('WebSocket 发生错误: ', error);
});
// 重连方法
// 若连接断开,则3秒后尝试重新连接
function reconnect() {
setTimeout(function () {
console.log('重试连接...');
socket = new WebSocket('ws://localhost:8080/bdhhk/ws');
// 在新的连接上重新绑定事件
socket.addEventListener('open', function (event) {
console.log('重新连接成功,发送消息到服务器');
socket.send('Hello Server!');
});
socket.addEventListener('message', function (event) {
console.log('来自服务器的消息: ', event.data);
});
socket.addEventListener('close', function (event) {
console.log('连接已关闭,尝试重新连接...');
reconnect();
});
socket.addEventListener('error', function (error) {
console.error('WebSocket 发生错误: ', error);
});
}, 3000); // 3秒后重试连接
}
4.前端页面中,向服务器发送消息:
ws.send("客户端发送的消息");
六、WebSocket 安全性
WebSocket 协议本身是一种基于 TCP 协议的协议,因此,WebSocket 协议本身是安全的。但是,由于 WebSocket 协议本身的限制,WebSocket 协议并不能完全抵御中间人攻击。
七、WebSocket 常见问题
7.1 客户端与服务端建立 WebSocket 连接时,如何验证身份?
WebSocket 协议本身并没有提供身份验证机制,因此,需要服务端根据业务需求,在建立 WebSocket 连接时,服务端可以向客户端发送身份验证信息,
客户端接收到身份验证信息后,根据身份验证信息进行验证,如cookie、token等。验证通过后,才建立 WebSocket 连接。