实现WebSocket聊天室功能

发布于:2024-07-03 ⋅ 阅读:(13) ⋅ 点赞:(0)

在现代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连接到客户端。