.NET MAUI作为微软主推的跨平台应用框架,配合SignalR可以很方便地实现实时通信功能,比如即时聊天、消息推送、实时数据看板等场景。相比传统的轮询方式,SignalR基于WebSocket建立长连接,既省流量又几乎零延迟。本文将完整演示如何在MAUI项目中创建HubConnection,连接服务端Hub,实现双向消息收发,并处理自动重连和跨平台权限问题。

一、准备工作:搭建SignalR服务端
在开始写MAUI客户端之前,我们需要先有一个可用的SignalR服务端。服务端通常是一个ASP.NET Core项目,通过NuGet安装Microsoft.AspNetCore.SignalR包(.NET 6及以上版本已内置在框架内,无需额外安装)。首先创建一个Hub类,Hub是SignalR服务端的核心,客户端所有的方法调用和消息接收都围绕它展开。
using Microsoft.AspNetCore.SignalR;
public class ChatHub : Hub
{
// 客户端调用此方法发送消息
public async Task SendMessage(string user, string message)
{
// 服务端广播给所有连接的客户端,客户端需要监听 ReceiveMessage 事件
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
public override async Task OnConnectedAsync()
{
Console.WriteLine($"新客户端连接:{Context.ConnectionId}");
await base.OnConnectedAsync();
}
}接着在Program.cs中注册Hub并开放跨域策略。这里要特别注意的是,如果你的MAUI应用在安卓模拟器上运行,模拟器访问本机服务不能直接用localhost,需要使用10.0.2.2这个地址,否则会一直连接失败。
var builder = WebApplication.CreateBuilder(args);
// 注册SignalR服务
builder.Services.AddSignalR();
// 配置跨域,允许任意来源(生产环境请收紧策略)
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.SetIsOriginAllowed(_ => true)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
var app = builder.Build();
app.UseCors();
app.MapHub<ChatHub>("/chathub");
app.Run();服务端默认监听5000端口左右,具体以启动日志为准。到这里服务端就绪了,接下来重点看MAUI客户端怎么写。
二、MAUI客户端创建HubConnection并收发消息
在MAUI项目中,需要先通过NuGet安装Microsoft.AspNetCore.SignalR.Client包。注意这是客户端专用包,和服务器端的包不是同一个,不要装错。安装完成后,在页面代码中创建HubConnection实例。这里建议把它注册为单例,因为一个连接对应一个实例,重复创建会导致连接数暴涨。
using Microsoft.AspNetCore.SignalR.Client;
public class ChatService
{
public HubConnection Connection { get; }
public ChatService()
{
Connection = new HubConnectionBuilder()
.WithUrl("https://10.0.2.2:7051/chathub") // 安卓模拟器访问本机
.WithAutomaticReconnect() // 启用自动重连
.Build();
// 注册接收消息的回调,服务端 SendAsync 的第一个参数要和这里一致
Connection.On<string, string>("ReceiveMessage", (user, message) =>
{
Console.WriteLine($"{user} 说:{message}");
});
}
public async Task ConnectAsync()
{
try
{
await Connection.StartAsync();
Console.WriteLine("连接成功");
}
catch (Exception ex)
{
Console.WriteLine($"连接失败:{ex.Message}");
}
}
public async Task SendAsync(string user, string message)
{
await Connection.InvokeAsync("SendMessage", user, message);
}
}代码中有几个关键点需要说明。WithUrl指定的是Hub的完整地址,路径/chathub必须和服务器端MapHub映射的路径完全一致。On方法用于注册服务端推送事件的处理器,泛型参数要和服务端SendAsync传入的参数类型一一对应,类型不匹配时回调会静默失败,这是新手最容易踩的坑。
InvokeAsync和SendAsync在客户端也有区别:前者会等待服务端执行完成,后者是纯异步发送不等待结果。如果服务端方法有返回值,必须用InvokeAsync<T>来接收。发送消息前最好检查一下Connection.State是否为Connected,避免在断线状态下调用抛异常。
三、自动重连与连接状态监听
移动端应用的网络环境很不稳定,用户可能随时切换WiFi和流量,所以重连机制必不可少。WithAutomaticReconnect默认的重试策略是0秒、2秒、10秒、30秒各尝试一次,如果四次都失败就会转入断开状态,不再继续尝试。对于生产环境,建议自定义重连参数,延长重试时间窗口。
Connection = new HubConnectionBuilder()
.WithUrl("https://your-server.com/chathub")
.WithAutomaticReconnect(new[]
{
TimeSpan.Zero,
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(15),
TimeSpan.FromSeconds(30),
TimeSpan.FromSeconds(60)
})
.Build();
// 监听重连过程中的各个事件
Connection.Reconnecting += error =>
{
Console.WriteLine($"正在重连:{error?.Message}");
return Task.CompletedTask;
};
Connection.Reconnected += connectionId =>
{
Console.WriteLine($"重连成功,新的连接ID:{connectionId}");
return Task.CompletedTask;
};
Connection.Closed += async error =>
{
Console.WriteLine("连接已关闭,10秒后手动重试");
await Task.Delay(TimeSpan.FromSeconds(10));
await Connection.StartAsync();
};Closed事件触发时说明自动重连已经彻底放弃,此时只能手动调用StartAsync重新建立连接。另外要注意,重连成功后ConnectionId会发生变化,如果业务上依赖连接ID做用户绑定(比如单点登录踢下线),需要在服务端使用IUserIdProvider基于用户ID而非连接ID来标识客户端。
还有一个细节是重连后的消息补偿问题。SignalR默认不保证断线期间的消息不丢失,如果业务对消息完整性要求高,可以在服务端引入AddMessagePackProtocol配合数据库记录消息序号,客户端重连后按序号拉取缺失消息。
四、跨平台注意事项与常见问题排查
MAUI最终会打包成不同平台的原生应用,每个平台的网络权限配置各不相同。安卓9.0以后默认禁止明文HTTP请求,如果你的服务端没有配置HTTPS,需要在MauiApp的安卓平台项目里修改AndroidManifest.xml,添加android:usesCleartextTraffic="true"属性,或者在network security config中放行指定域名。iOS模拟器访问本机服务可以直接用localhost,这一点和安卓模拟器不一样。
连接失败时建议按以下顺序排查:第一,确认设备能访问服务端地址,可以在浏览器里直接打开Hub路径,正常情况下会返回握手页提示;第二,检查HTTPS证书,模拟器或真机对自签名证书默认不信任,开发阶段可以临时关闭证书校验;第三,查看服务端日志是否有跨域拦截记录。这三个原因覆盖了绝大多数连接失败的场景。
性能方面,如果消息频率很高(比如每秒几百条的实时行情数据),建议启用MessagePack协议替代默认的JSON传输,二进制序列化能明显降低带宽占用和序列化开销,配置方式是服务端调用AddSignalR().AddMessagePackProtocol(),客户端对应的包也要安装并在HubConnectionBuilder中添加.AddMessagePackProtocol()。
总结一下,MAUI连接SignalR的核心流程就是安装客户端包、构建HubConnection、注册事件回调、启动连接,再加上可靠的重连策略和各平台网络权限配置。把这几步走通之后,无论是聊天室、订单状态推送还是物联网数据监控,都可以基于同一套连接框架快速扩展。