一、SignalR 概述SignalR是微軟為實現實時通信的一個類庫。一般情況下,signalR會使用JavaScript的長輪詢(long polling)的方式來實現客戶端和服務器通信,隨著Html5中WebSockets出現,SignalR也支持WebSockets通信。另外SignalR開發的程序不僅僅限制于宿主在IIS中,也可以宿主在任何應用程序,包括控制臺,客戶端程序和Windows服務等,另外還支持Mono,這意味著它可以實現跨平臺部署在Linux環境下。
signalR內部有兩類對象:
- 持久連接
一個持久連接代表了一個端點,它可以發送單一接收者,Group接受者或者廣播信息。持久連接的api是SignalR提供給開發者進入低級別協議的api。連接模型使用起來和WCF比較類似。
- Hubs(集線器)
Hubs是SignalR提供的高級別的api,它允許客戶端和服務端,在自己這邊相互調用對方的方法。Hubs模型類似于.Net Remoting。使用Hubs也可以讓你傳遞強類型參數,進行模型綁定。
SignalR將整個信息的交換封裝起來,客戶端和服務器都是使用JSON來溝通的,在服務端聲明的所有Hub信息,都會生成JavaScript輸出到客戶端,.NET則依賴Proxy來生成代理對象,而Proxy的內部則是將JSON轉換成對象。

SignalR 和 WebSocket如果客戶端和服務器都支持WebSocket,那么SignalR會通過WebSocket來傳輸數據。當然你也可以自己使用WebSocket來實現SignalR的功能,不過使用SignalR你就不用考慮如果客戶端或者服務器不支持WebSocket的問題了。
二、SignalR的協議選擇SignalR是可以在客戶端和服務器端進行即時通訊的幾種協議的抽象和實現。一個SignalR連接是通過http請求發起的,然后上升為WebSocket(如果客戶端和服務端都支持)。WebSocket是SignalR最理想的協議,它可以有效地利用服務器端的內存,有著最低的延遲,最多的基礎特性(比如客戶端和服務端的全雙工連接),不過它也有著嚴格的要求,需要服務器端使用Windows Server 2012或者Windows 8以上的系統,也需要.NET Framework 4.5.。如果不符合這些要求,那么SignalR會使用其他的協議來建立連接。
HTML 5協議
?WebSocket。如果服務器和客戶端都支持,那么就使用WebSocket協議來進行通訊。
?服務器推送事件(Server-sent Events)。除了IE,其他的瀏覽器基本都支持。
Comet協議
?Forever Frame (只支持IE)。
?Ajax長輪詢(Ajax long polling)。
SignalR協議選擇過程
1.如果瀏覽器是IE8或者更早的版本,使用長輪詢。
2.如果配置了Jsonp(如果連接開始的時候jsonp的參數設置為true), 使用長輪詢。
3.如果是跨域連接, 如果下面的條件符合就會使用WebSocket,如果有條件不符合,那就是用長輪詢。
?客戶端支持跨域連接
?客戶端支持WebSocket
?服務器端支持WebSocket
4.如果沒有配置jsonp,而且不是跨域連接,如果客戶端和服務端都支持WebSocket,那么就使用WebSocket。
5.如果客戶端或者服務端不支持WebSocket,使用服務器推送事件。
6.如果不支持服務器推送事件,使用Forever Frame。
7.如果不支持Forever Frame,使用長輪詢。
監控協議可以通過在你的Hub上開啟logging來監控你的SignalR使用了什么協議。
$.connection.hub.logging = true;
指定協議SignalR判斷協議也需要消耗一定的客戶端、服務端資源,如果你清楚客戶端、服務端支持的協議,那么你可以指定使用某種協議來建立連接。
比如,你知道客戶端只支持長輪詢,那么你可以指定使用長輪詢來進行通訊。
connection.start({ transport: 'longPolling' });
你也可以指定一個序列,客戶端會按照序列里的順序來進行通訊。下面的代碼的作用是,先使用WebSocket,如果失敗了,就使用長輪詢。
connection.start({ transport: ['webSockets','longPolling'] });
SignalR包含下面四種指定的協議常量
?webSockets
?foreverFrame
?serverSentEvents
?longPolling
三、SignalR的三種實現方式1. 集線器類(Hub) + 非自動生成代理模式
服務端與客戶端分別定義的相對應的方法,客戶端通過代理對象調用服務端的方法,服務端通過IHubConnectionContext回調客戶端的方法,客戶端通過回調方法接收結果。
JS端調用服務端方法采用:chat.invoke,而被服務端回調的方法則采用:chat.on (這里的chat是createHubProxy創建得來的)
var conn = $.hubConnection();
conn.qs = { "clientName": clientName };
conn.start().done(function () {
$("#btnSend").click(function () {
var toUserId = eUsers.val();
if (toUserId != "") {
chat.invoke("sendOne", toUserId, $("#message").val())
.done(function () {
//alert("發送成功!");
$("#message").val("").focus();
})
.fail(function (e) {
alert(e);
$("#message").focus();
});
}
else {
chat.invoke("send", $("#message").val())
.done(function () {
//alert("發送成功!");
$("#message").val("").focus();
})
.fail(function (e) {
alert(e);
$("#message").focus();
});
}
});
});
var chat = conn.createHubProxy("chat");
chat.on("receiveMessage", function (dt, cn, msg) {
var clsName = "linfo";
if (cn == clientName || cn.indexOf("您對") >= 0) clsName = "rinfo";
eChatBox.append("<p class='" + clsName + "'>" + dt + " <strong>" + cn + "</strong> 說:<br/>" + msg + "</p>");
eChatBox.scrollTop(eChatBox[0].scrollHeight);
});
chat.on("userChange", function (dt, msg, users) {
eChatBox.append("<p>" + dt + " " + msg + "</p>");
eUsers.find("option[value!='']").remove();
for (var i = 0; i < users.length; i++) {
if (users[i].Value == clientName) continue;
eUsers.append("<option value='" + users[i].Key + "'>" + users[i].Value + "</option>")
}
});
2. 集線器類(Hub)+ 自動生成代理模式
需要js引用
<script src="~/signalr/hubs" type="text/javascript"></script>
然而,我們在寫代碼的時候上面的引用并不存在,而當運行后就會自動生成上述signalr的代理腳本
這就是與非自動生成代理腳本最根本的區別,也正是因為這個自動生成的腳本,我們可以在JS中更加方便的調用服務端方法及定義回調方法,調用服務端方法采用:chat.server.XXX,而被服務端回調的客戶端方法則采用:chat.client.XXX
3.持久化連接類(PersistentConnection)
?
Startup.Configuration中是需要指定app.MapSignalR<MyConnection>("/MyConnection")
?
需實現繼承自PersistentConnection類的自定義的持久化連接類,在這個連接中可以重寫:OnConnected、OnDisconnected、OnReceived、OnReconnected、ProcessRequest方法,同時有幾個重要的屬性成員Connection、Groups,服務端發消息給客戶端采用:Connection.Broadcast(廣播,所有客戶端都可以收到消息),Connection.Send(發送給指定的客戶端)
具體實現參考
四、使用RignalR實現新消息推送(集線器類(Hub)+ 自動生成代理模式
)1.app.MapSignalR();
using System.Data.Entity;
using Microsoft.Owin;
using Owin;
using RCRS.WebApp.Town.Migrations;
using RCRS.WebApp.Town.Models.DomainEntity;
[assembly: OwinStartupAttribute(typeof(RCRS.WebApp.Town.Startup))]
namespace RCRS.WebApp.Town
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.MapSignalR();
Database.SetInitializer(new MigrateDatabaseToLatestVersion<TownContext, TownConfiguration>());
}
}
}
2. NotificationHub
using System.Linq;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using RCRS.WebApp.Town.Models.Town;
namespace RCRS.WebApp.Town.Hubs
{
[HubName("NotificationHub")]
public class NotificationHub : Hub
{
public void Connect(string userId)
{
var id = Context.ConnectionId;
if (BizHub.ConnectedUsers.Count(x => x.ConnectionId == id) == 0)
{
BizHub.ConnectedUsers.Add(new HubUser { ConnectionId = id, UserId = userId });
// send to caller
Clients.Caller.onConnected(id, userId, BizHub.ConnectedUsers);
// send to all except caller client
Clients.AllExcept(id).onNewUserConnected(id, userId);
}
}
public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
{
var item = BizHub.ConnectedUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
if (item != null)
{
BizHub.ConnectedUsers.Remove(item);
var id = Context.ConnectionId;
Clients.All.onUserDisconnected(id, item.UserId);
}
return base.OnDisconnected(stopCalled);
}
}
}
3.BizHub
/// <summary> </summary>
public static List<HubUser> ConnectedUsers = new List<HubUser>();
public void NotifyAll(string msg)
{
var hub = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
hub.Clients.All.broadcaastNotif(msg);
}
public void NotifyPrivate(string toUserId, string msg)
{
var toUser = ConnectedUsers.FirstOrDefault(x => x.UserId == toUserId);
var hub = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
if (toUser != null)
{
// send to
hub.Clients.Client(toUser.ConnectionId).broadcaastNotif(msg);
}
}
public void NotifyRole(List<string> roleLs, string msg)
{
List<string> lsUserIds = new List<string>();
using (ApplicationDbContext context = new ApplicationDbContext())
{
string cmd = getUsersByRoleLs(roleLs);
lsUserIds = context.Database.SqlQuery<string>(cmd).ToListAsync().Result;
}
foreach (string toUserId in lsUserIds)
NotifyPrivate(toUserId, msg);
}
4.引用js
bundles.Add(new ScriptBundle("~/bundles/signalR").Include(
"~/Scripts/jquery.signalR-2.2.3.js"));
5.
<script src="~/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
var id = '@ViewBag.UserId';
var notifyHub = $.connection.NotificationHub;
notifyHub.client.broadcaastNotif = function (message) {
$("#assist-top-new-msg").text(message);
$("#assist-msg-list-new-flg").text(message);
};
$.connection.hub.start()
.done(function () {
console.log("Notification Hub Connected!");
//Server Call
notifyHub.server.connect(id);
})
.fail(function () {
console.log("Could not Connect Notification Hub!");
});
});
</script>






posted on 2018-05-23 15:02
Ying-er 閱讀(1390)
評論(0) 編輯 收藏 所屬分類:
.Net 、
喵