的卢妨主——安卓 WOL 包组播

455 阅读3分钟

问题描述

在使用 Flutter 框架开发的 Android 应用中进行魔术包唤醒下发,执行单播往往可以正常触发对端设备。尝试通过组播或广播来唤醒局域网内设备时,始终遇到魔术包被阻断的情况,错误信息如下:

(OS Error: Permission denied, errno 13)

该错误表明系统权限不足。这是因为在 Android 设备上,默认情况下未开启广播或组播的网络权限。


权限配置

首先,确保应用拥有必要的权限。以下是需要在 AndroidManifest.xml 中添加的权限声明:

// 允许应用程序改变 Wi-Fi 的多播状态,以支持组播通信
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>

// 检查网络连接状态,确保设备连接到网络
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

尽管直接添加了这些权限后再尝试发送广播包,问题依然存在,日志中依然提示权限不足。


原生代码尝试

在添加上述权限的基础上,尝试使用原生 Kotlin 代码发送广播包,主要逻辑如下:

val socket = MulticastSocket()
val group = InetAddress.getByName(groupAddress)
val hexBytes = hexStringToByteArray(hexMessage.replace(" ", ""))
Log.d("sendMulticastMessage", "groupAddress:$groupAddress, Sending hex message: $hexMessage")
val packet = DatagramPacket(hexBytes, hexBytes.size, group, 9) // 使用 9 端口
socket.use {
    it.send(packet)
}

广播包成功发送,目标设备正常接收。这表明应用已经具备发送广播包的权限。因此,问题可能出现在 Flutter 下发逻辑与原生实现策略的差异。


Flutter 实现策略分析

通过对 Flutter 中的 RawDatagramSocket 进行研究,发现没有正确启用 broadcastEnabled 参数:

/// 是否启用了 IPv4 广播
///
/// 发送 IPv4 广播数据包时,发送方需要启用 IPv4 广播。
/// 默认情况下,IPv4 广播是禁用的。
///
/// 对于 IPv6,没有通用的广播机制,请使用组播替代。
abstract bool broadcastEnabled;
  • 默认状态: broadcastEnabled 属性默认设置为 false,意味着通过这个套接字发送的数据包不会被视为广播包。
  • 广播模式: 将 broadcastEnabled 设置为 true 后,套接字即可发送 UDP 广播数据包,这些数据包会被网络中的所有设备接收。例如,使用 255.255.255.255 作为目标地址发送 UDP 数据包,可以在同一子网的所有设备上接收到该数据包。

原有 Flutter 主要下发逻辑:

RawDatagramSocket.bind(InternetAddress.anyIPv4, 0).then((RawDatagramSocket socket) {
  socket.send(utf8.encode('Hello, network!'), InternetAddress('255.255.255.255'), 8888);
  socket.close();
});

解决方案:为了使广播包正常发送,需在 send 操作之前启用广播权限:

socket.broadcastEnabled = true; // 启用广播

在做出上述修改后,测试结果表明广播包可以正常发送,设备成功被唤醒。


总结

Flutter 项目执行 udp 广播,需添加 xml 广播权限后,并在发送前显式开启广播权限,才能成功下发广播包。 与原生相比 Flutter 框架的 scoket 默认禁用了广播,而原生代码则没有此限制。

参考文献:

Failed to create datagram socket (OS Error: Permission denied, errno = 13)