基于websocket搭建聊天室

基于websocket搭建聊天室

1.后端配置

1.依赖一个web一个websocket

复制代码
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

2.config

复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }


}

3.ws

复制代码
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;
import java.util.concurrent.CopyOnWriteArraySet;


@Component
@ServerEndpoint("/ws/chat/{username}")
public class WebSocketServer {

    private static final CopyOnWriteArraySet<WebSocketServer> clients = new CopyOnWriteArraySet<>();
    // 存储在线用户(线程安全)
    private static final Map<String, Session> userMap = new ConcurrentHashMap<>();
    private Session session;
    private String username; // 当前用户
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session) {
        this.username = username;
        userMap.put(username, session);
        this.session = session;
        clients.add(this);
        System.out.println("用户 " + username + " 连接成功,当前在线人数:" + clients.size());
        sendMessage("欢迎 " + username + " 加入聊天室!");
    }

    @OnMessage
    public void onMessage(String message) {
        System.out.println("收到消息:" + message);
        broadcast(message);
    }

    @OnClose
    public void onClose() {
        clients.remove(this);
        userMap.remove(username);
        System.out.println("用户 " + username + " 断开连接,当前在线人数:" + clients.size());
        broadcast("用户 " + username + " 离开聊天室");
        System.out.println("连接关闭:" + session.getId());
    }

    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("用户 " + username + " 发生错误:" + error.getMessage());
    }

    private void broadcast(String message) {
        for (WebSocketServer client : clients) {
            try {
                client.session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发送消息给某个用户
     */
    private void sendMessage(String message) {
        try {
            if (userMap.containsKey(username)) {
                userMap.get(username).getBasicRemote().sendText(message);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.前端

1.vue2+element ui

2.前端代理vue.config.js

复制代码
"use strict"
const path = require("path")

function resolve(dir) {
  return path.join(__dirname, dir)
}
const name =  "vue Template"
module.exports = {
  publicPath: "./",
  assetsDir: "static",
  lintOnSave: false,
  devServer: {
    proxy: {
      "^/socket": {
        target: "ws://localhost:8888",
        ws: true,
        changeOrigin: true,
        pathRewrite: {'/socket':''}
      },
      "^/api": {
        target: "http://localhost:8888",
        changeOrigin: true,
        // pathRewrite: {'/datashare':'/'}
      },

    },
  },
  productionSourceMap: false,
  configureWebpack: {
    name: name,
    resolve: {
      alias: {
        "@": resolve("src"),
      },
    },
  },

}

3.测试代码

复制代码
<template>
  <div>
    <!-- 连接按钮 -->
    <el-button type="primary" :disabled="loading || isConnected" @click="connect" v-loading="loading">
      {{ isConnected ? "已连接" : "连接" }}
    </el-button>

    <!-- 断开连接按钮 -->
    <el-button type="danger" :disabled="!isConnected" @click="disconnect">
      断开连接
    </el-button>

    <!-- 消息输入框 -->
    <el-input v-model="msg" placeholder="输入消息" @keyup.enter="sendMessage"/>

    <!-- 发送消息按钮 -->
    <el-button type="success" :disabled="!isConnected" @click="sendMessage">
      发送
    </el-button>

    <!-- 消息列表 -->
    <el-card v-if="messages.length > 0">
      <p v-for="(message, index) in messages" :key="index">{{ message }}</p>
    </el-card>
  </div>
</template>

<script>
export default {
  name: "WebSocketChat",
  data() {
    return {
      socket: null, // WebSocket 对象
      username: "Jack", // 当前用户
      msg: "", // 发送的消息
      messages: [], // 消息列表
      isConnected: false, // 连接状态
      loading: false, // 是否正在连接
    };
  },
  methods: {
    // 连接 WebSocket
    connect() {
      if (this.isConnected) {
        console.log("WebSocket 已连接");
        return;
      }

      this.loading = true;
      this.socket = new WebSocket(`/socket/ws/chat/${this.username}`);

      this.socket.onopen = () => {
        console.log("WebSocket 连接成功");
        this.isConnected = true;
        this.loading = false;
      };

      this.socket.onmessage = (event) => {
        console.log("收到消息:" + event.data);
        this.messages.push(event.data);
      };

      this.socket.onclose = () => {
        console.warn("WebSocket 连接关闭");
        this.isConnected = false;
        this.cleanupSocket();
      };

      this.socket.onerror = (error) => {
        console.error("WebSocket 发生错误", error);
        this.isConnected = false;
        this.cleanupSocket();
      };
    },

    // 发送消息
    sendMessage() {
      if (this.socket && this.isConnected) {
        this.socket.send(this.msg);
        console.log("发送消息:" + this.msg);
        this.messages.push(`我: ${this.msg}`);
        this.msg = "";
      } else {
        console.warn("WebSocket 未连接,无法发送消息");
      }
    },

    // 断开 WebSocket
    disconnect() {
      if (this.socket) {
        this.socket.close();
      }
      this.isConnected = false;
    },

    // 关闭 WebSocket 并清理
    cleanupSocket() {
      if (this.socket) {
        this.socket.close();
        this.socket = null;
      }
      this.isConnected = false;
      this.loading = false;
    },
  },
  beforeUnmount() {
    this.cleanupSocket();
  },
};
</script>

<style scoped>
</style>
相关推荐
数据与人工智能律师2 小时前
数字时代,如何为个人信息与隐私筑牢安全防线?
大数据·网络·人工智能·云计算·区块链
fhqlongteng5 小时前
一种动态分配内存错误的解决办法
运维·服务器·网络·c++
真正的醒悟9 小时前
IRF2.0&&IRF3.1
开发语言·网络·php
山石网科10 小时前
2025 RSAC|自主式 GenAI 安全智能体(Agent)开启防御新纪元
网络·人工智能
Johny_Zhao11 小时前
Ubuntu堡垒机搭建与设备管理指南
linux·网络·人工智能·信息安全·云计算·yum源·系统运维·teleport
计算机毕设定制辅导-无忧学长12 小时前
ActiveMQ 性能优化与网络配置实战(二)
网络·性能优化·activemq
hgdlip13 小时前
怎么查自己手机连接的ip归属地:完整指南
网络·tcp/ip·web安全·手机·ip归属地
同聘云13 小时前
网络安全防火墙技术有哪些?网络防火墙的主要作用
网络
2401_8352613816 小时前
网络原理初识
网络·智能路由器
caolib16 小时前
1.计算机网络概述
网络·计算机网络