Node.js 如何管理socket.io中的用户

Node.js 如何管理socket.io中的用户

Socket.IO是一个库,可以在浏览器和服务器之间实现实时、双向和基于事件的通信。在Socket.io和Node.js中管理用户通常涉及处理用户连接、断开连接以及向特定用户或用户组广播消息。

前提条件

  • React JS
  • Node JS
  • Socket.io

在Node.js中管理socket.io用户的方法

首先,重要的是要注意,当创建新的socket时,它会被分配一个唯一的id,可以通过调用socket.id获得。这个id可以存储在用户对象中,并且我们可以分配一个识别符,比如用户名。对于前端,我们将使用React,对于后端则使用node.js和express。在主目录中创建两个文件夹,一个是server(后端),一个是client(前端)。当需要时,Socket.on将是一个被调用的事件,并且该事件被socket.emit调用,我们在其中调用事件并传递参数(如果需要)。

创建应用程序的步骤

后端设置

步骤1: 使用命令初始化项目

npm init
JavaScript

步骤2: 安装所需的依赖项:

npm i cors express nodemon socket.io http
JavaScript

后端项目结构

Node.js 如何管理socket.io中的用户

在后端的 package.json 文件中,更新的依赖关系将如下所示:

{
    "dependencies": {
        "cors": "^2.8.5",
        "express": "^4.18.2",
        "http": "^0.0.1-security",
        "nodemon": "^3.0.1",
        "socket.io": "^4.7.2"
    }
}
JavaScript

示例: Socket.on用于在用户加入前端时发出连接事件,后端会发出消息事件并发送用户加入的消息。 **** 定义连接、消息和其他用户功能,如添加、删除和获取用户。

// Filename - index.js
 
const express = require('express');
const socketio = require('socket.io');
const http = require('http');
const cors = require('cors');
const { addUser, removeUser, getUser,
    getUsersInRoom } = require("./users");
 
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(cors())
 
io.on("connection", (socket) => {
    socket.on('join', ({ name, room }, callback) => {
 
        const { error, user } = addUser(
            { id: socket.id, name, room });
 
        if (error) return callback(error);
 
        // Emit will send message to the user
        // who had joined
        socket.emit('message', {
            user: 'admin', text:
                `{user.name}, 
            welcome to room{user.room}.`
        });
 
        // Broadcast will send message to everyone
        // in the room except the joined user
        socket.broadcast.to(user.room)
            .emit('message', {
                user: "admin",
                text: `{user.name}, has joined`
            });
 
        socket.join(user.room);
 
        io.to(user.room).emit('roomData', {
            room: user.room,
            users: getUsersInRoom(user.room)
        });
        callback();
    })
 
    socket.on('sendMessage', (message, callback) => {
 
        const user = getUser(socket.id);
        io.to(user.room).emit('message',
            { user: user.name, text: message });
 
        io.to(user.room).emit('roomData', {
            room: user.room,
            users: getUsersInRoom(user.room)
        });
        callback();
    })
 
    socket.on('disconnect', () => {
        const user = removeUser(socket.id);
        if (user) {
            io.to(user.room).emit('message',
                {
                    user: 'admin', text:
                        `{user.name} had left`
                });
        }
    })
 
})
 
server.listen(process.env.PORT || 5000,
    () => console.log(`Server has started.`));
JavaScript
// Filename - User.js
 
const users = [];
 
const addUser = ({ id, name, room }) => {
    name = name.trim().toLowerCase();
    room = room.trim().toLowerCase();
 
    const existingUser = users.find((user) => {
        user.room === room && user.name === name
    });
 
    if (existingUser) {
        return { error: "Username is taken" };
    }
    const user = { id, name, room };
 
    users.push(user);
    return { user };
 
}
 
const removeUser = (id) => {
    const index = users.findIndex((user) => {
        user.id === id
    });
 
    if (index !== -1) {
        return users.splice(index, 1)[0];
    }
}
 
const getUser = (id) => users
    .find((user) => user.id === id);
 
const getUsersInRoom = (room) => users
    .filter((user) => user.room === room);
 
module.exports = {
    addUser, removeUser,
    getUser, getUsersInRoom
};
JavaScript

前端设置

步骤1: 在终端中使用以下命令安装React前端

npx create react-app client
JavaScript

步骤2: 在安装了React之后,在client文件夹内安装项目依赖项。

cd client
npm i query-string react-emoji react-router socket.io-client
JavaScript

前端项目结构

Node.js 如何管理socket.io中的用户

前端的 package.json 文件中更新的依赖项如下:

    "dependencies": {
        "@testing-library/jest-dom": "^5.17.0",
        "@testing-library/react": "^13.4.0",
        "@testing-library/user-event": "^13.5.0",
        "query-string": "^8.1.0",
        "react": "^18.2.0",
        "react-dom": "^18.2.0",
        "react-emoji": "^0.5.0",
        "react-router": "^6.17.0",
        "react-scripts": "5.0.1",
        "socket.io-client": "^4.7.2",
        "web-vitals": "^2.1.4"
    },
JavaScript

步骤3: 在App.js中,为加入页面和聊天页面创建路由,并导入这两个页面的组件以在该路由上显示。

文件名: App.js

// Filename - App.js
import React from 'react';
 
import Chat from './components/Chat/Chat';
import Join from './components/Join/Join';
 
import { BrowserRouter as Router, Route }
    from "react-router-dom";
 
const App = () => {
    return (
        <Router>
            <Route path="/" exact component={Join} />
            <Route path="/chat" component={Chat} />
        </Router>
    );
}
 
export default App;
JavaScript
// Filename - Chat/Chat.js
 
import React, { useState, useEffect } from "react";
import queryString from "query-string";
import io from 'socket.io-client';
import TextContainer from '../TextContainer/TextContainer';
import Messages from '../Messages/Messages';
import InfoBar from '../InfoBar/InfoBar';
import Input from '../Input/Input';
 
import "./Chat.css";
 
let connectionOptions = {
    "force new connection": true,
    "reconnectionAttempts": "Infinity",
    "timeout": 10000,
    "transports": ["websocket"]
};
 
let socket = io.connect('https://localhost:5000', connectionOptions);
 
 
const Chat = ({ location }) => {
 
    const [name, setName] = useState('');
    const [room, setRoom] = useState("");
    const [users, setUsers] = useState('');
    const [message, setMessage] = useState('');
    const [messages, setMessages] = useState([]);
 
    const ENDPOINT = 'localhost:5000';
 
    useEffect(() => {
        const { name, room } = queryString.parse(location.search);
 
        setName(name);
        setRoom(room);
 
        socket.emit('join', { name, room }, (error) => {
            if (error) {
                alert(error);
            }
        })
        return () => {
            socket.emit('disconnect');
            socket.off();
        }
 
    }, [ENDPOINT, location.search]);
 
    useEffect(() => {
        socket.on('message', (message) => {
            setMessages([...messages, message]);
        })
 
        socket.on("roomData", ({ users }) => {
            setUsers(users);
        });
    }, [messages, users])
 
    //Function for Sending Message
    const sendMessage = (e) => {
        e.preventDefault();
        if (message) {
            socket.emit('sendMessage', message, () => setMessage(''))
        }
    }
 
    console.log(message, messages);
 
    return (
        <div className="outerContainer">
            <div className="container">
 
                <InfoBar room={room} />
                <Messages messages={messages} name={name} />
                <Input message={message} setMessage={setMessage}
                    sendMessage={sendMessage} />
            </div>
            <TextContainer users={users} />
        </div>
    )
};
 
export default Chat;
JavaScript
// Filename - InfoBar/InfoBar.js
 
import React from 'react';
 
import './InfoBar.css';
 
const InfoBar = ({ room }) => (
    <div className="infoBar">
        <div className="leftInnerContainer">
            <h3>{room}</h3>
        </div>
        <div className="rightInnerContainer">
            <a href="/">Leave</a>
        </div>
    </div>
);
 
export default InfoBar;
JavaScript
// Filename - Input/Input.js
 
import React from 'react';
 
import './Input.css';
 
const Input = ({ setMessage, sendMessage, message }) => (
    <form className="form">
        <input
            className="input"
            type="text"
            placeholder="Type a message..."
            value={message}
            onChange={({ target: { value } }) => setMessage(value)}
            onKeyPress={event => event.key === 'Enter'
                ? sendMessage(event) : null}
        />
        <button className="sendButton"
            onClick={e => sendMessage(e)}>Send</button>
    </form>
)
 
export default Input;
JavaScript
// Filename - Join/Join.js
 
import React, { useState } from "react";
import { Link } from 'react-router-dom';
 
import './Join.css';
 
const Join = () => {
 
    const [name, setName] = useState('');
    const [room, setRoom] = useState("");
 
    return (
        <div className="joinOuterContainer">
            <div className="joinInnerContainer">
                <h1 className="heading">Join</h1>
                <div>
                    <input placeholder="Name"
                        className="joinInput"
                        type="text"
                        onChange=
                        {(event) => setName(event.target.value)} />
                </div>
 
                <div>
                    <input placeholder="Room"
                        className="joinInput mt-20"
                        type="text" onChange=
                        {(event) => setRoom(event.target.value)} />
                </div>
 
                <Link onClick={e => (!name || !room) ?
                    e.preventDefault() : null}
                    to={`/chat?name={name}&room={room}`
                    }>
                    <button className={'button mt-20'}
                        type="submit">Sign In
                    </button>
                </Link>
            </div>
        </div>
    );
};
 
export default Join;
JavaScript
// Filename - Messages/Message/Message.js
 
import React from 'react';
 
import './Message.css';
 
import ReactEmoji from 'react-emoji';
 
const Message = ({ message: { text, user }, name }) => {
    let isSentByCurrentUser = false;
 
    const trimmedName = name.trim().toLowerCase();
 
    if (user === trimmedName) {
        isSentByCurrentUser = true;
    }
 
    return (
        isSentByCurrentUser
            ? (
                <div className="messageContainer justifyEnd">
                    <p className="sentText pr-10">{trimmedName}</p>
 
 
                    <div className="messageBox backgroundBlue">
                        <p className="messageText colorWhite">
                            {ReactEmoji.emojify(text)}
                        </p>
 
 
                    </div>
                </div>
            )
            : (
                <div className="messageContainer justifyStart">
                    <div className="messageBox backgroundLight">
                        <p className="messageText colorDark">
                            {ReactEmoji.emojify(text)}
                        </p>
 
 
                    </div>
                    <p className="sentText pl-10 ">{user}</p>
 
 
                </div>
            )
    );
}
 
export default Message;
JavaScript
// Filename - Messages/Messages.js
 
import React from 'react';
 
import Message from './Message/Message';
 
import './Messages.css';
 
const Messages = ({ messages, name }) => (
    <div>
        {messages.map((message, i) => <div key={i}>
            <Message message={message} name={name} />
        </div>)}
 
    </div>
);
 
export default Messages;
JavaScript
// Filename TextContainer/TextContainer.js
 
import React from "react";
 
import onlineIcon from "../../icons/onlineIcon.png";
 
import "./TextContainer.css";
 
const TextContainer = ({ users }) => (
    <div className="textContainer">
        {users ? (
            <div>
                <h1>People currently chatting:</h1>
                <div className="activeContainer">
                    <h2>
                        {users.map(({ name }) => (
                            <div
                                key={name}
                                className="activeItem"
                            >
                                {name}
                                <img
                                    alt="Online Icon"
                                    src={onlineIcon}
                                />
                            </div>
                        ))}
                    </h2>
                </div>
            </div>
        ) : null}
    </div>
);
 
export default TextContainer;
JavaScript

CSS

/* Filename - Chat/Chat.css */
 
.outerContainer {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: #1A1A1D;
}
 
.container {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    background: #FFFFFF;
    border-radius: 8px;
    height: 60%;
    width: 35%;
}
 
@media (min-width: 320px) and (max-width: 480px) {
    .outerContainer {
        height: 100%;
    }
 
    .container {
        width: 100%;
        height: 100%;
    }
}
 
@media (min-width: 480px) and (max-width: 1200px) {
    .container {
        width: 60%;
    }
}
JavaScript

CSS

/* Filename - InfoBar/InfoBar.css*/
 
.infoBar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: #2979FF;
    border-radius: 4px 4px 0 0;
    height: 60px;
    width: 100%;
}
 
.leftInnerContainer {
    flex: 0.5;
    display: flex;
    align-items: center;
    margin-left: 5%;
    color: white;
}
 
.rightInnerContainer {
    display: flex;
    flex: 0.5;
    justify-content: flex-end;
    margin-right: 5%;
}
 
.onlineIcon {
    margin-right: 5%;
}
JavaScript

CSS

/* Filename - Input/Input.css */
 
.form {
    display: flex;
    border-top: 2px solid #D3D3D3;
}
 
.input {
    border: none;
    border-radius: 0;
    padding: 5%;
    width: 80%;
    font-size: 1.2em;
}
 
input:focus,
textarea:focus,
select:focus {
    outline: none;
}
 
.sendButton {
    color: #fff !important;
    text-transform: uppercase;
    text-decoration: none;
    background: #2979FF;
    padding: 20px;
    display: inline-block;
    border: none;
    width: 20%;
}
JavaScript

CSS

/* Filename - Join/Join.css*/
 
html,
body {
    font-family: 'Roboto', sans-serif;
    padding: 0;
    margin: 0;
}
 
#root {
    height: 100vh;
}
 
* {
    box-sizing: border-box;
}
 
.joinOuterContainer {
    display: flex;
    justify-content: center;
    text-align: center;
    height: 100vh;
    align-items: center;
    background-color: #1A1A1D;
}
 
.joinInnerContainer {
    width: 20%;
}
 
.joinInput {
    border-radius: 0;
    padding: 15px 20px;
    width: 100%;
}
 
.heading {
    color: white;
    font-size: 2.5em;
    padding-bottom: 10px;
    border-bottom: 2px solid white;
}
 
.button {
    color: #fff !important;
    text-transform: uppercase;
    text-decoration: none;
    background: #2979FF;
    padding: 20px;
    border-radius: 5px;
    display: inline-block;
    border: none;
    width: 100%;
}
 
.mt-20 {
    margin-top: 20px;
}
 
@media (min-width: 320px) and (max-width: 480px) {
    .joinOuterContainer {
        height: 100%;
    }
 
    .joinInnerContainer {
        width: 90%;
    }
}
 
button:focus {
    outline: 0;
}
JavaScript

CSS

/* Filename - Messages/Message/Message.css */
 
.messageBox {
    background: #F3F3F3;
    border-radius: 20px;
    padding: 5px 20px;
    color: white;
    display: inline-block;
    max-width: 80%;
}
 
.messageText {
    width: 100%;
    letter-spacing: 0;
    float: left;
    font-size: 1.1em;
    word-wrap: break-word;
}
 
.messageText img {
    vertical-align: middle;
}
 
.messageContainer {
    display: flex;
    justify-content: flex-end;
    padding: 0 5%;
    margin-top: 3px;
}
 
.sentText {
    display: flex;
    align-items: center;
    font-family: Helvetica;
    color: #828282;
    letter-spacing: 0.3px;
}
 
.pl-10 {
    padding-left: 10px;
}
 
.pr-10 {
    padding-right: 10px;
}
 
.justifyStart {
    justify-content: flex-start;
}
 
.justifyEnd {
    justify-content: flex-end;
}
 
.colorWhite {
    color: white;
}
 
.colorDark {
    color: #353535;
}
 
.backgroundBlue {
    background: #2979FF;
}
 
.backgroundLight {
    background: #F3F3F3;
}
JavaScript

CSS

/* Filename Messages/Messages.css */
 
.messages {
    padding: 5% 0;
    overflow: auto;
    flex: auto;
 }
JavaScript

CSS

/* Filename - TextContainer/TextContainer.css */
 
.textContainer {
    display: flex;
    flex-direction: column;
    margin-left: 100px;
    color: rgb(201, 25, 25);
    height: 60%;
    justify-content: space-between;
}
 
.activeContainer {
    display: flex;
    align-items: center;
    margin-bottom: 50%;
}
 
.activeItem {
    display: flex;
    align-items: center;
}
 
.activeContainer img {
    padding-left: 10px;
}
 
.textContainer h1 {
    margin-bottom: 0px;
}
 
@media (min-width: 320px) and (max-width: 1200px) {
    .textContainer {
        display: none;
    }
}
JavaScript

运行后端步骤: 进入后端文件夹并打开终端,输入以下命令。

npm start
JavaScript

运行前端的步骤: 进入文件夹并打开终端,输入以下命令。

npm start
JavaScript

输出: 打开浏览器,并输入 localhost 3000来查看应用程序运行。

Node.js 如何管理socket.io中的用户

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程

登录

注册