我正在尝试实现 socket.io 来进行基本的聊天消息传递。 我想做的是通过套接字连接实时更新客户端, 下面是一个例子:
client one - logged in as x, client two - logged in as y.
因此,
在客户端二登录后,客户端一应该在其在线连接列表中看到 y,
但它在列表中看不到自己。所以它不能与自己“聊天”。
同样的想法也适用于客户端二,应该在列表中看到 x,因为它已经登录了,
意味着 2 个客户端在线,应该是:
client one - list [y]
,
client tow - list [x]
这里的问题是,在第二次连接之后,两个客户端都会看到 x,如果我刷新第二个客户端页面,两个客户端都会看到 y。这不是我想要的。
聊天消息尚未实现。 我一直在努力解决的是针对这种情况实时更新两个客户端。 知道我错过了什么吗?
服务器:
// Server
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const cors = require('cors');
require('dotenv').config();
const app = express();
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: process.env.CLIENT_ADDRESS,
methods: ['*'],
},
});
const PORT = process.env.SERVER_PORT;
app.use(cors());
const connections = {};
io.on('connection', (socket) => {
console.log(`User ${socket.id} connected`);
socket.on("store_user", (data) => {
connections[socket.id] = {
userName: data.userName,
socket: socket,
};
const onlineUsers = Object.values(connections)
.filter((user) => user.socket.id !== socket.id)
.map((user) => user.userName);
socket.emit('get_users', onlineUsers); // Send the list only to the current user.
socket.broadcast.emit('get_users', onlineUsers); //update everyone with the changes.
})
socket.on("disconnect", () => {
console.log(`User disconnected: ${socket.id}`);
delete connections[socket.id];
const onlineUsers = Object.values(connections).map((user) => user.userName);
io.emit('get_users', onlineUsers);
})
});
server.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
客户: 登录页面:
import {useState} from 'react'
import { useNavigate } from 'react-router-dom';
const Login = () => {
const [input, setInput] = useState("");
const navigate = useNavigate();
const setUserName = () => {
sessionStorage.setItem("userName", input);
navigate('/chat')
}
return (
<div>
<input type="text" onChange={(e)=>{setInput(e.target.value)}}/>
<button onClick={setUserName}>login</button>
</div>
)
}
export default Login
客户: 聊天页面:
// Client
import { useEffect, useState } from 'react';
import { io } from 'socket.io-client';
const serverURL = 'http://localhost:8000/';
const socket = io.connect(serverURL);
const Chat = () => {
const sessionUserName = sessionStorage.getItem('userName');
const [users, setUsers] = useState([]);
useEffect(() => {
const handleDisconnect = () => {
console.log('Disconnected');
};
const handleGetUsers = (data) => {
setUsers(data);
};
socket.on('disconnect', handleDisconnect);
socket.emit('store_user', { userName: sessionUserName });
socket.on('get_users', handleGetUsers);
}, [sessionUserName, socket]);
return (
<>
<button onClick={() => console.log(users)}>Show users</button>
<ul>
{users.map((user) => (
<li key={user}>{user}</li>
))}
</ul>
</>
);
};
export default Chat;
看来我能够解决这个问题。 首先,我在套接字实例之外创建了一个数组来存储后端的所有在线用户, 对于某些场景来说可能并不理想,但对于我想要的,它在后端足够接近。 然后我使用过滤器返回没有当前用户的在线用户列表。
代码: 后端:
require('dotenv').config();
const { Server } = require('socket.io');
const socketAuth = require('../middlewares/socketAuth');
const initSocket = (server) => {
const io = new Server(server, {
cors: {
origin: process.env.CLIENT_URL,
methods: ['*'], // can be change to => methods: ['GET', 'POST'],
credentials: true,
},
});
let onlineUsers = [];
io.use(socketAuth);
io.on('connection', (socket) => {
const userName = socket.handshake.query.userName;
console.log(`user: ${userName} conntected, socketId: ${socket.id}`);
onlineUsers.push({ socketId: socket.id, userName: userName });
//manage online users
//-----------------------------//
//emit users without the current one..
const filteredOnlineUsers = onlineUsers.filter((user) =>
user.socketId !== socket.id)
io.emit("getOnlineUsers", filteredOnlineUsers);
//-----------------------------//
socket.on('disconnect', () => {
console.log(`user ${socket.id} disconnected.`);
onlineUsers = onlineUsers.filter(user => user.socketId !==
socket.id);
});
});
return io;
};
module.exports = initSocket;
然后在客户端,我只是简单地调用该事件并将其加载到一个状态中, 注意:这只是部分代码,我只是分享我的答案的概念..
import {socket} from '../configs/socket'
useEffect(() => {
if(isLoggedIn && userName){
//send userName to server to store alongside the
//current socket.id connection.
socket.io.opts.query = {userName};
socket.connect();
}
socket.on("getOnlineUsers", (data) => {
console.log(data);
});
return () => {
socket.disconnect();
}
},[isLoggedIn, userName]);
如果您对我如何在客户端中的套接字进行配置感到好奇: 我也使用 cookies,因此“withCredentials”..
/* eslint-disable no-unused-vars */
import {io} from 'socket.io-client';
const URL = 'yourbackendURL'
export const socket = io(yourbackendURL, {
autoConnect: false,
withCredentials: true
});