【网络应用程序设计】实验四:物联网监控系统

个人博客:https://alive0103.github.io/

代码在GitHub:https://github.com/Alive0103/XDU-CS-lab

能点个Star就更好了,欢迎来逛逛哇~❣

主播写的刚够满足基本功能,多有不足,仅供参考,还请提PR指正,很高兴能帮到你

代码指路:GitHub

一、 实验内容

使用websocket建立网络聊天室,聊天室成员既包括web用户,也包括物联网节点,web用户可对物联网节点进行查询和控制。物联网节点可以是虚拟节点,如:mqtt.fx。

二、实验准备

  • 腾讯云虚拟机
    • Docker 环境
    • EMQX (服务器端)
    • Python编译环境 (WEB端)
  • 本机
    • 下载MQTT.fx (设备端)

注意事项:各个端的对应端口记得在虚拟机里开放。

网络拓扑​

text 复制代码
graph LR
    Web用户 -->|WS:8083| EMQX
    物联网设备 -->|MQTT:1883| EMQX
    Python服务器 -->|HTTP:8080| Web用户

三、核心配置

装好EMQX打开http://你的服务器IP:18083/,开始配置测试用户

1. 用户认证配置/权限规则配置




text 复制代码
**用户认证配置表**
| 用户名     | 密码         | 用户类型       |
|------------|--------------|----------------|
| web_user   | web_123456   | Web控制端       |
| sensor01   | iot_123456   | 物联网设备      |

**权限规则配置表**
| 用户名     | 操作类型 | 主题过滤器     | 权限状态 |
|------------|----------|----------------|----------|
| web_user   | 发布     | control/#      | 允许     |
| web_user   | 订阅     | status/#       | 允许     |
| sensor01   | 发布     | status/#       | 允许     |
| sensor01   | 订阅     | control/#      | 允许     |

2. MQTT.fx配置



写WEB端服务

核心逻辑​

javascript 复制代码
// MQTT连接配置(对应实验参数)
const client = mqtt.connect('ws://你的服务器IP:8083/mqtt', {
    username: 'web_user',
    password: 'web_123456',
    clientId: `web_${Date.now()}`
});

// 主题订阅管理
const subscribeDevice = (deviceId) => {
    client.subscribe(`status/${deviceId}`, { qos: 1 }, (err) => {
        if (!err) addLog(`已订阅设备${deviceId}状态`);
    });
};

// 指令下发验证
const validateCommand = (input) => {
    const pattern = /^[a-z0-9]{8}:(start|stop|reset|led=(on|off))$/;
    return pattern.test(input);
};

完整代码

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>物联网设备控制台</title>
    <script src="https://unpkg.com/[email protected]/dist/mqtt.min.js"></script>
    <style>
        body {
            margin: 0;
            padding: 20px;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background-color: #fff;
        }

        .status-bar {
            color: #2ecc71; /* 连接中状态颜色 */
            font-size: 14px;
            margin-bottom: 15px;
        }

        .message-container {
            height: 300px;
            border: 1px solid #e0e0e0;
            border-radius: 4px;
            margin-bottom: 15px;
            overflow-y: auto;
            padding: 10px;
        }

        .input-group {
            display: flex;
            gap: 10px;
        }

        #commandInput {
            flex: 1;
            padding: 8px 12px;
            border: 1px solid #e0e0e0;
            border-radius: 4px;
            font-size: 14px;
        }

        #sendButton {
            background: #007bff;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <!-- 状态显示 -->
    <div class="status-bar" id="status">状态:连接中...</div>
    
    <!-- 消息展示区域 -->
    <div class="message-container" id="messages"></div>
    
    <!-- 控制输入区 -->
    <div class="input-group">
        <input 
            type="text" 
            id="commandInput" 
            placeholder="输入控制指令(格式:设备ID:指令)"
            value="device01:led=on"
        >
        <button id="sendButton" onclick="sendCommand()">发送</button>
    </div>

    <script>
        // ================= MQTT连接配置 =================
        const client = mqtt.connect('ws://你的服务器IP:8083/mqtt', {
            username: 'web_user',
            password: 'web_user',
            clientId: 'web_user' 
        });

        // ================= 连接状态管理 =================
        client.on('connect', () => {
            document.getElementById('status').textContent = '状态:已连接';
            document.getElementById('status').style.color = '#27ae60';
            
            // 自动订阅设备状态主题
            client.subscribe('status/#', (err) => {
                if (!err) addSystemMessage('成功订阅设备状态主题');
            });
        });

        client.on('error', (err) => {
            document.getElementById('status').textContent = `错误:${err.message}`;
            document.getElementById('status').style.color = '#e74c3c';
        });

        // ================= 消息处理逻辑 =================
        client.on('message', (topic, payload) => {
            const msg = document.createElement('div');
            msg.textContent = `[${new Date().toLocaleTimeString()}] ${topic}: ${payload.toString()}`;
            msg.style.padding = '5px 0';
            msg.style.borderBottom = '1px solid #eee';
            document.getElementById('messages').appendChild(msg);
            messages.scrollTop = messages.scrollHeight; // 自动滚动到底部
        });

        // ================= 设备控制逻辑 =================
        const sendCommand = () => {
            const input = document.getElementById('commandInput').value.trim();
            if (!input) return;

            try {
                const [deviceId, command] = input.split(':');
                if (!deviceId || !command) throw new Error('指令格式错误');
                
                client.publish(`control/${deviceId}`, command);
                addSystemMessage(`已发送指令到 ${deviceId}`);
            } catch (err) {
                addSystemMessage(`错误:${err.message}`, true);
            }
        }

        // ================= 辅助函数 =================
        const addSystemMessage = (text, isError = false) => {
            const msg = document.createElement('div');
            msg.textContent = `[系统] ${new Date().toLocaleTimeString()} ${text}`;
            msg.style.color = isError ? '#e74c3c' : '#7f8c8d';
            msg.style.fontSize = '12px';
            msg.style.margin = '4px 0';
            document.getElementById('messages').appendChild(msg);
            messages.scrollTop = messages.scrollHeight;
        }
    </script>
</body>
</html>

启动WEB:

bash 复制代码
# 1. 部署到服务器指定目录
cd /home/lighthouse/chat-server/public
vim index.html # 粘贴上述代码

# 2. 重启HTTP服务
pkill python3
nohup python3 -m http.server 8080 --bind 0.0.0.0 >/dev/null 2>&1 &

# 3. 访问地址
echo "控制台地址:http://你的服务器IP:8080"

建议:用户名和密码全设成一样的,记不住一点。。。

四、实验结果

操作流程:

text 复制代码
graph TD
    A[启动Web服务] --> B[访问http://IP:8080]
    B --> C{状态显示"已连接"?}
    C -->|是| D[发送测试指令]
    C -->|否| E[检查控制台错误]
    D --> F[查看EMQX主题监控]

1. 在输入框输入:`device01:led=on`
2. 点击"发送"按钮
3. 在服务器执行:
   docker exec emqx_GSsj emqx_ctl topics
   # 应看到 control/device01 主题有消息统计

功能验证

测试场景 操作步骤 预期结果
Web用户订阅状态 浏览器控制台输入client.subscribe('status/#') 成功接收所有设备状态消息
非法主题发布拦截 使用web_user尝试发布到status/device01 返回PUBLISH not authorized错误
设备离线消息保留 设备离线时发送控制指令,设备上线后 立即执行离线期间最后一条指令
跨用户权限隔离 使用sensor01尝试订阅system/alerts 连接被立即断开

还是很酷炫的hh,撒花撒花!

相关推荐
塞尔维亚大汉6 分钟前
【鸿蒙南向开发】OpenHarmony小型系统内核(LiteOS-A)【文件系统】下
物联网·嵌入式·harmonyos
苏牧keio9 分钟前
Win10安装Linux子系统WSL
linux
无心水11 分钟前
基础服务系列-Jupyter Notebook 支持Java
ide·python·jupyter
风静雪冷16 分钟前
Ubuntu中选择Python虚拟环境
开发语言·python
MaYuKang17 分钟前
「ES数据迁移可视化工具(Python实现)」支持7.x索引数据互传
大数据·数据库·python·mysql·elasticsearch
kadog35 分钟前
《Python3网络爬虫开发实战(第二版)》配套案例 spa6
开发语言·javascript·爬虫·python
徒慕风流36 分钟前
利用Python爬虫实现百度图片搜索的PNG图片下载
开发语言·爬虫·python
放飞自我的Coder36 分钟前
【win11 安装WSL2 详解一遍过!!】
linux·ubuntu
蜕变的土豆41 分钟前
Ubuntu下软件运行常见异常退出问题汇总分析
linux·ubuntu
蹦蹦跳跳真可爱5891 小时前
Python----深度学习(基于深度学习Pytroch线性回归和曲线回归)
pytorch·python·深度学习·神经网络·回归·线性回归