【VUE小型网站开发】socket.io聊天室

本功能参考SpringBoot 整合 Socket 实战案例

1. 基本功能搭建

1.1 引入依赖

xml 复制代码
        <!-- Socket.io -->
        <dependency>
            <groupId>com.corundumstudio.socketio</groupId>
            <artifactId>netty-socketio</artifactId>
            <version>1.7.7</version>
        </dependency>

1.2 添加yml配置

根据需求进行调整

yaml 复制代码
socketio:
  #  SocketIO服务器的host
  host: localhost
  #  SocketIO服务器的端口
  port: 8503
  #  SocketIO服务器允许的最大帧负载长度
  maxFramePayloadLength: 1048576
  #  SocketIO服务器的最大http内容长度
  maxHttpContentLength: 1048576
  #  SocketIO服务器的线程数量
  bossCount: 1
  #  SocketIO服务器的工作线程数量
  workCount: 100
  #  SocketIO服务器的允许自定义请求
  allowCustomRequests: true
  #  SocketIO服务器的升级超时时间
  upgradeTimeout: 10000
  #  SocketIO服务器的ping超时时间
  pingTimeout: 60000
  #  SocketIO服务器的ping间隔时间
  pingInterval: 25000

1.3 创建配置类

java 复制代码
package com.tool.tooladmin.webSocket.config;

import com.corundumstudio.socketio.SocketConfig;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author yhc
 * @description SocketServerConfig
 */
@Configuration
public class SocketServerConfig {
    /**
     * SocketIO服务器的host
     */
    @Value("${socketio.host}")
    private String host;
    /**
     * SocketIO服务器的端口
     */
    @Value("${socketio.port}")
    private Integer port;
    /**
     * SocketIO服务器的boss线程数
     */
    @Value("${socketio.bossCount}")
    private int bossCount;
    /**
     * SocketIO服务器的work线程数
     */
    @Value("${socketio.workCount}")
    private int workCount;
    /**
     * 是否允许自定义请求
     */
    @Value("${socketio.allowCustomRequests}")
    private boolean allowCustomRequests;
    /**
     * SocketIO服务器的升级超时时间
     */
    @Value("${socketio.upgradeTimeout}")
    private int upgradeTimeout;
    /**
     * SocketIO服务器的ping超时时间
     */
    @Value("${socketio.pingTimeout}")
    private int pingTimeout;
    /**
     * SocketIO服务器的ping间隔时间
     */
    @Value("${socketio.pingInterval}")
    private int pingInterval;

    /**
     * 创建一个SocketIOServer实例,并设置相关配置。
     */
    @Bean
    public SocketIOServer socketIOServer() {
        SocketConfig socketConfig = new SocketConfig();
        socketConfig.setTcpNoDelay(true);
        socketConfig.setSoLinger(0);
        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
        buildSocketConfig(socketConfig, config);
        return new SocketIOServer(config);
    }

    /**
     * 创建一个SpringAnnotationScanner实例,用于扫描netty-socketIo的注解( @OnConnect、@OnEvent等)
     */
    @Bean
    public SpringAnnotationScanner springAnnotationScanner() {
        return new SpringAnnotationScanner(socketIOServer());
    }

    /**
     * 设置SocketIO服务器的配置,将SocketConfig配置项设置到com.corundumstudio.socketio.Configuration中
     * @param socketConfig
     * @param config
     */
    private void buildSocketConfig(SocketConfig socketConfig, com.corundumstudio.socketio.Configuration config) {
        config.setSocketConfig(socketConfig);
        config.setHostname(host);
        config.setPort(port);
        config.setBossThreads(bossCount);
        config.setWorkerThreads(workCount);
        config.setAllowCustomRequests(allowCustomRequests);
        config.setUpgradeTimeout(upgradeTimeout);
        config.setPingTimeout(pingTimeout);
        config.setPingInterval(pingInterval);
    }
}

1.4 创建消息实体类

java 复制代码
package com.tool.tooladmin.webSocket.domain;

import lombok.Data;

@Data
public class SocketMessageVo {
    /** 消息类型 */
    private String type;
    /** 消息内容 */
    private String content;
    /** 消息发送者 */
    private String from;
    /** 消息接收者 */
    private String to;
    /** 消息通道 */
    private String channel;
}

1.5 封装socket常用函数

java 复制代码
package com.tool.tooladmin.webSocket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.annotation.OnEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@Component
public class SocketUtil {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    //暂且把用户&客户端信息存在缓存
    public static ConcurrentMap<String, SocketIOClient> connectMap = new ConcurrentHashMap<>();

    @OnEvent(value = "CHANNEL_SYSTEM")
    public void systemDataListener(String receiveMsg) {
        if (!StringUtils.hasLength(receiveMsg)){
            return;
        }
        JSONObject msgObject = (JSONObject) JSON.parse(receiveMsg);
        String userFlag = String.valueOf(msgObject.get("from"));
        String content = String.valueOf(msgObject.get("content"));
        log.info("收到用户 : {} 推送到系统频道的一条消息 :{}",userFlag,content );
    }

    public void sendToAll(Map<String, Object> msg,String sendChannel) {
        if (connectMap.isEmpty()){
            return;
        }
        //给在这个频道的每个客户端发消息
        for (Map.Entry<String, SocketIOClient> entry : connectMap.entrySet()) {
            entry.getValue().sendEvent(sendChannel, msg);
        }
    }

    public void sendToOne(String userFlag, Map<String, Object> msg,String sendChannel) {
        //拿出某个客户端信息
        SocketIOClient socketClient = getSocketClient(userFlag);
        if (Objects.nonNull(socketClient) ){
            //单独给他发消息
            socketClient.sendEvent(sendChannel,msg);
        }
    }

    /***
     * 广播在线用户数
     */
    @OnEvent(value = "onlineUsers")
    public void broadcastOnlineUsers() {
        int count = connectMap.size();
        System.out.println("当前在线用户数:" + count);
        if (!connectMap.isEmpty()){
            for (Map.Entry<String, SocketIOClient> entry : connectMap.entrySet()) {
                entry.getValue().sendEvent("onlineUsers", count);
                System.out.println("已向用户:" + entry.getKey() + " 发送在线用户数:" + count);
            }
        }
    }


    /**
     * 识别出客户端
     * @param userFlag
     * @return
     */
    public SocketIOClient getSocketClient(String userFlag){
        SocketIOClient client = null;
        if (StringUtils.hasLength(userFlag) &&  !connectMap.isEmpty()){
            for (String key : connectMap.keySet()) {
                if (userFlag.equals(key)){
                    client = connectMap.get(key);
                }
            }
        }
        return client;
    }
}

1.6 创建 socket handler

负责客户端的连接、断开等

java 复制代码
package com.tool.tooladmin.webSocket;

import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class SocketHandler {
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private SocketIOServer socketIoServer;
    @PostConstruct
    private void start(){
        try {
            socketIoServer.start();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @PreDestroy
    private void destroy(){
        try {
            socketIoServer.stop();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @OnConnect
    public void connect(SocketIOClient client) {
        String userFlag = client.getHandshakeData().getSingleUrlParam("userFlag");
        SocketUtil.connectMap.put(userFlag, client);
        log.info("客户端: "+ userFlag+ "已连接");
    }
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        String userFlag = client.getHandshakeData().getSingleUrlParam("userFlag");
        log.info("客户端:" + userFlag + "断开连接");
        SocketUtil.connectMap.remove(userFlag, client);
    }
}

1.7 创建前端VUE页面

发现使用VUE一直掉线和存在无法使用,可能是用法不对,使用html倒是正常,未发现问题点,等发现后再调整代码

html 复制代码
<template>
  <h1>聊天室</h1>
  <h5>当前在线用户: {{ onlineUsers }}</h5>
  <div>
    <ul>
      <li v-for="msg in messages" :key="msg">{{ msg }}</li>
    </ul>
  </div>
  <input v-model="newMessage" @keyup.enter="sendMessage" placeholder="输入消息...">
  <button @click="sendMessage">发送</button>
</template>

<script setup>
import {ref, onMounted, onUnmounted} from 'vue';
import {io} from 'socket.io-client';

const newMessage = ref('');
const messages = ref([]);
const onlineUsers = ref(0);

let socket = null;

onMounted(() => {
  // 替换为你的 Socket.IO 服务器地址
  const userFlag = 'userFlag=用户' + Math.floor(Math.random() * 1000);
  socket = io('http://localhost:8889', {
    // transports: ['websocket'], // 指定传输方式,如WebSocket
    autoConnect: true, // 是否自动连接
    reconnection: true, // 是否自动重新连接
    // reconnectionAttempts: 3, // 重新连接尝试次数
    // reconnectionDelay: 1000, // 重新连接延迟时间(毫秒)
    query: { userFlag: userFlag }, // 自定义查询参数
  });

  socket.on('connect', () => {
    console.log('已连接到聊天室');
  });

  socket.on('message', (data) => {
    console.log('222222');
    messages.value.push(data);
  });

  socket.on('disconnect', () => {
    console.log('已断开连接');
  });

  socket.on('error', (error) => {
    console.error('Socket.IO 错误:', error);
  });

  socket.on('onlineUsers', (count) => {
    console.log('当前在线用户:', count)
    onlineUsers.value = count;
  });

});

onUnmounted(() => {
  if (socket) {
    socket.disconnect();
  }
});

const  sendMessage = () =>  {
  console.log('发送消息', newMessage.value)
  if (newMessage.value.trim()) {
    socket.emit('CHANNEL_SYSTEM', JSON.stringify({
      'content': newMessage.value,
      'from': 'user_JC'
    }));
    newMessage.value = '';
  }
}
</script>

<style scoped>
/* 添加样式以美化聊天界面 */
</style>
相关推荐
喵王叭1 小时前
【测试工具】 Postman 基本使用
javascript·测试工具·postman
编程百晓君3 小时前
Harmony OS开发-ArkTS语言速成五
javascript·harmonyos·arkts
明月看潮生3 小时前
青少年编程与数学 02-005 移动Web编程基础 15课题、移动应用开发
前端·青少年编程·编程与数学·移动web
qq_424317183 小时前
html+css+js网页设计 美食 好厨艺西餐美食企业网站模板6个页面
javascript·css·html
JINGWHALE14 小时前
设计模式 结构型 外观模式(Facade Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·外观模式
别发呆了吧5 小时前
vue路由模式面试题
前端·javascript·vue.js·前端面试题
等一场春雨5 小时前
React 中结合 antd 的 Input 组件实现防抖输入
前端·javascript·react.js
大莲芒6 小时前
[React] 生态有哪些
前端·react.js·前端框架
代码对我眨眼睛6 小时前
vite+vue3动态引入资源文件(问题已解决但离了个大谱)
开发语言·javascript·vue.js
疯狂的沙粒6 小时前
如何在 JavaScript 中实现日期格式化?
开发语言·前端·css·node.js