socket.io分房间交流

基本详情看这里

Socket.IO 是一个库,可以在客户端和服务器之间实现 低延迟双向 和 基于事件的 通信.

效果展示

安装依赖

// 后端插件安装
npm i socket.io -S
// 前端插件安装
npm i socket.io-client -S

前端搭建及逻辑

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

const username = ref('');
const socket = io('http://192.168.1.92:3000');
const val = ref('');
const messages = ref([]);
const isUsernameConfirmed = ref(false);
const room = ref('');
const userList = ref([]);
const messageContainer = ref(null);

const confirmUsername = () => {
    if (!username.value) {
        alert('请输入用户名');
        return;
    }
    isUsernameConfirmed.value = true;
};

const joinRoom = () => {
    if (room.value.trim().length === 0) {
        return; // 禁止加入空房间
    }
    if (!room.value) {
        alert('请输入房间号');
        return;
    }
    socket.emit('joinRoom', JSON.stringify({ username: username.value, room: room.value }));
};

const sendMessage = () => {
    if (!val.value) {
        alert('请输入消息内容');
        return;
    }

    const obj = {
        username: username.value,
        mes: val.value,
        room: room.value, // 将房间号也一起发送到服务器
    };
    socket.emit('chat message', JSON.stringify(obj));
    messages.value.push({
        username: username.value,
        mes: val.value,
        type: 'sent',
    });
    val.value = ''; // 重置输入框的值

    // 滚动到底部
    nextTick(() => {
        scrollToBottom();
    });
};
const scrollToBottom = () => {
    if (messageContainer.value) {
        messageContainer.value.scrollTop = messageContainer.value.scrollHeight;
    }
};

onMounted(() => {
    scrollToBottom();
    socket.on('userList', (data) => {
        console.log(data);
        userList.value = JSON.parse(data);
    });

    socket.on('receiveMessage', function (data) {
        const message = JSON.parse(data);
        console.log(message);
        if (message.username === username.value) {
            // 不渲染自己发送的消息
            return;
        }
        messages.value.push({
            username: message.username,
            mes: message.mes,
            type: 'received',
        });

        // 滚动到底部
        nextTick(() => {
            scrollToBottom();
        });
    });
});
</script>

<template>
    <div>
        <div v-if="!isUsernameConfirmed">
            <input v-model="username" placeholder="请输入聊天名称..." @keyup.enter="confirmUsername" />
            <button @click="confirmUsername">确定</button>
        </div>

        <div v-else>
            <input v-model="room" placeholder="请输入房间号" @keyup.enter="joinRoom" />
            <button @click="joinRoom">加入房间</button>

            <div>房间成员列表:</div>
            <ul>
                <li v-for="user in userList" :key="user">{{ user }}</li>
            </ul>
            <div class="message-container" ref="messageContainer">
                <ul>
                    <li v-for="message in messages" :key="message.content"
                        :style="{ textAlign: message.type === 'sent' ? 'right' : 'left', color: message.type === 'sent' ? 'blue' : 'red' }">
                        <span v-if="message.type === 'received'" class="nick">{{ message.username }}:</span>
                        {{ message.mes }}
                    </li>
                </ul>
            </div>
            <input id="m" autocomplete="off" v-model="val" @keyup.enter="sendMessage" :disabled="!room" /><button
                @click="sendMessage">Send</button>
        </div>
    </div>
</template>

<style scoped>
ul#messages {
    list-style-type: none;
    margin: 0;
    padding: 0;
}

li {
    margin: 5px 0;
}

.nick {
    font-weight: bold;
}

.message-container {
    height: 300px;
    /* 设置容器高度 */
    overflow-y: auto;
    /* 启用垂直滚动 */
}
</style>

后端搭建及逻辑

const express = require("express");
const app = express();
const { Server } = require("socket.io");
const io = new Server(3000, {
  cors: {
    origin: ["http://192.168.1.92:8080"],
  },
});

const userList=[] // 存放聊天数据
const rooms = {}; // 群聊房间列表

io.on("connection", (socket) => {
  // 多人聊天精简版
  // 开始监听前端的socket请求连接(前端每次执行一次io()方法就就会发起一次socket请求)
  console.log("开始了");
  // 接收客户端发来的数据
  socket.on("joinRoom", (data) => {
    const { username, room } = JSON.parse(data);

    socket.join(room);

    if (!rooms[room]) {
      rooms[room] = [];
    }

    const user = { id: socket.id, username: username };
    rooms[room].push(user);
    console.log(`${username} joined room ${room}`);

    const userList = rooms[room].map((user) => user.username);
    io.to(room).emit("userList", JSON.stringify(userList));
  });

  socket.on("chat message", (data) => {
    const obj = JSON.parse(data);
    console.log("Received message:", obj.username, obj.mes);

    // 将消息发送给房间内的其他用户
    let roomUsers = rooms[obj.room];

    if (!Array.isArray(roomUsers)) {
      roomUsers = [];
    }

    roomUsers.forEach((user) => {
      if (user.id !== socket.id) {
        io.to(user.id).emit(
          "receiveMessage",
          JSON.stringify({ username: obj.username, mes: obj.mes })
        );
      }
    });
  });

  // 私人聊天版本
  // console.log('connection',socket.id);
  const username = socket.handshake.query.username;
  if (!username) return;

  const userInfo = userList.find((user) => user.username === username);
  if (userInfo) {
    userInfo.id = socket.id;
  } else {
    userList.push({
      id: socket.id,
      username,
    });
    // 监听用户断开连接事件,将用户从用户列表中移除
    socket.on("disconnect", () => {
      //多人聊天断开socket请求
      console.log("user disconnect --- 退出了了天", username);
      io.emit("tuichu", {
        msg: `用户${username}推出了聊天界面,选择某用户聊条吧...`,
      });

      Object.keys(rooms).forEach((room) => {
        const index = rooms[room].findIndex((user) => user.id === socket.id);
        if (index > -1) {
          rooms[room].splice(index, 1);
          const userList = rooms[room].map((user) => user.username);
          io.to(room).emit("userList", JSON.stringify(userList));
        }
      });
    });
  }
  // console.log(userList)
  io.emit("online", {
    userList,
  });
});

app.listen(8000, (err) => {
  console.log("服务器启动成功");
});

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值