目录

实现简易聊天室功能

实现简易聊天室功能

实现简易聊天室功能

1.前端设计

https://i-blog.csdnimg.cn/direct/9f2bc0e5c33549368f05b6ace8273ffb.png

https://i-blog.csdnimg.cn/direct/bdd7e45cb2104820a4e0563542ae9423.png

https://i-blog.csdnimg.cn/direct/de5fcef81371441f8f95b9e6c222993b.png

<template>
  <div>
    <div>
      <!-- 连接按钮 -->
      <el-button v-loading="loading" :disabled="loading || isConnected" type="primary" @click="connect">
        {{ isConnected ? "已连接" : "连接" }}
      </el-button>
      <!-- 断开连接按钮 -->
      <el-button :disabled="!isConnected" type="danger" @click="disconnect">断开</el-button>
      <!-- 发送消息按钮 -->
      <el-button :disabled="!isConnected" type="success" @click="sendMessage">发送</el-button>
    </div>
    <div>
      <el-input v-model="username" :disabled="isConnected" placeholder="用户名" style="width: 100px"
                @keyup.enter="sendMessage"/>
    </div>
    <div>
      <!-- 消息输入框 -->
      <el-input v-model="msg" placeholder="输入消息" @keyup.enter="sendMessage"/>
    </div>
    <!-- 消息列表 -->
    <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: null, // 当前用户
      msg: '', // 发送的消息
      messages: [], // 消息列表
      isConnected: false, // 连接状态
      loading: false, // 是否正在连接
    };
  },
  methods: {
    // 连接 WebSocket
    connect() {
      const vm = this
      // 参数校验
      if (vm.isConnected) {
        vm.$message.success("WebSocket 已连接");
        return;
      } else if (!vm.username) {
        vm.$message.success("请输入用户名");
        return;
      }
      // 连接
      vm.loading = true;
      vm.socket = new WebSocket(`/socket/ws/chat/${this.username}`);

      // 连接成功
      vm.socket.onopen = () => {
        vm.$notify.success("WebSocket 连接成功");
        vm.isConnected = true;
        vm.loading = false;
      };

      // 接收消息
      vm.socket.onmessage = (event) => {
        vm.messages.push(event.data);
      };
      // 关闭
      vm.socket.onclose = () => {
        vm.$notify.success("WebSocket 连接关闭");
        vm.cleanupSocket();
      };
      // 异常
      vm.socket.onerror = (error) => {
        vm.$notify.error("WebSocket 发生错误" + error);
        vm.cleanupSocket();
      };
    },
    // 发送消息
    sendMessage() {
      const vm = this
      if (vm.socket && vm.isConnected) {
        vm.socket.send(vm.msg);
        vm.messages.push(`我: ${vm.msg}`);
        vm.msg = "";
      } else {
        vm.$notify.warning("WebSocket 未连接,无法发送消息");
      }
    },
    // 断开 WebSocket
    disconnect() {
      const vm = this
      if (vm.socket) {
        vm.socket.close();
      }
      vm.isConnected = false;
    },
    // 关闭 WebSocket 并清理
    cleanupSocket() {
      const vm = this
      if (vm.socket) {
        vm.socket.close();
        vm.socket = null;
      }
      vm.isConnected = false;
      vm.loading = false;
    },
  },
  beforeUnmount() {
    this.cleanupSocket();
  },
};
</script>

<style scoped>
</style>

2.后端

依赖

        <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>

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();
    }


}

控制类

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.stereotype.Controller;

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


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

    // 存储在线用户(线程安全)
    private static final Map<String, Session> userSessionMap = new ConcurrentHashMap<>();
    private Session session;
    private String username;


    /**
     * 创建时触发 用户在进入聊天室的时候触发
     *
     * @param username
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session) {
        this.username = username;
        this.session = session;
        userSessionMap.put(username, session);
        System.out.println("【open】用户 " + username + " 连接成功,当前在线人数:" + userSessionMap.size());
        sendOne("【open-one】" + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        sendAll("【open-all】用户 " + username + " 加入聊天室");
    }

    /**
     * 响应字符串
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        System.out.println("收到消息:" + message);
        System.out.println("当前用户" + username);
        sendAllNotMe(username + ":" + message);
    }


    /**
     * 响应字节流
     *
     * @param session session
     * @param message message
     */
    @OnMessage
    public void onMessage(Session session, byte[] message) {
        System.out.println("响应字节流");
    }


    /**
     * 断开 离开聊天室的时候触发
     */
    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        userSessionMap.remove(username);
        System.out.println("用户 " + username + " 断开连接,当前在线人数:" + userSessionMap.size());
        System.out.println("连接关闭:" + session.getId());
        sendAll("【all】用户 " + username + " 离开聊天室");
    }

    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("用户 " + username + " 发生错误:" + error.getMessage());
        try {
            this.session.close();
            userSessionMap.remove(username);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 发送给所有人
     * @param message
     */
    private void sendAll(String message) {
        if (userSessionMap != null && userSessionMap.size() > 0) {
            for (Map.Entry<String,Session> entry : userSessionMap.entrySet()) {
                try {
                    entry.getValue().getBasicRemote().sendText(message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 发送给除了我的
     * @param message
     */
    private void sendAllNotMe(String message) {
        if (userSessionMap != null && userSessionMap.size() > 0) {
            for (Map.Entry<String,Session> entry : userSessionMap.entrySet()) {
                try {
                    if (StrUtil.isNotBlank(entry.getKey()) && entry.getKey().equals(username)) {
                        continue;
                    } else {
                        entry.getValue().getBasicRemote().sendText(message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

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