# 局域网服务发现:组播叠加设计 日期:2026-07-06 状态:已批准(方案 A) ## 背景与问题 真机内网联调依赖 UDP 发现机制找到开发机上的网关/CDN 地址。当前客户端探测目标为: `255.255.255.255` + `127.0.0.1` + 各网卡子网定向广播 + 写死的开发机 IP `192.168.130.40`。 定向广播默认不被路由器转发(RFC 2644,已实测不通),跨网段场景只能靠写死 IP 兜底——换机器/换网段就要改代码,不通用。 ## 目标 - 新增组播探测路径,使发现请求**有机会**跨网段到达开发机(取决于路由器是否开启组播路由,当前网络环境未验证)。 - 纯增量改动:现有广播/loopback/写死 IP 路径全部保留,任何一条路通即发现成功;组播不通也不比现状差。 ## 非目标 - 不移除写死 IP(实测组播跨网段可行后再删)。 - 不做探测 IP 可配置化(若组播实测失败,作为后续备选方案)。 - 不改发现协议本身(magic "XWDL"、端口 48923、编解码格式均不变)。 ## 关键技术决策 | 决策 | 取值 | 理由 | |---|---|---| | 组播组地址 | `239.192.48.92` | 239.192.0.0/14 为组织本地范围(RFC 2365),专为跨子网管理域设计;239.255.0.0/16 是本地范围,路由器可能限制其跨网段 | | 端口 | `48923`(复用现有) | 服务端同一 socket 可同时收单播/广播/组播,零额外监听 | | 客户端组播 TTL | `8` | 组播默认 TTL=1 出不了本网段;8 足够覆盖内网多级路由,又不至于扩散过远 | | Android MulticastLock | 不需要 | 客户端只**发**组播、**收**单播回复;MulticastLock 只影响收组播 | ## 改动清单 ### 1. 协议常量 `XWorld.Framework.Protocol.DevDiscovery` 新增: ```csharp public const string MulticastGroup = "239.192.48.92"; ``` ### 2. 服务端 `Server\Gateway\DevDiscoveryResponder.cs` 构造函数中,绑定 `0.0.0.0:48923` 后,枚举本机所有 Up 状态、非 Loopback 的 IPv4 网卡,对每块网卡执行: ```csharp _udp.JoinMulticastGroup(groupAddr, nicIPv4Addr); ``` - 默认 `JoinMulticastGroup(group)` 只加入默认网卡,多网卡开发机(有线+无线/虚拟网卡)会漏收,故须逐网卡 join。 - 逐网卡 try/catch,任何一块失败不中断其余(与客户端 `GetDirectedBroadcasts` 的容错风格一致)。 - 收包与回复逻辑零改动:组播查询的 `RemoteEndPoint` 是发送方单播地址,现有单播回复天然可用。 ### 3. 客户端热更层 `Client\Assets\Script\xmain\MiniGame\DevServerDiscovery.cs` - socket 初始化后设置 `MulticastTimeToLive = 8`。 - 组播端点**不加入** `targets` 通用列表(避免只走默认路由网卡),而是在每轮重发中单独处理:逐 Up 状态 IPv4 网卡设置 `IP_MULTICAST_IF`(`SocketOptionName.MulticastInterface`,值为网卡 IPv4 地址字节)后向 `239.192.48.92:48923` 发一次,逐网卡 try/catch。 - 手机通常单网卡,此逻辑主要覆盖编辑器/PC 多网卡场景。 - 现有 targets(广播、loopback、定向广播、`DevExtraProbeIps`)全部不动;`DevExtraProbeIps` 注释更新:组播跨网段实测通过后可删。 ### 4. 客户端底包 `Client\Assets\Base\LocalCdnDiscovery.cs` 与热更层同步:内联 `MulticastGroup` 常量(Base 程序集不能引用热更程序集,沿用现有"改协议时两边须同步"约定),发送逻辑同第 3 条。 ## 错误处理 join / 设组播网卡 / 发送失败全部静默吞掉——发现机制是"多路齐发、谁通算谁",单路失败不影响整体,与现有代码风格一致。 ## 测试 `Server\Gateway.Tests\DevDiscoveryResponderTests.cs` 新增一条: - 客户端测试 socket 设置 `MulticastLoopback = true`、`MulticastTimeToLive = 1`,向 `239.192.48.92:48923` 发查询,断言收到合法回复(验证响应器 join 组播组 + 组播收包路径)。 - 现有单播/广播测试不动。 ## 验收标准 1. 服务端/客户端单元测试通过。 2. 编辑器同机自测:发现日志中出现组播目标,发现成功。 3. 真机同网段:发现成功(组播或广播任一路径)。 4. 真机跨网段实测:若组播路径通 → 后续任务删除 `DevExtraProbeIps` 写死 IP;若不通 → 保留写死 IP,评估"探测 IP 可配置化"方案。 ## 风险 - **组播路由未验证**:公司路由器可能未开 PIM/IGMP Proxy,组播出不了网段。缓解:纯增量设计,失败无损现状。 - **部分 Android WiFi 驱动丢组播发包**:概率低(丢的主要是收方向),且广播路径仍在。