Socket.io 教程不是聊天应用程序(使用 React.js) Socket.io 教程不是聊天应用程序(使用 React.js)

2025-05-24

Socket.io 教程,不是聊天应用(使用 React.js)

Socket.io 教程,不是聊天应用(使用 React.js)

Socket.io 教程,不是聊天应用(使用 React.js)

最近,一位朋友咨询如何在仪表盘上实时(或尽可能接近实时)显示传感器数据。具体来说,他要读取一辆直线加速赛车的 OBD2 数据,并希望将一些数据点显示到外部系统。在考虑过 Kafka 这样的消息总线之后,我们觉得这太过分了。我建议使用 WebSocket,特别是 Socket.io 的实现。说实话,我从未使用过 Socket.io 库,只是在书上读到过,而且在寻找如何在聊天应用或多人游戏之外使用它们的信息时,我遇到了意想不到的困难。对我来说,将Socket.io上的聊天室应用教程转换成不同的用例需要一些时间,所以我决定写一个小演示,或许能帮助到那些以不同方式思考套接字的人。

3层

这是我们提出的超高级设计:
我喜欢表情包

对于我们的原型,我们将模拟传感器/读取器,设置后端服务器作为直通,并实现前端消费者。

后端

我们需要的两个包是:

const express = require("express");
const http = require("http");
const socketIo = require("socket.io");

//Port from environment variable or default - 4001
const port = process.env.PORT || 4001;

//Setting up express and adding socketIo middleware
const app = express();
const server = http.createServer(app);
const io = socketIo(server);

//Setting up a socket with the namespace "connection" for new sockets
io.on("connection", socket => {
    console.log("New client connected");

    //Here we listen on a new namespace called "incoming data"
    socket.on("incoming data", (data)=>{
        //Here we broadcast it out to all other sockets EXCLUDING the socket which sent us the data
       socket.broadcast.emit("outgoing data", {num: data});
    });

    //A special namespace "disconnect" for when a client disconnects
    socket.on("disconnect", () => console.log("Client disconnected"));
});

server.listen(port, () => console.log(`Listening on port ${port}`));
Enter fullscreen mode Exit fullscreen mode

让我们分解一下:

const express = require("express");
const http = require("http");
const socketIo = require("socket.io");

//Port from environment variable or default - 4001
const port = process.env.PORT || 4001;

//Setting up express and adding socketIo middleware
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
Enter fullscreen mode Exit fullscreen mode

如果你以前用过 express,那么大部分内容都没什么新意。这里唯一与 socket.io 相关的东西就是const io = socketIo(server);设置了一个新的 socket.io 服务器实例。

//Setting up a socket with the namespace "connection" for new sockets
io.on("connection", socket => {
    console.log("New client connected");

    //Here we listen on a new namespace called "incoming data"
    socket.on("incoming data", (data)=>{
        //Here we broadcast it out to all other sockets EXCLUDING the socket which sent us the data
       socket.broadcast.emit("outgoing data", {num: data});
    });

    //A special namespace "disconnect" for when a client disconnects
    socket.on("disconnect", () => console.log("Client disconnected"));
});

server.listen(port, () => console.log(`Listening on port ${port}`));
Enter fullscreen mode Exit fullscreen mode

这里我们设置了一个名为 的套接字命名空间,connection客户端将连接到该命名空间。初始连接建立后,我们将监听两个新的命名空间incoming datadisconnect。第一个命名空间也是我们的“生产者”或传感器/读取器将推送数据的地方。

在回调中,我们调用socket.broadcast.emit("outgoing data", {num: data});。广播标志很特殊,因为它允许我们向所有客户端(除了发送数据的客户端)发送数据。将数据发送回生产者毫无意义,因此我们在另一个命名空间 上进行广播outgoing data

您会注意到,我们在将数据推送到outgoing data命名空间之前对其进行了序列化。这将使前端更加清晰,并让您了解如何在一次发送中发送多个数据点。

断开连接命名空间是为客户端断开连接而保留的。这里是进行任何清理操作的好地方。例如,如果您的服务器正在跟踪已连接的客户端,那么这里就是将客户端状态更改为断开连接的好地方。

最后一行是设置我们的快速应用程序以开始监听。

模拟传感器

由于这是一个模拟,我们只需要发送一些随机数据。对于原型,这部分是用纯 Node.js 实现的,但 socket.io 有很多客户端库,它们在 Arduino 或其他可以连接 OBD2 传感器的微控制器上运行肯定会更好。别太过苛责我,这只是一个演示。

对于这个演示,我将演示“速度”值。

我们在这里使用的唯一包是socket.io-client

let socket = require('socket.io-client')('http://127.0.0.1:4001');

//starting speed at 0
let speed = 0;

//Simulating reading data every 100 milliseconds
setInterval(function () {
    //some sudo-randomness to change the values but not to drastically
    let nextMin = (speed-2)>0 ? speed-2 : 2;
    let nextMax = speed+5 < 140 ? speed+5 : Math.random() * (130 - 5 + 1) + 5;
    speed = Math.floor(Math.random() * (nextMax - nextMin + 1) + nextMin);

    //we emit the data. No need to JSON serialization!
    socket.emit('incoming data', speed);
}, 100);

Enter fullscreen mode Exit fullscreen mode

其中大部分内容应该很容易理解,所以本节会比较简短。
let socket = require('socket.io-client')('http://127.0.0.1:4001');设置要使用的包。我们首先将速度变量设置为 0。

let socket = require('socket.io-client')('http://127.0.0.1:4001');返回要使用的套接字连接。我们告诉它它在何处运行以及在哪个端口上运行。

setInterval在这里每 100 毫秒模拟一次微控制器和传感器之间的读取请求。设置下一个速度的数学方法只是一种“黑客”方法,每次都会稍微增加或减少速度,并且不允许速度超过 140 或低于 0。

socket.emit('incoming data', speed);是我们通过套接字发送数据的地方。我们将数据发送到incoming data上一节中在后端设置的命名空间中。

就是这样!很酷吧?

仪表板

我用 React 构建了这个,非常简单快捷。我不会深入 React 的细节,因为这超出了本文的范围。我将重点介绍如何从套接字中获取数据。话虽如此,我使用了react-d3-speedometer来显示速度计。不得不说,它的外观真的让我印象深刻!我还使用了我们在生产器上使用的同一个socket.io-client包。

这是 React 组件:

import React, {Component} from "react";
import socketIOClient from "socket.io-client";
import ReactSpeedometer from "react-d3-speedometer"

class App extends Component {
    constructor() {
        super();
        this.state = {
            response: 0,
            endpoint: "http://127.0.0.1:4001"
        };
    }

    componentDidMount() {
        const {endpoint} = this.state;
        //Very simply connect to the socket
        const socket = socketIOClient(endpoint);
        //Listen for data on the "outgoing data" namespace and supply a callback for what to do when we get one. In this case, we set a state variable
        socket.on("outgoing data", data => this.setState({response: data.num}));
    }

    render() {
        const {response} = this.state;
        return (
            <div style={{textAlign: "center"}}>
                <ReactSpeedometer
                    maxValue={140}
                    value={response}
                    needleColor="black"
                    startColor="orange"
                    segments={10}
                    endColor="red"
                    needleTransition={"easeElastic"}
                    ringWidth={30}
                    textColor={"red"}
                />
            </div>
        )
    }
}

export default App;
Enter fullscreen mode Exit fullscreen mode

state.response将保存来自后端的值,并且state.endpoint指向服务器所在的位置。神奇的事情发生在生命周期函数中componentDidMount()。对于不熟悉 React 的人来说,这个函数会在组件添加到 DOM 时调用。因此,我们将在这里连接到套接字并监听数据。

const socket = socketIOClient(endpoint);只需将我们连接到服务器并打开套接字连接。

socket.on("outgoing data", data => this.setState({response: data.num}));看起来很熟悉,不是吗?我们开始在outgoing data命名空间上列出。我们有一个回调,它接收响应并将状态设置为新值。

让我们看一下渲染函数:

    render() {
        const {response} = this.state;
        return (
            <div style={{textAlign: "center"}}>
                <ReactSpeedometer
                    maxValue={140}
                    value={response}
                    needleColor="black"
                    startColor="orange"
                    segments={10}
                    endColor="red"
                    needleTransition={"easeElastic"}
                    ringWidth={30}
                    textColor={"red"}
                />
            </div>
        )
    }
Enter fullscreen mode Exit fullscreen mode

ReactSpeedometer 组件有很多 props,你可以传递给它来自定义它。大部分 props 都一目了然,你也可以在这里阅读全部内容。我使用了needleTransition 的“easeElastic”属性,因为它看起来很酷,但“eastLinear”属性可能更适合用作速度计。点击此处了解过渡效果。

渲染函数提取速度的当前状态值,并将其传递给名为 的 ReactSpeedometer 属性value。这将更新速度计。

那么它看起来怎么样!

https://i.imgur.com/D4qzm7o.gif
(在此帖子中嵌入 gif 时出现问题。抱歉!)

它最终表现得更像一个转速表,但结果非常酷!

文章来源:https://dev.to/captainpandaz/a-socket-io-tutorial-that-isn-ta-chat-app-with-react-js-58jh
PREV
使用 Web Crypto API 进行端到端加密聊天
NEXT
开始 React 吧!30 天学会 React