node+react 使用WebSocket 实现实时聊天功能

为什么会使用WebSocket

        WebSocket 常用于需要低延迟、实时数据更新和双向通信的场景,例如实时聊天、在线游戏、金融交易、协同办公、物联网、数据可视化、在线教育以及直播和流媒体应用等,以优化用户体验和提高系统性能。

代码实现

如果你是写的typescript 可以先定义数据类型

export enum ESendType {
    text = 'text', // 用户消息
    message = 'message', // 系统提示消息
    image = 'image', // 发送图片
    video = 'video' // 发送视频
};

export interface MessageData {
    sendType: keyof typeof ESendType; // 发送类型
    content: string; // 发送内容
    timestamp: number; // 发送时间
    isMy: boolean; 
};

服务端

    1. 安装ws

npm install ws @types/ws

   2.开启WebSocket服务

import WebSocket from 'ws';
import { MessageData } from '../types/ISocket'
function snedText(content: string, sendType: MessageData['sendType'] = 'message') {
    return JSON.stringify({
        sendType,
        content,
        timestamp: Date.now(),
        isMy: false
    } as MessageData);
}
export default () => {
    const wss = new WebSocket.Server({ port: 8088 });
    let client1: WebSocket | null = null;
    let client2: WebSocket | null = null;

    wss.on('connection', (ws) => { // 开启连接
        if (!client1) {
            client1 = ws;
            client1.send(snedText("等待匹配"));
        } else if (!client2) {
            client2 = ws;
            client2.send(snedText("可以开始聊天了"));
            client1.send(snedText("匹配完成,可以开始聊天了"));
        }
        // 接收客户端消息
        ws.on('message', (message) => {
            const data: MessageData = JSON.parse(message as unknown as string);
            if (ws === client1) {
                if (client2 && client2.readyState === WebSocket.OPEN) {
                    data.isMy = !data.isMy
                    client2.send(JSON.stringify(data));
                }
            } else if (ws === client2) {
                if (client1 && client1.readyState === WebSocket.OPEN) {
                    data.isMy = !data.isMy
                    client1.send(JSON.stringify(data));
                }
            }
        });
        ws.on('close', () => {
            if (ws === client1) {
                client1 = null;
                if (client2) client2.send(snedText('第一个用户已断开连接'));
            } else if (ws === client2) {
                client2 = null;
                if (client1) client1.send(snedText('第二个用户已断开连接'));
            }
            console.log('客户端断开连接');
        });

        ws.on('error', (err) => {
            console.log(err);
        });
    })
}

前端

import { useEffect, useState } from "react";
import { MessageData } from '../../types/ISocket';
export default () => {
    const [socket, setSocket] = useState<WebSocket | null>(null);
    const [isSocketConnect, setIsSocketConnect] = useState<boolean>(false); // 连接状态
    const [sendValue, setSendValue] = useState<string>('');//发送内容
    const [messageList, setMessageList] = useState<MessageData[]>([]); // 消息记录
    // 发送消息
    function onSendMessage(event: React.KeyboardEvent) {
        if (event.code === 'Enter' && socket && isSocketConnect) {
            if (!sendValue) throw new Error('请输入内容');
            const value = {
                sendType: 'text',
                content: sendValue,
                timestamp: Date.now(),
                isMy: true,
            } as MessageData;
            socket.send(JSON.stringify(value));
            setMessageList((messageList) => messageList.concat(value));
            setSendValue(''); // 清空发送内容
        }
    }
    // 连接WebSocket
    function webSocketConnect() {
        const socket = new WebSocket('ws://localhost:8088');
        // WebSocket 连接已打开
        socket.onopen = () => {
            setSocket(socket);
            setIsSocketConnect(true);
            console.log('连接成功');
        };
        // 连接失败
        socket.onerror = (err) => {
            setIsSocketConnect(false);
            console.error(err, '连接失败');
        }
        // 关闭连接
        socket.onclose = () => setIsSocketConnect(false);
        // 接收服务端发送过来的消息
        socket.addEventListener('message', (event) => {
            const data = JSON.parse(event.data) as MessageData;
            // 合并服务端发送过来的消息
            setMessageList((messageList) => messageList.concat(data));
        });
    }
    useEffect(() => {
        webSocketConnect();
    }, []);
    return (
        <main>
            <div>
                <input type="text" value={sendValue} onChange={(e) => setSendValue(e.target.value)} onKeyUp={onSendMessage} />
            </div>
            <div style={{ margin: 'auto', width: '100%' }}>
                {messageList.map((item) => {
                    return <div style={{ color: item.isMy ? 'red' : 'black' }}>{item.content}</div>
                })}
            </div>
        </main>
    )
}

完整代码地址 yangjike123/WebSocket (github.com)

最终效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值