实现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 如果处理消息时发生错误。*/@Overrideprotected 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处理器和配置访问路径及允许来源。*/@Overridepublic 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"><divv-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() {// 初始化用户IDthis.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连接到客户端。