一、SignalR 概述
          https://docs.microsoft.com/en-us/aspnet/signalr/index
          SignalR是微軟為實(shí)現(xiàn)實(shí)時(shí)通信的一個(gè)類(lèi)庫(kù)。一般情況下,signalR會(huì)使用JavaScript的長(zhǎng)輪詢(xún)(long polling)的方式來(lái)實(shí)現(xiàn)客戶(hù)端和服務(wù)器通信,隨著Html5中WebSockets出現(xiàn),SignalR也支持WebSockets通信。另外SignalR開(kāi)發(fā)的程序不僅僅限制于宿主在IIS中,也可以宿主在任何應(yīng)用程序,包括控制臺(tái),客戶(hù)端程序和Windows服務(wù)等,另外還支持Mono,這意味著它可以實(shí)現(xiàn)跨平臺(tái)部署在Linux環(huán)境下。

              signalR內(nèi)部有兩類(lèi)對(duì)象:
          1. 持久連接
            一個(gè)持久連接代表了一個(gè)端點(diǎn),它可以發(fā)送單一接收者,Group接受者或者廣播信息。持久連接的api是SignalR提供給開(kāi)發(fā)者進(jìn)入低級(jí)別協(xié)議的api。連接模型使用起來(lái)和WCF比較類(lèi)似。
          2. Hubs(集線(xiàn)器)
            Hubs是SignalR提供的高級(jí)別的api,它允許客戶(hù)端和服務(wù)端,在自己這邊相互調(diào)用對(duì)方的方法。Hubs模型類(lèi)似于.Net Remoting。使用Hubs也可以讓你傳遞強(qiáng)類(lèi)型參數(shù),進(jìn)行模型綁定。
          SignalR將整個(gè)信息的交換封裝起來(lái),客戶(hù)端和服務(wù)器都是使用JSON來(lái)溝通的,在服務(wù)端聲明的所有Hub信息,都會(huì)生成JavaScript輸出到客戶(hù)端,.NET則依賴(lài)Proxy來(lái)生成代理對(duì)象,而Proxy的內(nèi)部則是將JSON轉(zhuǎn)換成對(duì)象。



          SignalR 和 WebSocket
          如果客戶(hù)端和服務(wù)器都支持WebSocket,那么SignalR會(huì)通過(guò)WebSocket來(lái)傳輸數(shù)據(jù)。當(dāng)然你也可以自己使用WebSocket來(lái)實(shí)現(xiàn)SignalR的功能,不過(guò)使用SignalR你就不用考慮如果客戶(hù)端或者服務(wù)器不支持WebSocket的問(wèn)題了。

          二、SignalR的協(xié)議選擇
          SignalR是可以在客戶(hù)端和服務(wù)器端進(jìn)行即時(shí)通訊的幾種協(xié)議的抽象和實(shí)現(xiàn)。一個(gè)SignalR連接是通過(guò)http請(qǐng)求發(fā)起的,然后上升為WebSocket(如果客戶(hù)端和服務(wù)端都支持)。WebSocket是SignalR最理想的協(xié)議,它可以有效地利用服務(wù)器端的內(nèi)存,有著最低的延遲,最多的基礎(chǔ)特性(比如客戶(hù)端和服務(wù)端的全雙工連接),不過(guò)它也有著嚴(yán)格的要求,需要服務(wù)器端使用Windows Server 2012或者Windows 8以上的系統(tǒng),也需要.NET Framework 4.5.。如果不符合這些要求,那么SignalR會(huì)使用其他的協(xié)議來(lái)建立連接。

          HTML 5協(xié)議
          ?WebSocket。如果服務(wù)器和客戶(hù)端都支持,那么就使用WebSocket協(xié)議來(lái)進(jìn)行通訊。
          ?服務(wù)器推送事件(Server-sent Events)。除了IE,其他的瀏覽器基本都支持。
          Comet協(xié)議
          ?Forever Frame (只支持IE)。
          ?Ajax長(zhǎng)輪詢(xún)(Ajax long polling)。
          SignalR協(xié)議選擇過(guò)程
          1.如果瀏覽器是IE8或者更早的版本,使用長(zhǎng)輪詢(xún)。
          2.如果配置了Jsonp(如果連接開(kāi)始的時(shí)候jsonp的參數(shù)設(shè)置為true), 使用長(zhǎng)輪詢(xún)。
          3.如果是跨域連接, 如果下面的條件符合就會(huì)使用WebSocket,如果有條件不符合,那就是用長(zhǎng)輪詢(xún)。
              ?客戶(hù)端支持跨域連接
              ?客戶(hù)端支持WebSocket
              ?服務(wù)器端支持WebSocket
          4.如果沒(méi)有配置jsonp,而且不是跨域連接,如果客戶(hù)端和服務(wù)端都支持WebSocket,那么就使用WebSocket。
          5.如果客戶(hù)端或者服務(wù)端不支持WebSocket,使用服務(wù)器推送事件。
          6.如果不支持服務(wù)器推送事件,使用Forever Frame。
          7.如果不支持Forever Frame,使用長(zhǎng)輪詢(xún)。

          監(jiān)控協(xié)議
          可以通過(guò)在你的Hub上開(kāi)啟logging來(lái)監(jiān)控你的SignalR使用了什么協(xié)議。
          $.connection.hub.logging = true;

          指定協(xié)議
          SignalR判斷協(xié)議也需要消耗一定的客戶(hù)端、服務(wù)端資源,如果你清楚客戶(hù)端、服務(wù)端支持的協(xié)議,那么你可以指定使用某種協(xié)議來(lái)建立連接。
          比如,你知道客戶(hù)端只支持長(zhǎng)輪詢(xún),那么你可以指定使用長(zhǎng)輪詢(xún)來(lái)進(jìn)行通訊。
          connection.start({ transport: 'longPolling' });

          你也可以指定一個(gè)序列,客戶(hù)端會(huì)按照序列里的順序來(lái)進(jìn)行通訊。下面的代碼的作用是,先使用WebSocket,如果失敗了,就使用長(zhǎng)輪詢(xún)。
          connection.start({ transport: ['webSockets','longPolling'] });

          SignalR包含下面四種指定的協(xié)議常量
              ?webSockets
              ?foreverFrame
              ?serverSentEvents
              ?longPolling
          三、SignalR的三種實(shí)現(xiàn)方式
          1. 集線(xiàn)器類(lèi)(Hub) + 非自動(dòng)生成代理模式
          服務(wù)端與客戶(hù)端分別定義的相對(duì)應(yīng)的方法,客戶(hù)端通過(guò)代理對(duì)象調(diào)用服務(wù)端的方法,服務(wù)端通過(guò)IHubConnectionContext回調(diào)客戶(hù)端的方法,客戶(hù)端通過(guò)回調(diào)方法接收結(jié)果。
          JS端調(diào)用服務(wù)端方法采用:chat.invoke,而被服務(wù)端回調(diào)的方法則采用:chat.on (這里的chat是createHubProxy創(chuàng)建得來(lái)的)
                      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("發(fā)送成功!");
                                      $("#message").val("").focus();
                                  })
                                  .fail(function (e) {
                                      alert(e);
                                      $("#message").focus();
                                  });
                              }
                              else {
                                  chat.invoke("send", $("#message").val())
                                  .done(function () {
                                      //alert("發(fā)送成功!");
                                      $("#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("您對(duì)") >= 0) clsName = "rinfo";
                          eChatBox.append("<p class='" + clsName + "'>" + dt + " <strong>" + cn + "</strong> 說(shuō):<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. 集線(xiàn)器類(lèi)(Hub)+ 自動(dòng)生成代理模式
          需要js引用
          <script src="~/signalr/hubs" type="text/javascript"></script>
          然而,我們?cè)趯?xiě)代碼的時(shí)候上面的引用并不存在,而當(dāng)運(yùn)行后就會(huì)自動(dòng)生成上述signalr的代理腳本
          這就是與非自動(dòng)生成代理腳本最根本的區(qū)別,也正是因?yàn)檫@個(gè)自動(dòng)生成的腳本,我們可以在JS中更加方便的調(diào)用服務(wù)端方法及定義回調(diào)方法,調(diào)用服務(wù)端方法采用:chat.server.XXX,而被服務(wù)端回調(diào)的客戶(hù)端方法則采用:chat.client.XXX

          3.持久化連接類(lèi)(PersistentConnection)
          ?Startup.Configuration中是需要指定app.MapSignalR<MyConnection>("/MyConnection")
          ?需實(shí)現(xiàn)繼承自PersistentConnection類(lèi)的自定義的持久化連接類(lèi),在這個(gè)連接中可以重寫(xiě):OnConnected、OnDisconnected、OnReceived、OnReconnected、ProcessRequest方法,同時(shí)有幾個(gè)重要的屬性成員Connection、Groups,服務(wù)端發(fā)消息給客戶(hù)端采用:Connection.Broadcast(廣播,所有客戶(hù)端都可以收到消息),Connection.Send(發(fā)送給指定的客戶(hù)端)

          具體實(shí)現(xiàn)參考
          https://www.cnblogs.com/zuowj/p/5674615.html

          四、使用RignalR實(shí)現(xiàn)新消息推送(集線(xiàn)器類(lèi)(Hub)+ 自動(dòng)生成代理模式
          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 閱讀(1394) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): .Net
          主站蜘蛛池模板: 铅山县| 讷河市| 本溪| 育儿| 潼南县| 唐河县| 七台河市| 开封县| 正宁县| 宁明县| 右玉县| 鲁山县| 易门县| 家居| 英吉沙县| 睢宁县| 含山县| 靖宇县| 乌拉特中旗| 靖西县| 华安县| 深泽县| 禄劝| 牟定县| 宿州市| 太白县| 横峰县| 平定县| 潼南县| 左云县| 寿光市| 盐山县| 沂南县| 施秉县| 金秀| 吉安县| 电白县| 芮城县| 海安县| 朝阳市| 治县。|