如何通过广播快速发现树莓派上指定端口的Web服务器?
用UDP广播快速发现树莓派上的.NET Web服务
一、先搞定树莓派端的广播发送(Docker里的.NET服务)
你的Web服务跑在Docker容器里,首先得处理网络问题——要么让容器共享主机网络,要么把UDP广播端口映射出来,不然局域网里的MAUI设备收不到广播信号。
1. Docker端口/网络配置
- 图省事的话,直接让容器复用主机网络,运行容器时加
--network host参数,这样容器里的端口和主机端口完全一致,不用单独映射:docker run -d --network host --name your-web-service your-image - 要是不想用主机网络,就同时映射Web服务端口(7299)和UDP广播端口(比如设为7300):
docker run -d -p 7299:7299 -p 7300:7300/udp --name your-web-service your-image
2. 在.NET Web服务里加广播逻辑
写个后台定时发送广播的服务,主动告诉局域网里的设备“我在这,我的地址是XX”:
using System.Net; using System.Net.Sockets; using System.Text; public class UdpBroadcaster : BackgroundService { private readonly ILogger<UdpBroadcaster> _logger; // 广播专用端口,要和MAUI客户端保持一致 private const int BroadcastPort = 7300; // 自定义服务标识,避免和其他设备的广播消息混淆 private const string ServiceTag = "RPI_DOTNET_WEB"; // 你的Web服务端口 private readonly string _webPort = "7299"; public UdpBroadcaster(ILogger<UdpBroadcaster> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { using var udpClient = new UdpClient { EnableBroadcast = true }; var broadcastPoint = new IPEndPoint(IPAddress.Broadcast, BroadcastPort); while (!stoppingToken.IsCancellationRequested) { try { // 获取设备的局域网IP(用host网络的话就是树莓派本身的IP) var localIp = GetLanIp(); // 拼接消息:服务标识;IP地址;Web端口 var broadcastMsg = $"{ServiceTag};{localIp};{_webPort}"; var msgBytes = Encoding.UTF8.GetBytes(broadcastMsg); await udpClient.SendAsync(msgBytes, msgBytes.Length, broadcastPoint); _logger.LogInformation("发送广播:{Msg}", broadcastMsg); // 每5秒发一次,可根据需求调整频率 await Task.Delay(5000, stoppingToken); } catch (Exception ex) { _logger.LogError(ex, "广播发送失败"); await Task.Delay(1000, stoppingToken); } } } private IPAddress GetLanIp() { var hostEntry = Dns.GetHostEntry(Dns.GetHostName()); foreach (var ip in hostEntry.AddressList) { // 筛选非回环的IPv4地址 if (ip.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(ip)) { return ip; } } throw new InvalidOperationException("没找到有效的局域网IP"); } }
然后在Program.cs里注册这个后台服务:
builder.Services.AddHostedService<UdpBroadcaster>();
二、.NET MAUI端的监听逻辑
MAUI这边要监听指定端口的UDP广播,收到消息后解析出服务器的地址和端口。
1. 写监听代码
可以把逻辑放在ViewModel里,方便和页面绑定:
using System.Net; using System.Net.Sockets; using System.Text; using CommunityToolkit.Mvvm.ComponentModel; public partial class ServiceDiscoveryVm : ObservableObject { private const int BroadcastPort = 7300; private const string ServiceTag = "RPI_DOTNET_WEB"; private UdpClient _udpListener; private CancellationTokenSource _cancelTokenSource; [ObservableProperty] private string _serverUrl; public async Task StartSearching() { _cancelTokenSource = new CancellationTokenSource(); _udpListener = new UdpClient(BroadcastPort); // 允许重复绑定端口,避免多个应用监听时冲突 _udpListener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); try { while (!_cancelTokenSource.Token.IsCancellationRequested) { var receiveResult = await _udpListener.ReceiveAsync(_cancelTokenSource.Token); var receivedMsg = Encoding.UTF8.GetString(receiveResult.Buffer); var parts = receivedMsg.Split(';'); // 验证消息是我们的树莓派服务发的 if (parts.Length == 3 && parts[0] == ServiceTag) { var serverIp = parts[1]; var serverPort = parts[2]; ServerUrl = $"http://{serverIp}:{serverPort}"; // 找到服务后可以停止监听,也可以继续监听应对IP变化 // _cancelTokenSource.Cancel(); } } } catch (OperationCanceledException) { // 监听被取消,正常结束 } catch (Exception ex) { // 这里可以加异常提示,比如弹窗告诉用户搜索失败 } finally { _udpListener?.Close(); _udpListener?.Dispose(); } } public void StopSearching() { _cancelTokenSource?.Cancel(); } }
2. 在页面里调用监听
在页面的生命周期事件里启动和停止监听:
protected override async void OnAppearing() { base.OnAppearing(); await ViewModel.StartSearching(); } protected override void OnDisappearing() { base.OnDisappearing(); ViewModel.StopSearching(); }
三、要注意的细节
- 权限配置:MAUI应用需要网络权限,Android要在
AndroidManifest.xml中添加:
iOS要在<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />Info.plist中添加:<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict> - Docker网络坑:如果不用
--network host,容器内部的IP可能不是树莓派的局域网IP,这时候可以把树莓派的IP作为环境变量传给容器,广播时直接用这个IP。比如启动容器时加-e HOST_IP=192.168.xxx.xxx,然后在广播服务里读取这个环境变量。 - 广播频率:不要太频繁发广播,5秒一次足够,太密会占用局域网带宽;也不要太疏,不然MAUI启动后要等很久才能发现服务。
内容的提问来源于stack exchange,提问作者Hickori
相关产品推荐
相关产品推荐

