SignalR Self Host模式允许开发者将实时消息推送服务宿主在控制台程序、Windows服务等进程中,无需依赖IIS环境,适合需要轻量部署、跨平台运行的场景。本文通过一个完整的实例演示如何搭建支持多端连接的Self Host消息推送服务。

环境准备
首先需要安装对应的NuGet包,在项目中通过包管理器控制台执行以下命令安装依赖:
// 安装SignalR Self Host核心包 Install-Package Microsoft.AspNet.SignalR.SelfHost // 安装跨域支持包,支持多端不同域访问 Install-Package Microsoft.Owin.Cors
服务端核心实现
1. 定义消息推送Hub
Hub是SignalR中处理客户端和服务端通信的核心组件,我们定义一个自定义的PushHub类:
using Microsoft.AspNet.SignalR;
namespace SignalRSelfHostDemo
{
// 自定义Hub,继承自Hub基类
public class PushHub : Hub
{
// 服务端方法,供客户端调用,向所有连接的客户端推送消息
public void SendMessage(string userName, string content)
{
// 调用所有客户端的receiveMessage方法,传递发送者和消息内容
Clients.All.receiveMessage(userName, content);
}
// 客户端连接时的回调
public override System.Threading.Tasks.Task OnConnected()
{
string connId = Context.ConnectionId;
// 可以在这里记录连接的客户端信息
return base.OnConnected();
}
// 客户端断开连接时的回调
public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
{
string connId = Context.ConnectionId;
// 可以在这里清理客户端连接记录
return base.OnDisconnected(stopCalled);
}
}
}
2. 配置Self Host服务
创建Owin启动类,配置SignalR和跨域规则:
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(SignalRSelfHostDemo.Startup))]
namespace SignalRSelfHostDemo
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// 允许所有域的跨域请求,实际生产环境可配置指定域
app.UseCors(CorsOptions.AllowAll);
// 映射SignalR到默认路径 /signalr
app.MapSignalR();
}
}
}
3. 启动服务宿主
在控制台程序的入口方法中启动Self Host服务:
using Microsoft.Owin.Hosting;
using System;
namespace SignalRSelfHostDemo
{
class Program
{
static void Main(string[] args)
{
string baseUrl = "http://127.0.0.1:8080";
try
{
// 启动WebApp,监听指定地址
using (WebApp.Start<Startup>(baseUrl))
{
Console.WriteLine($"SignalR Self Host服务已启动,监听地址:{baseUrl}");
Console.WriteLine("按任意键停止服务...");
Console.ReadKey();
}
}
catch (Exception ex)
{
Console.WriteLine($"服务启动失败:{ex.Message}");
}
}
}
}
多端客户端实现
1. Web端客户端
Web端通过引入SignalR的JS库连接服务,以下是简单的页面示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Web端消息接收</title>
<!-- 引入SignalR JS库 -->
<script src="https://ipipp.com/signalr/jquery.signalr-2.4.3.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<div>
<input type="text" id="userName" placeholder="输入用户名" />
<input type="text" id="message" placeholder="输入消息内容" />
<button id="sendBtn">发送消息</button>
</div>
<div id="msgList" style="margin-top:20px;"></div>
<script>
$(function () {
// 配置SignalR服务地址
$.connection.hub.url = "http://127.0.0.1:8080/signalr";
// 获取PushHub的代理对象
var pushHub = $.connection.pushHub;
// 定义客户端接收消息的方法,与服务端调用的receiveMessage对应
pushHub.client.receiveMessage = function (userName, content) {
$("#msgList").append(`<p>${userName}:${content}</p>`);
};
// 启动连接
$.connection.hub.start().done(function () {
console.log("Web端连接成功");
// 绑定发送按钮点击事件
$("#sendBtn").click(function () {
var name = $("#userName").val();
var msg = $("#message").val();
// 调用服务端的SendMessage方法
pushHub.server.sendMessage(name, msg);
});
}).fail(function (err) {
console.log("连接失败:" + err);
});
});
</script>
</body>
</html>
2. .NET桌面端客户端
桌面端通过Microsoft.AspNet.SignalR.Client包连接服务,示例代码如下:
using Microsoft.AspNet.SignalR.Client;
using System;
using System.Windows.Forms;
namespace WinFormsClient
{
public partial class Form1 : Form
{
private HubConnection connection;
private IHubProxy pushProxy;
public Form1()
{
InitializeComponent();
InitSignalR();
}
private async void InitSignalR()
{
// 创建连接,指向Self Host服务的地址
connection = new HubConnection("http://127.0.0.1:8080");
// 创建PushHub的代理
pushProxy = connection.CreateHubProxy("PushHub");
// 注册接收消息的方法
pushProxy.On<string, string>("receiveMessage", (userName, content) =>
{
Invoke(new Action(() =>
{
textBox_msgList.AppendText($"{userName}:{content}rn");
}));
});
try
{
// 启动连接
await connection.Start();
MessageBox.Show("桌面端连接成功");
}
catch (Exception ex)
{
MessageBox.Show($"连接失败:{ex.Message}");
}
}
private async void button_send_Click(object sender, EventArgs e)
{
if (connection.State == ConnectionState.Connected)
{
// 调用服务端的SendMessage方法
await pushProxy.Invoke("SendMessage", textBox_userName.Text, textBox_input.Text);
}
}
}
}
常见问题说明
- 如果多端连接时出现跨域错误,需要检查Startup类中是否正确配置了Cors规则。
- 服务启动失败提示端口占用时,可以更换baseUrl中的端口号,确保端口未被其他程序占用。
- 客户端调用服务端方法时,方法名需要与服务端Hub中定义的方法名完全一致,区分大小写。
注意:生产环境中建议对连接进行身份验证,避免未授权的客户端接入推送服务,同时可以根据需求调整跨域配置,仅允许指定的域名访问。