Flutter零配置连接插件zeroconnect的使用
Flutter零配置连接插件zeroconnect的使用
zeroconnect
使用NSD自动在局域网内创建数据流。
这是将Erhannis/zeroconnect 转换为Flutter版本的结果。某些语句或代码可能稍有不准确或奇怪,因为这是在Flutter环境中实现的。 请注意,我在尝试让不同的NSD/MDNS/Zeroconf等实现接受相同的类型结构或协同工作时遇到了一些问题,因此目前此版本无法与Python版本通信,尽管感觉已经接近了。
该插件使用了NSD,这意味着它有一些需求:
目录
权限
Android
在您的manifest文件中添加以下权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
iOS
在您的Info.plist文件中添加以下权限,并相应地替换"YOURSERVICEID":
<key>NSLocalNetworkUsageDescription</key>
<string>Required to discover local network devices</string>
<key>NSBonjourServices</key>
<array>
<string>_YOURSERVICEID._tcp</string>
</array>
用法
一个或多个服务器和一个或多个客户端运行在同一局域网上。(可以是Wi-Fi或以太网)
最基本的用法
服务端
import 'package:zeroconnect/zeroconnect.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); // 如果已经调用了`runApp`,则不需要这行
await ZeroConnect().advertise(serviceId: "YOURSERVICEID", callback: (messageSock, nodeId, serviceId) async {
print("got message connection from $nodeId");
var str = await messageSock.recvString();
print(str);
await messageSock.sendString("Hello from server");
});
}
客户端
import 'package:zeroconnect/zeroconnect.dart';
Future<void> main() async {
var messageSock = await ZeroConnect().connectToFirst(serviceId: "YOURSERVICEID");
await messageSock?.sendString("Hello from client");
var str = await messageSock?.recvBytes();
print(str);
}
较复杂的用法
服务端
import 'package:zeroconnect/zeroconnect.dart';
const SERVICE_ID = "YOURSERVICEID";
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); // 如果已经调用了`runApp`,则不需要这行
var zc = ZeroConnect(localId: "SERVER_ID");
await zc.advertise(serviceId: SERVICE_ID, callback: (messageSock, nodeId, serviceId) async {
print("got message connection from $nodeId");
// 如果你也想自发发送消息,可以将socket传递给其他线程
while (true) {
var str = await messageSock.recvString();
print("$str");
switch (str) {
case "enable jimjabber":
print("ENABLE JIMJABBER");
break;
case "save msg:":
var toSave = await messageSock.recvBytes();
print("SAVE MESSAGE $toSave");
break;
case "marco":
await messageSock.sendString("polo");
print("PING PONGED");
break;
case null:
print("Connection closed from $nodeId");
await messageSock.close();
return;
default:
print("Unhandled message: $str");
break;
}
// 使用 messageSock.sock 进行例如 sock.remoteAddress 操作
// 我推荐在完成后调用 messageSock.close() - 但当调用 zc.close() 时,它也会被关闭
}
});
// 当你想要关闭现有的一切时,可以调用 zc.close()
}
客户端
import 'package:zeroconnect/zeroconnect.dart';
const SERVICE_ID = "YOURSERVICEID";
Future<void> main() async {
var zc = ZeroConnect(localId: "CLIENT_ID"); // 技术上讲,nodeId 是可选的;它会分配给你一个随机UUID
var ads = await zc.scan(serviceId: SERVICE_ID, time: const Duration(seconds: 5));
// OR: var ads = await zc.scan(serviceId: SERVICE_ID, nodeId: NODE_ID);
// Ad 包含了一个 serviceId 和 nodeId 等信息;详情请查看 Ad 类
var messageSock = await zc.connect(ads.first); // 也可以使用 (ZeroConnect).connectRaw
// OR: var messageSock = await zc.connectToFirst(serviceId: SERVICE_ID);
// OR: var messageSock = await zc.connectToFirst(serviceId: SERVICE_ID, nodeId: NODE_ID, time: const Duration(seconds: 10));
// 有一天你可能会能够仅指定一个 nodeId,但我尚未解决当时遇到的一些问题。
await messageSock?.sendString("enable jimjabber");
await messageSock?.sendString("save msg:");
await messageSock?.sendString("i love you");
await messageSock?.sendString("marco");
print("rx: ${await messageSock?.recvString()}");
// ...
await zc.close();
}
你可以选择获取原始套接字而不是MessageSocket,如果你更喜欢这样做。 参见例如 (ZeroConnect).advertiseRaw 和 (ZeroConnect).connectRaw。 实际上,我没有测试过它们,而且由于我最初将Socket包裹在一个MessageSocket中进行一些握手操作,这可能会干扰使用原始Socket。抱歉。 //DUMMY 查看是否可以修复这个问题,并可能添加示例代码
还有一些你可能觉得有用的函数。请检查自动完成并查看源代码。
YOURSERVICEID
据报道(#3),你的服务ID必须是1-15个字符长,并且只能由a-z、A-Z、0-9组成。我个人没有检查过这一点,但我记得遇到过类似的情况(可能是平台相关的),所以如果你遇到问题,请检查你的服务ID是否符合这些限制。
提示
要小心不要让两个节点同时接收消息,否则会导致死锁。 然而,你可以在同一时间发送消息(根据我的测试)。
ZeroConnect
应通过其方法进行操作,但如果你读取字段中的数据,它可能不会立即爆炸。
注意,有些计算机/网络可能会阻止mdns/nsd/zeroconf或外部连接尝试等。
调用broadcast
会自动清理死连接。理论上如此。在我用Flutter测试时,发送没有抛出错误。
如果你在发送消息后立即关闭套接字,数据可能无法完全发送。这不是我的错,归咎于套接字。
broadcast
使用 MessageSockets,因此如果你正在使用原始套接字,请注意消息会被前缀一个头部,当前是一个表示后续消息长度的8字节无符号大端长整数,后面跟着相同的但反转并异或(与8字节的0xFF异或)。请参阅MessageSocket
。
请参阅zeroconnect.dart开头附近以查看日志设置,或者像这样操作:
import 'package:zeroconnect/zeroconnect.dart';
void main() {
ZC_LOGGING = 10; // 4+ 表示所有内容,目前;-1 表示除了未捕获的异常外什么都没有
// ...
}
更多关于Flutter零配置连接插件zeroconnect的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter零配置连接插件zeroconnect的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter项目中,使用zeroconnect
插件可以帮助你实现零配置连接(ZeroConf),这对于发现和服务网络上的设备非常有用。以下是如何在Flutter项目中使用zeroconnect
插件的一个简单示例。
首先,确保你已经在pubspec.yaml
文件中添加了zeroconnect
依赖:
dependencies:
flutter:
sdk: flutter
zeroconnect: ^最新版本号 # 请替换为最新的版本号
然后,运行flutter pub get
来安装依赖。
接下来,我们来看一个基本的代码示例,展示如何使用zeroconnect
插件来浏览和解析网络上的服务。
主文件 main.dart
import 'package:flutter/material.dart';
import 'package:zeroconnect/zeroconnect.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Service> _services = [];
@override
void initState() {
super.initState();
_startBrowsing();
}
void _startBrowsing() async {
try {
// 开始浏览服务,这里以 "_http._tcp.local." 为例
var browser = await Zeroconnect().browse(serviceType: "_http._tcp.local.");
// 监听发现的服务
browser.serviceAdded.listen((service) {
setState(() {
_services.add(service);
});
});
// 监听移除的服务
browser.serviceRemoved.listen((service) {
setState(() {
_services.removeWhere((s) => s.fullName == service.fullName);
});
});
} catch (e) {
print("Error browsing services: $e");
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Zeroconnect Example'),
),
body: ListView.builder(
itemCount: _services.length,
itemBuilder: (context, index) {
var service = _services[index];
return ListTile(
title: Text(service.fullName),
subtitle: Text(service.address + ':' + service.port.toString()),
);
},
),
),
);
}
}
class Service {
String fullName;
String address;
int port;
Service({required this.fullName, required this.address, required this.port});
}
注意事项
- 权限:在iOS上,使用ZeroConf可能需要网络权限。确保在
Info.plist
中添加相应的权限声明。 - 错误处理:在实际应用中,应该添加更多的错误处理逻辑,以处理网络问题或服务发现失败的情况。
- 服务类型:在
browse
方法中,serviceType
参数指定了你想要浏览的服务类型。不同的服务类型可能有不同的前缀和后缀,例如_http._tcp.local.
、_printer._tcp.local.
等。
这个示例展示了如何使用zeroconnect
插件来浏览网络上的服务,并将发现的服务显示在Flutter应用的列表中。你可以根据需要进一步扩展这个示例,例如解析更多服务信息、连接到发现的服务等。