前言
Asp.Net SignalR已經出來很久了,但是一直沒有靜下心來好好看看。昨天花了幾個小時的時間看了下。首先借鑒了官方文檔,如何搭建一個SignalR的Demo。
SignalR地址:https://github.com/aspnet/SignalR
所以為了快速搭建和體驗.Net Core版本的SignalR,我選擇了下載官方的Demo和參考官方給的教程。所以具體的搭建過程我就不再本文中寫了。
體驗效果
官網給出的DEMO運行如下圖:

點擊connect,查看一下network。可以發現,它在當前瀏覽器支持三種方式。

而且和.NET Framework版本不同的是,新版SignalR中的Hub類型也是蠻豐富的。Demo中給出了 普通Hub,DynamicHub,Hub<T> 三種類型。我們去看看其中的區別吧。
普通Hub
查看定義,可以看到普通Hub中的Clients類型是 IHubCallerClients
namespace Microsoft.AspNetCore.SignalR
{
//
// 摘要:
// A base class for a SignalR hub.
public abstract class Hub : IDisposable
{
protected Hub();
//
// 摘要:
// Gets or sets an object that can be used to invoke methods on the clients connected
// to this hub.
public IHubCallerClients Clients { get; set; }
//
// 摘要:
// Gets or sets the hub caller context.
public HubCallerContext Context { get; set; }
//
// 摘要:
// Gets or sets the group manager.
public IGroupManager Groups { get; set; }
//
public void Dispose();
//
// 摘要:
// Called when a new connection is established with the hub.
//
// 返回結果:
// A System.Threading.Tasks.Task that represents the asynchronous connect.
public virtual Task OnConnectedAsync();
//
// 摘要:
// Called when a connection with the hub is terminated.
//
// 返回結果:
// A System.Threading.Tasks.Task that represents the asynchronous disconnect.
public virtual Task OnDisconnectedAsync(Exception exception);
//
// 摘要:
// Releases all resources currently used by this Microsoft.AspNetCore.SignalR.Hub
// instance.
//
// 參數:
// disposing:
// true if this method is being invoked by the Microsoft.AspNetCore.SignalR.Hub.Dispose
// method, otherwise false.
protected virtual void Dispose(bool disposing);
}
}
IHubCallerClients 定義如下:
public interface IHubCallerClients : IHubCallerClients<IClientProxy>, IHubClients<IClientProxy>
{
}
而框架又給IClientProxy增加了擴展方法:SendAsync
所以在普通Hub中,定義客戶端方法的時候,需要把方法名當作參數傳入SendAsync方法中。例如如下代碼:
public Task Send(string message)
{
return Clients.All.SendAsync("Receive", $"{Context.ConnectionId}: {message}");
}
DynamicHub
DynamicHub我是比較喜歡的,因為他和 Framework版的是一樣(或者說看起來是一樣的)的。動態Hub我們就可以不必拘泥於只能調用SendAsync方法了。例如:
public Task SendToOthers(string message)
{
return Clients.Others.ThisIsMyReceiveMethod($"{Context.ConnectionId}: {message}");
}
DynamicHub的Clients類型為:DynamicHubClients ,它的內部變量全都是dynamic類型的。

Hub<T>
泛型Hub就把規約交給開發者制定。在Demo中 Hub<IChatClient> 中的IChatClient接口定義了Receive方法,因此Clients中的對象可以調用Receive方法。同理,我們可以根據業務需要定義自己的方法。至少從代碼上看會顯得更加通俗易懂一些。比如:
public interface IChatClient
{
Task Receive(string message);
Task LoginSuccess(long userId);
}
public Task Login(long userId)
{
return Clients.Caller.LoginSuccess(userId);
}
其實從代碼上來看的話,他們都是Hub,只不過是不同的擴展實現而已。而泛型Hub不過是用戶自定義泛型接口,而默認Hub中的默認泛型接口為:IClientProxy.所以看到這里,如果我就想使用原生的Hub而又想自定義方法怎么辦呢?很簡單,加擴展就可以了。
為什么自己加就可以呢,其實 SendAsync 就是擴展方法,它內部也是調用了SendCoreAsync方法。於是乎,寫下自己的擴展方法,那這樣子就很靈活了。我們把method參數去掉,直接寫死試試:
public static Task LoginAsync(this IClientProxy clientProxy, string message, CancellationToken cancellationToken = default(CancellationToken))
{
return clientProxy.SendCoreAsync("LoginSuccess", new object[] { message}, cancellationToken);
}
其實說白了,這個擴展方法還是需要傳入method參數的,只不過封裝了一層(似乎感覺這么做有意義嗎?哈哈,還是老老實實用泛型吧),那么我們在去看Hub中的方法,修改Send方法如下:
public Task Send(string message)
{
return Clients.All.LoginAsync($"{Context.ConnectionId}: {message}");
}
是不是這樣子就實現了自己自定義方法了呢?個人覺得這么寫還繞了一圈,不如用泛型或者Dynamic了。
運行一下,看看效果:

其實我也是抱着試試的態度,沒想到還真是這樣,和新方法就是SendCoreAsync,而其他方法只不過是上層封裝使得代碼更加通俗易懂。
使用Redis
Demo中的其他例子就不再演示了。廣播,一對一,一對多,加入組,退出組等基本和之前一樣。這里在演示一下使用Redis做不同實例之間的通信效果。
首先程序集是不能少的:Microsoft.AspNetCore.SignalR.Redis,然后在Startup中補充代碼:

打開Redis客戶端,使用MONITOR命令監聽一下,從程序啟動,到連接,在發送一條廣播消息:hello redis。 redis 監聽結果如下:

所以,PUB/SUB還是立了大功呢。
這里用CMD運行了兩個實例,端口分別為 8881,8882來模擬兩個站點。

演示效果如下:

沒問題的哦,其實仔細想想,雖然運行了兩個網站實例,但是連接信息都保存在同一個Redis上,那肯定通信是木的問題的啦。
總結
只是簡單的運行了一下DEMO,大致了解了一下 .Net Core SignalR的表層,至少跑Demo是跑起來了,並且使用Redis也是沒有問題的。不過好像會出現運行一旦時間,程序自動停掉的問題,不知道是不是我電腦的問題。。今天就到這里吧,希望大家能有所收獲。 本文代碼地址:https://github.com/fanpan26/LayIM.AspNetCore/tree/master/src/LayIM.AspNetCore.Demo/SignalRSamples
