求助:基于Flutter开发Web平台Modbus TCP程序的实现方案
Flutter Web 开发 Modbus TCP 程序的解决方案
问题原因
你使用的modbus_client_tcp这类库依赖 Dart 的dart:io模块实现 TCP 连接,但 Flutter Web 运行在浏览器沙箱环境中,受浏览器安全限制,无法直接调用原生 TCP Socket API,仅支持 WebSocket 通信,因此在 Chrome 中会出现连接失败的情况。
可行解决方案
目前最通用的解决方式是搭建WebSocket 转 Modbus TCP 的代理服务器,让 Flutter Web 通过 WebSocket 与代理通信,代理再与 Modbus 设备建立 TCP 连接并转发数据。
1. 搭建代理服务器(Node.js 示例)
用 Node.js 快速实现一个轻量代理,依赖ws(WebSocket 库)和modbus-serial(Modbus 客户端库):
const WebSocket = require('ws'); const ModbusRTU = require('modbus-serial'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws) => { const client = new ModbusRTU(); ws.on('message', async (data) => { try { const { ip, port, unitId, registerAddr } = JSON.parse(data); // 连接Modbus设备 if (!client.isOpen) { await client.connectTCP(ip, { port: port }); client.setID(unitId); } // 读取保持寄存器(单次读1个) const response = await client.readHoldingRegisters(registerAddr, 1); ws.send(JSON.stringify({ success: true, value: response.data[0] })); } catch (err) { ws.send(JSON.stringify({ success: false, error: err.message })); if (client.isOpen) client.close(); } }); ws.on('close', () => { if (client.isOpen) client.close(); }); }); console.log('WebSocket代理服务器运行在 ws://localhost:8080');
安装依赖:npm install ws modbus-serial,运行:node proxy.js
2. Flutter Web 端代码示例
修改原有代码,改用 WebSocket 与代理服务器通信:
import 'package:flutter/material.dart'; import 'dart:convert'; import 'dart:html' as html; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { final _ipController = TextEditingController(text: '192.168.1.100'); final _portController = TextEditingController(text: '502'); final _registerController = TextEditingController(text: '4098'); final _unitIdController = TextEditingController(text: '15'); String _result = ''; Future<void> _readHoldingRegister() async { setState(() => _result = '正在连接...'); try { // 连接代理服务器的WebSocket final ws = html.WebSocket('ws://localhost:8080'); ws.onOpen.listen((_) { // 发送请求参数到代理(注意寄存器地址转换:4xxxx类型需减去40001) ws.send(jsonEncode({ 'ip': _ipController.text, 'port': int.parse(_portController.text), 'unitId': int.parse(_unitIdController.text), 'registerAddr': int.parse(_registerController.text) - 40001, })); }); ws.onMessage.listen((event) { final res = jsonDecode(event.data); setState(() { if (res['success']) { // 转换为实际值(对应原代码的multiplier:0.1) final value = res['value'] * 0.1; _result = '寄存器值: ${value.toStringAsFixed(1)} V'; } else { _result = '错误: ${res['error']}'; } }); ws.close(); }); ws.onError.listen((error) { setState(() => _result = '连接代理失败: $error'); ws.close(); }); ws.onClose.listen((_) => setState(() => _result += '\n连接已关闭')); } catch (e) { setState(() => _result = '异常: $e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Modbus TCP Web 读取器')), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField(controller: _ipController, decoration: const InputDecoration(labelText: '设备IP')), TextField(controller: _portController, decoration: const InputDecoration(labelText: '设备端口'), keyboardType: TextInputType.number), TextField(controller: _unitIdController, decoration: const InputDecoration(labelText: 'Unit ID'), keyboardType: TextInputType.number), TextField(controller: _registerController, decoration: const InputDecoration(labelText: '寄存器地址'), keyboardType: TextInputType.number), const SizedBox(height: 20), ElevatedButton(onPressed: _readHoldingRegister, child: const Text('读取寄存器')), const SizedBox(height: 20), Text(_result), ], ), ), ); } }
注意事项
- 代理服务器需与 Modbus 设备处于同一网络,或具备访问设备的网络权限
- 生产环境需为代理服务器添加身份验证、请求限流、异常重试等增强逻辑
- 若你的 Modbus 设备支持直接 WebSocket 通信(部分工业设备支持 Modbus over WebSocket),可跳过代理直接连接
内容的提问来源于stack exchange,提问作者Camiu
相关产品推荐
相关产品推荐

