Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the wordpress-seo domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /var/www/html/wp-includes/functions.php on line 6114 vue+websocket实现即时聊天平台 - 说了一半的话的博客
vue+websocket实现即时聊天平台

1 什么是websocket

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它主要用于在客户端和服务器之间建立持久的连接,允许实时数据交换。WebSocket 的设计目的是为了提高 Web 应用程序的交互性,减少延迟和带宽的使用。

  • 全双工通信:客户端和服务器可以同时发送和接收数据,而不需要等待对方完成发送。
  • 持久连接:建立一次连接后,可以保持该连接,直到主动关闭。这比传统的 HTTP 请求/响应模型更加高效。
  • 低延迟:由于不需要为每个请求建立新的连接,WebSocket 可以显著减少延迟。
  • 节省带宽:在 WebSocket 中,只有数据被发送而不需要携带大量的头部信息,这减少了带宽的消耗。

2 实现步骤

实施前提:默认在springBoot环境下实施

2.1 导入依赖

<!--WebSocket依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    <version> 3.3.4</version>
</dependency>

2.2 编写代码

WebSocketConfig:主要实现websocket的一些配置
package com.hyh.admin.config.websocket;

import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;

/**
 * WebSocket配置
 * @author hyh
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements ServletContextInitializer {
    /*
     *  ServerEndpointExporter 作用
     *  这个Bean会自动注册使用@ServerEndpoint注解声明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

    /*
     * 解除websocket对数据大小的限制
     * @param servletContext Servlet上下文
     *
     */
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        // 解除websocket对数据大小的限制
        servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize","10240000");
        servletContext.setInitParameter("org.apache.tomcat.websocket.binaryBufferSize","10240000");
    }
}

WebSocketSingleServe:具体的实现聊天的实时代码需求
package com.hyh.admin.config.websocket;

import com.hyh.ad.common.core.domain.model.SysUser;
import com.hyh.admin.config.websocket.context.SpringBeanContext;
import com.hyh.admin.domain.Messages;
import com.hyh.admin.service.MessageService;
import com.hyh.admin.sys.service.ISysUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket 单聊服务端
 */
@ServerEndpoint("/singleChat/{username}")
@Component
public class WebSocketSingleServe implements InitializingBean {

    private static final Logger log = LoggerFactory.getLogger(WebSocketSingleServe.class);

    // 记录当前在线的连接
    public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session) {
        // 将用户的session放入map中
        session.getUserProperties().put("username", username);
        sessionMap.put(username, session);
        log.info("用户:{}",session.getUserProperties().get("username"));
        log.info("用户:{} 连接成功,session:{},总数:{}", username, session.getId(), sessionMap.size());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        try {
            sessionMap.values().remove(session);
            log.info("连接关闭,session:{},总数:{}", session.getId(), sessionMap.size());
        } catch (Exception e) {
            log.error("连接关闭异常:{}", e.getMessage());
        }
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session fromSession) {
        // 假设消息格式为 "username:file:data"
        String[] parts = message.split(":", 3);
        if (parts.length == 3) {
            String targetUsername = parts[0].trim(); // 目标用户
            String type = parts[1].trim(); // 消息类型(text/file)
            String content = parts[2].trim(); // 消息内容

            log.info("收到消息:{},类型:{},内容:{}", targetUsername, type, content);
            // 根据类型处理消息
            if ("text".equals(type)) {
                // 发送文本消息
                sendMessageToUser(targetUsername, content, type);
            } else if ("image".equals(type)) {
                // 发送文件消息
                sendFileToUser(targetUsername, content, type);
            }else if ("file".equals(type)) {
                // 发送文件消息
                sendFileToUser(targetUsername, content, "file");
            }

            // 消息持久化
            String username = (String) fromSession.getUserProperties().get("username");
            saveMessage(username, targetUsername, content, type);
        }
    }



    /*
     * 消息持久化
     */
    private void saveMessage(String sendUsername, String targetUsername, String msg, String type) {
        // 保存消息
        try {
            MessageService messageService = SpringBeanContext.getContext().getBean(MessageService.class);
            ISysUserService sysUserService = SpringBeanContext.getContext().getBean(ISysUserService.class);
            SysUser targetUser = sysUserService.selectUserByUserName(targetUsername);
            Long targetUserId = targetUser.getId();
            SysUser sendUser = sysUserService.selectUserByUserName(sendUsername);
            Long userId = sendUser.getId();

            Messages messages = new Messages();
            messages.setSenderId(userId);
            messages.setReceiverId(targetUserId);
            messages.setContent(msg);
            messages.setMessageType(type); // 保存消息类型

            messageService.addMessage(messages);
            log.info("消息持久化成功");
        } catch (Exception e) {
            log.error("消息持久化失败:{}", e.getMessage());
        }
    }

    /*
     *  发送文件给用户
     */
    private void sendFileToUser(String targetUsername, String fileContent, String type) {
        Session targetSession = sessionMap.get(targetUsername);
        if (targetSession != null) {
            try {
                targetSession.getBasicRemote().sendText(type + "|" + fileContent); // 文件发送格式
                log.info("发送文件给用户:{},发送成功", targetUsername);
            } catch (IOException e) {
                log.error("发送文件失败:{}", e.getMessage());
            }
        }
    }


    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误,session:{} ,错误信息:{}", session.getId(), error);
    }

    /**
     * 服务端发送消息给指定用户
     * @param username 目标用户
     * @param message 消息内容
     */
    public void sendMessageToUser(String username, String message, String type) {
        Session session = sessionMap.get(username);
        if (session != null && session.isOpen()) {
            try {
                session.getBasicRemote().sendText(type + "|" + message);
                log.info("发送给用户:{},内容:{}", username, message);
            } catch (IOException e) {
                log.error("发送消息失败:{}", e.getMessage());
            }
        } else {
            log.warn("用户:{} 不在线,无法发送消息", username);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("WebSocket服务端启动");
    }
}

  onopen方法主要用于连接的的方法,所有和websocket发起连接的客户端都会经过这个方法。

  onmessage方法主要用于发送消息的方法,其中定义了发送消息的格式,可以自行定义。

   前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>聊天界面</title>
    <style>
        body { font-family: Arial, sans-serif; }
        #messages { border: 1px solid #ccc; height: 300px; overflow-y: scroll; margin-bottom: 10px; }
        input, button { margin: 5px; }
    </style>
</head>
<body>
<h2>聊天界面</h2>
<input type="text" id="targetUser" placeholder="输入目标用户名...">
<input type="text" id="message" placeholder="输入消息...">
<button id="sendBtn">发送</button>
<div id="messages"></div>
<img src="https://c-ssl.duitang.com/uploads/item/202003/27/20200327141738_ulbvu.jpg" alt="">
<script>
    const username = prompt("请输入您的用户名:"); // 获取当前用户的用户名
    const socket = new WebSocket(`ws://127.0.0.1:8088/singleChat/${username}`);

    socket.onopen = function() {
        console.log(`${username} 已连接`);
    };

    socket.onmessage = function(event) {
        const messagesDiv = document.getElementById("messages");
        messagesDiv.innerHTML += `<p>${event.data}</p>`;
        messagesDiv.scrollTop = messagesDiv.scrollHeight; // 滚动到底部
    };

    document.getElementById("sendBtn").onclick = function() {
        const targetUser = document.getElementById("targetUser").value;
        const messageInput = document.getElementById("message").value;
        const message = `${targetUser}:text:${messageInput}`; // 格式化消息
        socket.send(message);

        // 显示自己发送的消息
        const messagesDiv = document.getElementById("messages");
        messagesDiv.innerHTML += `<p>我: ${messageInput}</p>`;
        messagesDiv.scrollTop = messagesDiv.scrollHeight; // 滚动到底部

        document.getElementById("message").value = "";  // 清空输入框
    };
</script>
</body>
</html>

vue的部分代码和项目完整的截图为:

 this.socket = new WebSocket(
        `ws://127.0.0.1:8088/singleChat/${localStorage.getItem("username")}`
      );

      this.socket.onopen = () => {
        console.log(localStorage.getItem("username") + " 连接成功");
      };

      // 只设置一次 onmessage 处理逻辑
      this.socket.onmessage = (event) => {
        const message = event.data; // 假设格式为 "type:content"
        const parts = message.split("|"); // 按冒号分割

        if (parts.length === 2) {
          const type = parts[0].trim(); // 消息类型
          const content = parts[1].trim(); // 消息内容

          this.contactRecord.push({
            id: Date.now(), // 使用时间戳作为消息 ID
            senderId: this.user.id, // 或者其他用户的 ID
            content: content,
            messageType: type, // 添加类型
          });

          // 进度条滚动到底部
          this.scrollToBottom();
        }
      };
    },

需要源码的请私信我:

谢谢各位的支持!!!  ​


本文《vue+websocket实现即时聊天平台》由 说了一半的话 发布。想了解更多详细内容,请点击 这里


欢迎分享这篇文章链接:https://odgclusterflutter.top/vuewebsocket%e5%ae%9e%e7%8e%b0%e5%8d%b3%e6%97%b6%e8%81%8a%e5%a4%a9%e5%b9%b3%e5%8f%b0/



暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇