实现WebSocket聊天室功能
- 什么是WebSocket?
- WebSocket的工作原理
- 服务器端实现
- 客户端实现
在现代Web开发中,实时通信已经变得越来越重要。传统的HTTP协议由于其无状态和单向通信的特点,无法很好地满足实时通信的需求。而WebSocket协议则应运而生,提供了全双工的通信能力,非常适合实现诸如聊天室这样的实时应用。在这篇博客中,我们将深入探讨WebSocket的工作原理,并一步步实现一个简单的聊天室应用。
什么是WebSocket?
WebSocket是HTML5的一部分,它为客户端和服务器之间提供了全双工通信通道。与传统的HTTP协议不同,WebSocket允许服务器主动向客户端推送数据,而不仅仅是客户端请求时服务器响应。这使得WebSocket非常适合需要频繁更新的应用,如实时聊天、在线游戏、股票行情等。
WebSocket的工作原理
握手阶段:WebSocket通信从HTTP协议开始,客户端发起一个HTTP请求,通过特殊的头部字段表明要升级到WebSocket协议。服务器响应这个请求,并同意升级协议。
数据传输阶段:握手成功后,客户端和服务器之间建立了一条全双工的通信通道,双方可以通过这个通道随时发送和接收数据。
连接关闭:任何一方都可以随时关闭连接,关闭时双方都会收到一个关闭帧,告知对方连接已经关闭。
实现一个简单的WebSocket聊天室
接下来,我们将使用Node.js和前端JavaScript来实现一个简单的WebSocket聊天室。我们将分为两个部分:服务器端和客户端。
服务器端实现
springboot整合websocket
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>3.3.1</version>
</dependency>
创建MyWebSocketHandler2
/**
* MyWebSocketHandler2类继承自TextWebSocketHandler,用于处理WebSocket的文本消息。
* 该类的主要作用是接收客户端发送的文本消息,并将消息内容回显给客户端。
*/
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
/**
* @className MyWebSocketHandler2
* @描述 RuoYi-Vue-Test
* @Author ljquan
* @Date 2024/7/2 上午9:43 星期二
*/
@Component(value = "MyWebSocketHandler2")
public class MyWebSocketHandler2 extends TextWebSocketHandler {
/*
处理接收到的文本消息。
@param session WebSocket会话,用于发送和接收消息。
* @param message 接收到的文本消息对象。
* @throws Exception 如果处理消息时发生异常。
*/
/**
* 当收到文本消息时,该方法会被调用。
* 主要功能是打印接收到的消息,并发送一个回显消息给客户端。
*
* @param session WebSocket会话,用于发送和接收消息。
* @param message 接收到的文本消息。
* @throws Exception 如果处理消息时发生错误。
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 提取文本消息的负载部分。
// 提取消息负载部分。
String payload = message.getPayload();
// 打印接收到的消息,用于调试和日志记录。
// 打印接收到的消息。
System.out.println("收到客户端的消息2: " + payload);
// 构造回显消息,并发送给客户端。
// 回显接收到的消息给客户端。
session.sendMessage(new TextMessage("张三的ws: " + payload));
}
}
创建WebSocketConfig配置类
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import javax.annotation.Resource;
/**
* WebSocket配置类,用于配置WebSocket的相关设置。
* 通过实现WebSocketConfigurer接口,可以自定义WebSocket的处理逻辑和访问路径。
*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
/**
* 注入名为"MyWebSocketHandler"的WebSocketHandler,用于处理WebSocket连接。
*/
@Resource(name = "MyWebSocketHandler")
WebSocketHandler MyWebSocketHandler;
/**
* 注入名为"MyWebSocketHandler2"的WebSocketHandler,用于处理另一个WebSocket连接。
* 这种做法可以支持多个不同的WebSocket处理逻辑。
*/
@Resource(name = "MyWebSocketHandler2")
WebSocketHandler MyWebSocketHandler2;
/**
* 配置WebSocket处理器,将WebSocketHandler与特定的URL路径关联起来。
* 此方法允许配置多个WebSocket处理路径,并设置允许的来源。
*
* @param registry WebSocketHandlerRegistry,用于注册WebSocket处理器和配置访问路径及允许来源。
*/
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 将MyWebSocketHandler与路径"/ws"关联,并允许所有来源访问。
registry.addHandler(MyWebSocketHandler, "/ws").setAllowedOrigins("*");
// 同样将MyWebSocketHandler2与路径"/ws2"关联,也允许所有来源访问。
// 这样可以支持不同的WebSocket服务在同一应用中。
registry.addHandler(MyWebSocketHandler2, "/ws2").setAllowedOrigins("*");
}
}
客户端实现
创建test.vue
<template>
<div id="test">
<h1>聊天室</h1>
<div class="chat-window">
<div
v-for="(message, index) in messages"
:key="index"
:class="['message', message.isSelf ? 'self' : 'other']"
>
<div class="user-id">{{ message.userId }}</div>
<div class="message-text">{{ message.text }}</div>
</div>
</div>
<input v-model="inputMessage" @keyup.enter="sendMessage" placeholder="输入消息" />
<button @click="sendMessage">发送</button>
</div>
</template>
<script>
import { connectWebSocket, sendMessage } from "@/util/websocket";
export default {
name: 'TestChat',
data() {
return {
// 存储聊天消息的数组
messages: [],
// 输入框的消息内容
inputMessage: '',
// 用户ID,用于区分消息发送者
userId: null
};
},
methods: {
/**
* 发送消息方法
* 当输入框的消息不为空时,发送消息并清空输入框内容
*/
sendMessage() {
if (this.inputMessage.trim()) {
// 发送消息
sendMessage(JSON.stringify({ userId: this.userId, text: this.inputMessage }));
// 清空输入框内容
this.inputMessage = '';
}
}
},
created() {
// 初始化用户ID
this.userId = new Date().getTime();
// 连接WebSocket,并处理接收到的消息
connectWebSocket('ws://localhost:54552/ws', (message) => {
const parsedMessage = JSON.parse(message);
// 接收到的消息添加到消息数组中,并标记是否为本人发送
this.messages.push({
text: parsedMessage.text,
userId: parsedMessage.userId,
isSelf: parsedMessage.userId.toString() === this.userId.toString()
});
});
}
};
</script>
<style>
#test {
text-align: center;
margin-top: 50px;
}
.chat-window {
width: 600px;
height: 400px;
border: 1px solid #ccc;
margin: 0 auto;
padding: 10px;
overflow-y: scroll;
}
.message {
margin-bottom: 10px;
padding: 10px;
border-radius: 10px;
max-width: 50%;
}
.self {
background-color: #daf1da;
margin-left: auto;
text-align: right;
}
.other {
background-color: #f1dada;
margin-right: auto;
text-align: left;
}
.user-id {
font-weight: bold;
margin-bottom: 5px;
}
.message-text {
margin-bottom: 10px;
}
input {
width: 250px;
padding: 5px;
}
button {
padding: 5px 10px;
}
</style>
再创建一个脚本js,websocket.js
let socket;
export function connectWebSocket(url, onMessage) {
socket = new WebSocket(url);
socket.onopen = function() {
console.log("WebSocket连接已建立");
};
socket.onmessage = function(event) {
onMessage(event.data);
};
socket.onclose = function() {
console.log("WebSocket连接已关闭");
};
socket.onerror = function(error) {
console.error("WebSocket发生错误: ", error);
};
}
export function sendMessage(message) {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(message);
}
}
然后配置路由
{path: '/TestChat', name: 'TestChat', props: true,component: () => import("../views/webSocket/demo/test.vue")},
最后执行前后端,测试得到以下:
一般如果使用WebSocket来实现大屏数据的,需要用到定时任务,然后定时可以更新数据后WebSocket连接到客户端。