基于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>
相关推荐
EasyDSS3 小时前
连锁扩张管控难?一套视频会议/直播/点播/集群对讲EasyDSS音视频云平台打通门店全流程数字化
网络·音视频·easydss
比兔代理5 小时前
游戏、直播、远程办公遇到动态IP怎么办?
网络
Lzg_na5 小时前
ARP 地址解析协议详解
网络·网络协议
北京盛世宏博6 小时前
通信异常深度排查:以太网温湿度设备 TCP 粘包、UDP 丢包根治方案
单片机·网络协议·tcp/ip·udp·以太网·传感器·温湿度
上海广测检测科技有限公司6 小时前
消费类智能联网设备安全分级国标征求意见要点解析:三级五维框架与跨境对标逻辑
网络·经验分享
QZ166560951597 小时前
金融数据流转监测排行:从看见到拦截的距离
网络·安全·网络安全·金融
paopaokaka_luck8 小时前
滑雪场管理系统(AI智能助手流式对话,ECharts 运营数据分析、教练、雪具与场地一体化预约,钱包支付退款、订单核销与服务评价)
java·人工智能·spring boot·网络协议·数据分析·echarts
CCblandy9 小时前
HTTP 长连接 3 类故障实测:[WinError 10053]、99 个 TIME_WAIT、69 次多余握手,4 种对策怎么选
后端·python·网络协议·http·云原生·性能优化·架构
雪雪爱冲浪9 小时前
2026 年网络爬虫领域的Puppeteer vs. Playwright
爬虫·网络协议·测试工具·接口隔离原则
zxanz19 小时前
SSL 证书申请下来快吗?
网络·网络协议·ssl