Flutter for OpenHarmony:multicast_dns 发现局域网设备,实现零配置网络 (mDNS/Bonjour/Zeroconf)(本地服务发现) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:multicast_dns 发现局域网设备,实现零配置网络 (mDNS/Bonjour/Zeroconf)(本地服务发现) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net

在这里插入图片描述

前言

在智能家居、打印机共享、P2P 文件传输等场景中,设备往往通过局域网互联。但 IP 地址是动态分配的,用户不可能去记 192.168.1.105。mDNS(Multicast DNS)允许设备在没有 DNS 服务器的情况下,通过 .local 域名互相发现。

multicast_dns 是 Dart 官方提供的库,用于发送和接收 mDNS 报文。你可以用它来查找局域网内的 HTTP 服务、Google Cast 设备,或者广播自己的服务供他人发现。

一、概念介绍/原理解析

1.1 基础概念

  • Multicast (组播): 向特定组播地址(224.0.0.251)发送数据包,局域网所有设备都能收到。
  • Service Type (服务类型): 如 _http._tcp (Web 服务), _googlecast._tcp (Chromecast)。
  • Record (记录): SRV (端口), TXT (元数据), A (IP)。

查询: _http._tcp.local

响应: 佳能打印机 @ 192.168.1.5

响应: 客厅电视 @ 192.168.1.8

手机终端

局域网组播

打印机

电视机

1.2 进阶概念

虽然 iOS/Android/macOS 系统层面都有 mDNS 服务(Bonjour/NsdManager),但 multicast_dns 是在 Dart 层直接操作 UDP Socket 实现的,因此跨平台一致性较好,但也受制于 Socket 权限。

二、核心 API/组件详解

2.1 基础用法

搜索局域网内的 Google Cast 设备。

import'package:multicast_dns/multicast_dns.dart';voidmain()async{final client =MDnsClient();await client.start();// 查询指针记录 (PTR)awaitfor(final ptr in client.lookup<PtrResourceRecord>(ResourceRecordQuery.serverPointer('_googlecast._tcp.local'),)){print('发现设备: ${ptr.domainName}');// 进一步查询详情 (SRV, IP)awaitfor(final srv in client.lookup<SrvResourceRecord>(ResourceRecordQuery.service(ptr.domainName),)){print('端口: ${srv.port}, 主机: ${srv.target}');}} client.stop();}
在这里插入图片描述

2.2 广播服务

目前库主要侧重于 客户端查询。要想作为服务端广播,通常需要更底层的 API 或者依赖平台插件(如 nsd)。但若是纯 Dart 环境,可以手动构造响应包(较复杂)。

三、常见应用场景

3.1 场景 1:查找打印机

搜索 _ipp._tcp_printer._tcp 服务。

awaitfor(final ptr in client.lookup<PtrResourceRecord>(ResourceRecordQuery.serverPointer('_ipp._tcp.local'),)){print('Found Printer: ${ptr.domainName}');}
在这里插入图片描述

3.2 场景 2:局域网对战游戏

两台手机进入同一 WiFi,互相发现对方建立连接。

// 搜索自定义的游戏服务类型final query =ResourceRecordQuery.serverPointer('_mygame._udp.local');awaitfor(final ptr in client.lookup<PtrResourceRecord>(query)){// 连接到发现的对战房间_connectToRoom(ptr.domainName);}
在这里插入图片描述

3.3 场景 3:开发调试

在 PC 开启服务,手机自动寻找调试端口,免去手动输入 IP。

// PC端可能通过 avahi 广播了 _debug._tcpfinal srv =await client.lookup<SrvResourceRecord>(ResourceRecordQuery.service('pc-debug._debug._tcp.local'),).first;print('Connect to Debugger: ${srv.target}:${srv.port}');
在这里插入图片描述

四、OpenHarmony 平台适配

4.1 组播权限与锁

在移动设备上监听组播需要特殊权限,并且在某些省电模式下,系统会过滤组播包。在 OpenHarmony 上,记得申请 INTERNET 权限。

4.2 网络绑定

多网卡设备(如同时开启 WiFi 和热点)可能导致 mDNS 发到了错误的网卡。MDnsClient 允许指定 RawDatagramSocket.bind 的参数。如果您发现搜不到设备,尝试检查网络接口。

五、完整示例代码

本示例构建一个局域网设备扫描器,列出所有 HTTP 服务 (_http._tcp)。

import'package:flutter/material.dart';import'package:multicast_dns/multicast_dns.dart';voidmain(){runApp(constMaterialApp(home:MDnsPage()));}classMDnsPageextendsStatefulWidget{constMDnsPage({super.key});@overrideState<MDnsPage>createState()=>_MDnsPageState();}classServiceInfo{finalString name;finalString host;final int port;finalString ip;ServiceInfo(this.name,this.host,this.port,this.ip);}class _MDnsPageState extendsState<MDnsPage>{finalList<ServiceInfo> _services =[]; bool _scanning =false;MDnsClient? _client;Future<void>_startScan()async{setState((){ _services.clear(); _scanning =true;}); _client =MDnsClient();await _client!.start();try{// 1. 查找服务实例 (PTR)awaitfor(final ptr in _client!.lookup<PtrResourceRecord>(ResourceRecordQuery.serverPointer('_http._tcp.local'),)){// 2. 查找服务详情 (SRV)awaitfor(final srv in _client!.lookup<SrvResourceRecord>(ResourceRecordQuery.service(ptr.domainName),)){// 3. 查找 IP (A)awaitfor(final ip in _client!.lookup<IPAddressResourceRecord>(ResourceRecordQuery.addressIPv4(srv.target),)){final info =ServiceInfo( ptr.domainName, srv.target, srv.port, ip.address.address,);if(mounted){setState((){if(!_services.any((s)=> s.ip == info.ip && s.port == info.port)){ _services.add(info);}});}}}}}finally{ _client?.stop();if(mounted)setState(()=> _scanning =false);}}@overridevoiddispose(){ _client?.stop();super.dispose();}@overrideWidgetbuild(BuildContext context){returnScaffold( appBar:AppBar(title:constText('局域网 HTTP 服务发现')), body:Column( children:[if(_scanning)constLinearProgressIndicator(),Expanded( child: _services.isEmpty ?constCenter(child:Text('没有发现 HTTP 服务,请确保 WiFi 连接')):ListView.builder( itemCount: _services.length, itemBuilder:(context, index){final s = _services[index];returnListTile( leading:constIcon(Icons.computer), title:Text(s.name.replaceAll('._http._tcp.local','')), subtitle:Text('${s.host}:${s.port} (${s.ip})'),);},),),],), floatingActionButton:FloatingActionButton( onPressed: _scanning ?null: _startScan, child:constIcon(Icons.refresh),),);}}
在这里插入图片描述

六、总结

multicast_dns 是 Dart 原生的 mDNS 解决方案。虽然不如原生 API 稳定(受限于 UDP 组播环境),但其跨平台一致性和灵活性是巨大的优势。

最佳实践

  1. 超时:mDNS 查询可能永远挂起(如果没有响应),务必结合 timeout 或手动 stop
  2. 缓存:设备名称与 IP 的映射关系应有短时缓存,减少网络风暴。
  3. 兼容性:Android/OpenHarmony 某些版本默认禁止后台组播,确保 App 前台运行且权限正确。

Read more

Python 爬虫实战:爬取国家统计局公开数据

前言 国家统计局作为权威的宏观经济数据发布机构,汇聚了人口、GDP、就业、产业经济等多维度的核心统计数据,是开展经济研究、政策分析、行业洞察的重要数据支撑。相较于商业数据平台,国家统计局的公开数据具备权威性、全面性和时效性等特点,但部分数据分散在不同栏目且缺乏结构化下载渠道。本文基于 Python 爬虫技术,系统讲解国家统计局公开数据的爬取方法,重点解决静态表格解析、分页数据抓取、数据结构化转换等问题,同时严格遵循政府网站数据使用规范,为科研、教学等场景提供合规的数据获取方案。 摘要 本文以国家统计局官网(http://www.stats.gov.cn)为爬取目标,深度解析政府类网站数据爬取的核心逻辑:首先分析国家统计局公开数据的页面结构,包括静态表格布局、分页请求规则、数据栏目分类;其次基于 requests 库获取网页响应,结合 BeautifulSoup 与 pandas 实现 HTML 表格解析,提取地区、指标、数值、时间等核心字段;

By Ne0inhk

Python文本为什么会乱码?从根源到解决方案的深度解析

“乱码”是每个Python开发者,尤其是处理中文、日文等非ASCII字符时,都会遇到的“噩梦”。明明代码逻辑正确,文件也存在,但打印出来或保存的文件却是一堆莫名其妙的符号(如éÂ\x87Â\x91éÂ\x9eÂ\x93)。 这篇文章将带你彻底理解乱码产生的根本原因,并提供一套行之有效的解决方案和最佳实践。 一、乱码的本质:编码与解码的“鸡同鸭讲” 要理解乱码,首先必须明白两个核心概念:字符集(Charset) 和 字符编码(Character Encoding)。 1. 字符集(Charset):是一个系统支持的所有抽象字符的集合。比如: * ASCII:包含128个字符(英文字母、数字、符号),用1个字节(8位)表示。 * GBK/GB2312:中国国家标准,包含汉字、符号等,用1或2个字节表示。 * Unicode:

By Ne0inhk
2025年8个热门Python Web开发框架,(非常详细)从零基础到精通,收藏这篇就够了!

2025年8个热门Python Web开发框架,(非常详细)从零基础到精通,收藏这篇就够了!

Python 拥有适合各种用例的框架,从全栈 Web 开发到数据可视化,为每位开发人员提供了所需的工具。得益于其活跃的社区和强大的生态系统,开发人员在构建 Web 应用时拥有广泛的选择。然而,选择数量之多可能会使您难以为您的项目选择合适的框架。 这就是为什么我们回顾了用于构建 Web 应用程序的顶级Python 框架,并比较了每个框架的优缺点。在本文中,我们将回顾以下框架: Reflex、Django、Flask、Gradio、Streamlit、Dash、FastAPI。 1、Reflex Reflex代表了 Web 开发的一次变革,它使开发人员能够使用 Python 这种单一语言构建全栈 Web 应用 。Reflex 旨在无缝集成后端和前端,它提供了 60 多个内置组件,开发人员可以轻松自定义和扩展这些组件,还可以选择引入自己的组件(底层是 React)。 优点 * 纯 Python:Reflex 允许您使用

By Ne0inhk
从原理到实战:随机森林算法全解析(附 Python 完整代码)

从原理到实战:随机森林算法全解析(附 Python 完整代码)

一、算法定义 随机森林(Random Forest)是一种基于集成学习思想的分类算法,它通过 “随机采样数据 + 随机选择特征” 的方式构建多棵决策树,最终通过投票法(分类任务)或平均法(回归任务)整合所有决策树的预测结果,输出最终结论。 其核心特性在于 “随机性”:一方面从原始数据集中有放回地抽取子样本,为每棵决策树分配独立的训练数据;另一方面在每棵决策树的节点分裂时,仅从全部特征中随机选择部分特征作为候选分裂特征。这种双重随机性有效降低了单棵决策树的过拟合风险,同时提升了模型的稳定性和泛化能力。 二、算法的作用与应用场景 随机森林主要用于解决分类问题(二分类 / 多分类),同时也可扩展至回归任务,核心价值是处理复杂数据的模式识别与预测,适用场景包括: 1. 商业预测:如根据市民属性预测购车行为、根据用户消费数据预测复购概率等; 2. 风险评估:如金融领域的信贷风险评级、医疗领域的疾病风险预测; 3. 特征重要性分析:在预测的同时,可量化每个特征对结果的影响程度,为业务决策提供依据; 4. 处理复杂数据:能有效应对高维数据、混合类型数据(需预处理)

By Ne0inhk