socket.io react/node 如何在没有当前连接的情况下更新连接?

问题描述 投票:0回答:1

我正在尝试实现 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;

reactjs node.js socket.io
1个回答
0
投票

看来我能够解决这个问题。 首先,我在套接字实例之外创建了一个数组来存储后端的所有在线用户, 对于某些场景来说可能并不理想,但对于我想要的,它在后端足够接近。 然后我使用过滤器返回没有当前用户的在线用户列表。

代码: 后端:

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
});
© www.soinside.com 2019 - 2024. All rights reserved.