• 第四节:SignalR灵魂所在Hub模型及再探聊天室样例


    一. 整体介绍

      本节:开始介绍SignalR另外一种通讯模型Hub(中心模型,或者叫集线器模型),它是一种RPC模式,允许客户端和服务器端各自自定义方法并且相互调用,对开发者来说相当友好。
      该节包括的内容有:
        ①:从零搭建
        ②:Hub模型和URL匹配,默认模式和指定路径
        ③:服务器端代码介绍
        ④:客户端的两种模式,代理和非代理,各自的写法
        ⑤:服务器端和客户端包含的方法和各自如何自定义方法并且相互调用
        ⑥:第三方调用的问题,比如:如何在控制器中调用客户端的方法。
        ⑦:再探聊天室样例
     

    二. 从零搭建

     1. 新建MVC5项目,通过Nuget安装程序集:Microsoft.AspNet.SignalR,安装成功后如下图。

    2. 新建一个中心模型Hub类(MySpecHub1),该类继承了Hub类,并且几个必要方法可以override

     

    3. 新建一个OWIN Startup Class(Startup),并在Configuration方法中指定使用的通讯模型的URl,  这里采用默认的方式:如: app.MapSignalR(); 【后续展开介绍如何指定URL及其中的问题】

      PS: 程序启动时候首先会找到该类,然后运行里面的Configuration方法,从而url和通讯模型的匹配将生效。

     

    4. 引入必要的JS文件,进行前端代码的编写,如下图。【后续详细介绍】

     

    三. 模型和URL匹配

       我们都知道,在OWIN Startup Class(即Startup类)中Configuration方法中进行模型URL的指定,并且在很多例子中,看到都是这么写:app.MapSignalR(); 貌似并没有配置URL,但事实上并不是这样的,我们通过反编译代码可以看到,它会默认指定一个路径  "/signalr"  ,如下图:

      特别注意:这里的"/signalr",与js端的自动生成代理类的代码:<script src="~/signalr/hubs"></script>没有任何毛线关系,这两个根本不是一个东西!!!!!,只是路径相似而已罢了。

       那么如何指定路径:

      通过 代码:app.MapSignalR("/myhub1", new HubConfiguration()); 可以将路径指定为:"/myhub1",至于前端页面怎么与之匹配,在下面介绍。

      PS:这里还可以配置其它参数,如下图:

     

    四. 服务器端代码介绍

       前端页面的JS代码有两种模式,代理模式和非代理模式(下面介绍),但无论JS使用哪种模式,服务器端的代码都是唯一不变。

    1. MySpecHub1类继承成Hub类,所以可以Override三个方法:

      (1). OnConnected:连接成功时调用

      (2). OnDisconnected:连接断开时调用

      (3). OnReconnected:重连时调用

    2. 自定义方法

      服务器端可以自定义方法供客户端调用,比如:  public void AddUser(string userName, string userId){....}

        特别特别注意:前端【代理模式】的情况下调用的服务器端方法或者与代理文件建立连接时,有一个非常坑爹的规则,首字母必须小写,比如服务器端定义方法为:“AddUserMsg”,前端【代理模式】情况下调用必须写成:“addUserMsg”;再比如这里的Hub类为 "MySpecHub1",前端调用的时候必须写成"mySpecHub1";对此我表示很无语,当年这一点坑了我很久!!!

      PS:上述指定是【代理模式】,【非代理模式】不存在这个问题。

    虽然我们已经知道这个规则了,但经常写着写着就忘了,那么如何解决上面这个问题呢?:

      这里有两个特性分别是:[HubName()] 和  [HubMethodName()],可以自行指定Hub类和自定义方法的名称,指定为什么,前端调用就用什么,这样【代理模式】下,坑爹的首字母小写规则,就不存在了。为了后续不麻烦,所以我通常在每个方法上面都加: [HubMethodName(nameof(方法名))],这样就不会存在问题了,如下图:

      

     3. 上下文对象(this.Context)

      (1). 当前用户的标记: this.Context.ConnectionId (Guid生成,不会重复)

      (2). 其它信息:RequestCookies、Headers、QueryString、User、Request

    4. 如何调用客户端方法 

      使用Clients对象进行调用,Clients对象下的属性和方法有:

        ① 向所有人发送(包括自己):All { get; }

        ② 向所有人发送(排除一些人):AllExcept(params string[] excludeConnectionIds);

        ③ 向指定人发送,一对一:Client(string connectionId);

        ④ 向一些人发送,一对多:Clients(IList<string> connectionIds);

        ⑤ 向某个组发送(排除一些人):Group(string groupName, params string[] excludeConnectionIds);

        ⑥ 向多个组发送(排除一些人):Groups(IList<string> groupNames, params string[] excludeConnectionIds);

        ⑦ 由Id标识的特定用户:Users(IList<string> userIds);

        ⑧ 由Id标识的特定多用户:User(string userId);

      调用形式比如:Clients.All.客户端方法名称

    5. 组的概念(Groups对象)

      ① 将连接添加到指定组:Task Add(string connectionId, string groupName);

      ② 从指定组中删除连接:Task Remove(string connectionId, string groupName);

      调用如:this.Groups.Add("", "");

    截图几段代码:

    五. 客户端(js)代码介绍-代理模式

     1. 必备JS文件的引入

      前端Html页面使用SignalR,必须引入两个文件:JQuery和SignalR,必须JQuery在上,且最低版本为1.6.4,不能再低了。如下图:

    2. 代理JS代码的生成

       代理JS代码用于帮助客户端调用服务器端自定义方法,注意这里的引入路径只能是: <script src="/signalr/js"></script> 或者  <script src="/signalr/hubs"></script>,至于为什么路径非要这么写?这个地方不纠结了,我们姑且就这么使用(有兴趣探讨一下内部原理吧)。

      引入该代码后,进入页面F12,会发现多了JS代码,没错,这就是自动生成的代理代码,在前端代码的编写中,需要依赖该文件。

      可能会用人问,我把自动生成的这个JS代码拷贝出来,单独放到一个JS文件里,然后在页面引入,并去掉生成代理代码的这句话 <script src="/signalr/js"></script>,行不行呢?

      答案是:肯定行。

        但这种拷贝出来的方式有点Low,服务器端代码只要一改,我就需要重新拷贝一遍,那么有没有别的方便的方法呢?

        显然有,大约有两种方法。

      ①:借助Microsoft.AspNet.SignalR.Utils程序集和指令。

      ②:借助Microsoft.AspNet.SignalR.Utils程序集和VS开发工具

      在这一节里,暂时不介绍这两种方式,后面章节详细介绍。

    3. 如何与服务器Hub模型路径相配?

      在上面的代码中介绍过,服务器Hub模型默认的URL为"/signalr",那么客户端的代码怎么写呢?

    1   //1. 与服务器路径进行匹配
    2   var conn = $.connection.hub;
    3   //2. 与生成的代理类建立连接
    4   var proxy = $.connection.mySpecHub1;

      乍一看,丝毫没有看到与"/signalr"相关的代码,不要急,这时去看一下自动生成代理类中的代码,如下图:

      

      我们再看一下SignalR的JS代码中关于hubConnection方法的声明,如下图:

     

     

       配合第二个截图简单分析一下这块源代码,首先if判断" 一真或为真",只要!url 和 useDefaultPath有一个是真的就进入方法体内部,然后在拼接 url+“/signalr”,如果不进入if方法体,那么你输入的url是什么,这里用的就是什么。

      前面的代码为: signalR.hub = $.hubConnection("/signalr", { useDefaultPath: false }); ||两边都为false,进入不了if方法体内部,所以URL就是默认输入的“/signalr”。

    4. 坑爹的调用规范

      在代理模式中,客户端调用服务器端方法或者与代理建立连接的时候,比如:

      ①:服务器端的Hub名称为MySpecHub1,客户端调用的时候必须为首字母小写:$.connection.mySpecHub1;

      ②:服务器端自定义的方法为SendSingleMsg,客户端调用的时候必须为首字母小写:proxy.server.sendSingleMsg;

      注:非代理模式中则不存在这个问题!!!!

      解决: 引入两个特性[HubName("")] 和 [HubMethodName("")]  ,放在服务器端代码上面,就解决了。 (详见服务器端代码)

    5. 客户端方法的声明和调用服务器端方法

      ①. 声明客户端方法: proxy.client.xxx = function (x1, x2) {}       xxx代表客户端方法名称

      ②. 调用服务器端方法: proxy.server.xxx(x1,x2);                          xxx代表服务器端方法名称

     注:这里的proxy,是 $.connection.mySpecHub1; 与自动生成的代理类建立连接。

    6. 服务器端指定模型URL后,前端如何匹配?

      如服务器端代码为:app.MapSignalR("/myhub1", new HubConfiguration());

      ①. 当使用自动生成代理类js文件时候,与<script src="/signalr/hubs"></script>冲突,暂时未解决 (欢迎下方留言讨论)

      ②. 手动引入代理类时候可以使用,只需添加 conn.url = "/myhub1"; 即可以将路径改为 "/myhub1"。 

    代码如下:

    通过Fiddler检测一下。

     

    7. 其它方法

      同PersistentConnection模式中相同,比如建立连接和检测断线。

     

    六. 客户端(js)代码介绍-非代理模式 

       有了前面代理模式的铺垫,非代理模式就很容易了,下面介绍一下在使用上的一些区别:

    1. 基本使用

      不需要引入 <script src="/signalr/js"></script> 或者 <script src="/signalr/hubs"></script>,也不需要引入手动添加的代理类 <script src="~/Scripts/AutoProxy.js"></script>,但在代码上要这么写,比如创建代理类: $.hubConnection().CreateHubProxy("MySpecHub1");

    详细代码如下:

    1  //1. 与服务器路径进行匹配
    2 var conn = $.hubConnection();
    3 //2. 手动创建代理类
    4  var proxy = conn.createHubProxy("MySpecHub1");

    2. 在非代理模式中,服务器端的Hub名称和服务器端自定义的方法不必首字母小写(PS:小写也能用)

      ①:服务器端的Hub名称为MySpecHub1,客户端调用的时候 conn.createHubProxy("MySpecHub1");

      ②:服务器端自定义的方法为SendSingleMsg,客户端调用的时候必须为首字母小写: proxy.invoke("SendSingleMsg", $("#j_receiveId").val(), $("#j_content").val());

      注:服务器端的两个特性[HubName("")] 和 [HubMethodName("")]仍然好用!!!

    3. 声明客户端方法和调用服务器端方法

      ①. 声明客户端方法: proxy.on("方法名",function(x1,x2,x3){});

      ②. 调用服务器端方法: proxy.invoke("方法名", "参数1","参数2","参数3");

    4 默认路径匹配

      在不使用代理的情况下,$.hubConnection()与服务器路径进行匹配的时候,通过Fiddler可以发现,默认是"/signalr"路径

    5. 服务器端指定路径模型路径:

      如服务器端代码为:app.MapSignalR("/myhub1", new HubConfiguration());

      客户端对应的代码为:$.hubConnection("/myhub1", { useDefaultPath: false });

      注:通过Fidder或者调试源代码,可以看到路径已经改为"/myhub1";

      特别补充:如果客户端代码var conn = $.hubConnection("/myhub1")这么写,useDefaultPath这个属性默认为true,则最后的路径为:"/myhub1/signalr",原因借助前面的分析很容易理解了。

     

    七. 第三方调用 

      上面介绍的所有代码都是直接基于 Hub模型这个类来通信的,但在很多情况下,  发送信息之前需要进行很多其他业务的处理,这个时候所有的代码都写在Hub类中就不太合适了,  这时候需要借助控制器里的Action来通信,那么问题来了,如何通过调用控制器里的方法来实现发送信息的功能呢?

      其实非常简单,我们只需要通过 GlobalHost.ConnectionManager.GetHubContext<MySpecHub1>(); 获取到这个Hub即可。

      代码如下:

     1        /// <summary>
     2         /// 向所有人发送消息
     3         /// </summary>
     4         /// <param name="myConnectionId">当前用户的登录标记</param>
     5         /// <param name="msg">发送的信息</param>
     6         public string MySendAll(string myConnectionId ,string msg)
     7         {
     8             //Hub模式
     9             var hub = GlobalHost.ConnectionManager.GetHubContext<MySpecHub1>();
    10             hub.Clients.AllExcept(myConnectionId).receiveMsg($"用户【{myConnectionId}】发来消息:{msg}");
    11             return "ok";
    12         } 

    八. 聊天室样例

       在本系列的第一节,基于WebSocket写了一个聊天室样例,还吐槽了一番,写法很麻烦,这里基于Signalr的Hub模型,再写一次聊天室,并补充几个新功能。

      效果图如下:

      包括的功能有:

      ①:连接成功后通知所有人包括自己登录成功。

      ②:离线后,通知除了自己以外的所有人已经离开。

      ③:通过输入接收人的ConnectionId,实现一对一发送信息。

      ④: 通过点击群发按钮,向除了自己以外的所有人发送信息。

      ⑤:可以进入room1或room2房间,然后实现向指定房间内的所有人发送信息。

      ⑥:调用控制器中中的方法进行通讯

    下面分享代码,包括(Hub模型代码,控制器代码,前端代码(代理和非代理两套))

    Hub模型代码

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Threading.Tasks;
      5 using System.Web;
      6 using Microsoft.AspNet.SignalR;
      7 using Microsoft.AspNet.SignalR.Hubs;
      8 
      9 namespace HubDemo
     10 {
     11   
     12 
     13     //[HubName("MySpecHub1")]
     14     public class MySpecHub1 : Hub
     15     {
     16 
     17         /**************************************下面是Override的方法*************************************************/
     18 
     19 
     20         #region 01-连接成功的时候调用
     21         /// <summary>
     22         /// 连接成功的时候调用
     23         /// </summary>
     24         /// <returns></returns>
     25         public override Task OnConnected()
     26         {
     27 
     28             //调用客户端的方法通知所有人包括自己
     29             Clients.All.LoginSuccessNotice($"用户【{this.Context.ConnectionId}】登录成功", this.Context.ConnectionId);
     30             //回传给客户端自己的CId
     31             Clients.Client(this.Context.ConnectionId).ReceiveOwnCid(this.Context.ConnectionId);
     32             return base.OnConnected();
     33         }
     34         #endregion
     35 
     36         #region 02-连接断开的时候调用
     37         /// <summary>
     38         /// 连接断开的时候调用
     39         /// </summary>
     40         /// <param name="stopCalled"></param>
     41         /// <returns></returns>
     42         public override Task OnDisconnected(bool stopCalled)
     43         {
     44             //除去自己以外的消息
     45             Clients.AllExcept(this.Context.ConnectionId).receiveMsg($"用户【{this.Context.ConnectionId}】已经离开");
     46             return base.OnDisconnected(stopCalled);
     47         }
     48         #endregion
     49 
     50         #region 03-重新连接的时候调用
     51         /// <summary>
     52         /// 重新连接的时候调用
     53         /// </summary>
     54         /// <returns></returns>
     55         public override Task OnReconnected()
     56         {
     57             return base.OnReconnected();
     58         }
     59         #endregion
     60 
     61         /**************************************下面是自定义的服务器端方法*************************************************/
     62 
     63         #region 01-点对点发送消息
     64         /// <summary>
     65         /// 点对点发送消息
     66         /// </summary>
     67         /// <param name="receiveId"></param>
     68         /// <param name="msg"></param>
     69         public void SendSingleMsg(string receiveId, string msg)
     70         {
     71             Clients.Client(receiveId).receiveMsg($"用户【{this.Context.ConnectionId}】发来消息:{msg}");
     72         }
     73         #endregion
     74 
     75         #region 02-群发消息
     76         /// <summary>
     77         /// 群发消息
     78         /// </summary>
     79         /// <param name="msg"></param>
     80         [HubMethodName(nameof(SendAllMsg))]
     81         public void SendAllMsg(string msg)
     82         {
     83             //除去自己以外的消息(不需要自己存储ConnectionId)
     84             Clients.AllExcept(this.Context.ConnectionId).receiveMsg($"用户【{this.Context.ConnectionId}】发来消息:{msg}");
     85         }
     86         #endregion
     87 
     88         #region 03-进入指定组
     89         /// <summary>
     90         /// 进入指定组
     91         /// </summary>
     92         /// <param name="roomName">组的名称</param>
     93         [HubMethodName(nameof(EnterRoom))]
     94         public void EnterRoom(string roomName)
     95         {
     96             //进入组
     97             Groups.Add(this.Context.ConnectionId, roomName);
     98             //告诉自己进入成功
     99             Clients.Client(this.Context.ConnectionId).receiveMsg($"用户【{this.Context.ConnectionId}】成功进入组:【{roomName}】");
    100         }
    101         #endregion
    102 
    103         #region 04-向指定组发送消息
    104         /// <summary>
    105         /// 向指定组发送消息
    106         /// </summary>
    107         /// <param name="roomName">组名</param>
    108         /// <param name="msg">内容</param>
    109         [HubMethodName(nameof(SendRoomNameMsg))]
    110         public void SendRoomNameMsg(string roomName, string msg)
    111         {
    112             //向指定组发送消息,如果这个组包含自己,将自己除外
    113             Clients.Group(roomName, this.Context.ConnectionId).receiveMsg($"用户【{this.Context.ConnectionId}】发来消息:{msg}");
    114         }
    115         #endregion
    116 
    117     }
    118 }
    View Code

    控制器代码

     1 using Microsoft.AspNet.SignalR;
     2 using System;
     3 using System.Collections.Generic;
     4 using System.Linq;
     5 using System.Web;
     6 using System.Web.Mvc;
     7 
     8 namespace HubDemo.Controllers
     9 {
    10     /// <summary>
    11     /// Hub模型(中心模型)
    12     /// 
    13     /// </summary>
    14     public class HubController : Controller
    15     {
    16         #region 01-代理模式页面
    17         /// <summary>
    18         /// 代理模式页面
    19         /// </summary>
    20         /// <returns></returns>
    21         public ActionResult Index()
    22         {
    23             return View();
    24         }
    25         #endregion
    26 
    27         #region 02-非代理模式页面
    28         /// <summary>
    29         /// 非代理模式页面
    30         /// </summary>
    31         /// <returns></returns>
    32         public ActionResult NoProxyIndex()
    33         {
    34             return View();
    35         }
    36         #endregion
    37 
    38 
    39 
    40         /****************************************************下面是第三方调用方法****************************************************************/
    41 
    42 
    43         #region 01-向所有人发送消息
    44         /// <summary>
    45         /// 向所有人发送消息
    46         /// </summary>
    47         /// <param name="myConnectionId">当前用户的登录标记</param>
    48         /// <param name="msg">发送的信息</param>
    49         public string MySendAll(string myConnectionId ,string msg)
    50         {
    51             //Hub模式
    52             var hub = GlobalHost.ConnectionManager.GetHubContext<MySpecHub1>();
    53             hub.Clients.AllExcept(myConnectionId).receiveMsg($"用户【{myConnectionId}】发来消息:{msg}");
    54             return "ok";
    55         } 
    56         #endregion
    57 
    58 
    59 
    60     }
    61 }
    View Code

    代理

      1 @{
      2     Layout = null;
      3 }
      4 
      5 <!DOCTYPE html>
      6 
      7 <html>
      8 <head>
      9    
     10     <meta name="viewport" content="width=device-width" />
     11     <title>Index</title>
     12     <script src="~/Scripts/jquery-3.3.1.min.js"></script>
     13     <script src="~/Scripts/jquery.signalR-2.3.0.js"></script>
     14     <!--自动生成代理类 -->
     15     <script src="/signalr/hubs"></script>
     16     <!--手动引入代理类 -->
     17     @*<script src="~/Scripts/AutoProxy.js"></script>*@
     18     <script type="text/javascript">
     19         $(function () {
     20 
     21             //一. 初始化信息
     22             //默认路径
     23             //1. 与服务器路径进行匹配
     24             var conn = $.connection.hub;
     25             //2. 与生成的代理类建立连接
     26             var proxy = $.connection.mySpecHub1;
     27             //3.当服务器端指定路径的时候,需要有下面的代码进行匹配(仅使用手动代理)
     28             //conn.url = "/myhub1";
     29 
     30             //二. 定义客户端的方法
     31             //1 接受用户登录成功后的提示
     32             proxy.client.LoginSuccessNotice = function (data, connectionId) {
     33                 $("#j_Msg").append("<li>" + data + "</li>");
     34                
     35             };
     36             //2 接收点对点用户发送来的消息
     37             proxy.client.receiveMsg = function (data) {
     38                 $("#j_Msg").append("<li>" + data + "</li>");
     39             }
     40             //3. 接收自己的connectionId
     41             proxy.client.ReceiveOwnCid = function (connectionId) {
     42                 //把用户ConnectionID存起来
     43                 $("#j_connectionId").val(connectionId);
     44             }
     45 
     46 
     47             //三. 主动事件
     48             //1.建立连接
     49             $("#j_connect").click(function () {
     50                 conn.start().done(function () {
     51                     $("#j_notice").html("连接成功");
     52                 }).fail(function () {
     53                     console.log('Could not connect');
     54                 });
     55             });
     56             //2.断开连接
     57             $("#j_close").click(function () {
     58                 conn.stop();
     59             });
     60             //3.点对点发送消息
     61             $("#j_send").click(function () {
     62                 var state = conn.state;
     63                 if (state == 1) {
     64                     //调用服务器端方法
     65                     proxy.server.sendSingleMsg($("#j_receiveId").val(), $("#j_content").val());
     66                 } else if (state == 0) {
     67                     $("#j_notice").html("正在连接中,请稍等");
     68                 } else if (state == 2) {
     69                     $("#j_notice").html("正在重连,请稍等");
     70                 } else if (state == 4) {
     71                     $("#j_notice").html("掉线了,请重新连接");
     72                 }
     73 
     74             });
     75             //4.群发消息
     76             $("#j_sendAll").click(function () {
     77                 var state = conn.state;
     78                 if (state == 1) {
     79                     //调用服务器端方法
     80                     proxy.server.SendAllMsg($("#j_content").val());
     81                 } else if (state == 0) {
     82                     $("#j_notice").html("正在连接中,请稍等");
     83                 } else if (state == 2) {
     84                     $("#j_notice").html("正在重连,请稍等");
     85                 } else if (state == 4) {
     86                     $("#j_notice").html("掉线了,请重新连接");
     87                 }
     88 
     89             });
     90             //5.进入room1
     91             $("#j_room1").click(function () {
     92                 //调用服务器端方法
     93                 proxy.server.EnterRoom("room1");
     94             });
     95             //6.进入room2
     96             $("#j_room2").click(function () {
     97                 //调用服务器端方法
     98                 proxy.server.EnterRoom("room2");
     99             });
    100             //7. 给room1中的用户发送消息
    101             $("#j_sendRoom1").click(function () {
    102                 proxy.server.SendRoomNameMsg("room1", $('#j_content2').val());
    103             });
    104             //8. 给room2中的用户发送消息
    105             $("#j_sendRoom2").click(function () {
    106                 proxy.server.SendRoomNameMsg("room2", $('#j_content2').val());
    107             });
    108 
    109             //9. 调用控制器中的方法发送信息
    110             $("#j_btn").click(function () {
    111                 var myConnectionId = $("#j_connectionId").val();
    112                 var msg = $('#j_content3').val();
    113                 console.log(myConnectionId);
    114                 $.post("/Hub/MySendAll", { myConnectionId: myConnectionId, msg: msg }, function (data) {
    115                     if (data == "ok") {
    116                         alert("发送成功");
    117                     }
    118                 });
    119 
    120             });
    121 
    122 
    123 
    124 
    125 
    126             //四. 监控事件
    127             //1. 连接断开的方法
    128             conn.disconnected(function () {
    129                 $("#j_notice").html("连接中断");
    130             });
    131 
    132 
    133 
    134 
    135         });
    136     </script>
    137 </head>
    138 <body>
    139     <div>
    140         <div><span>提示:</span><span id="j_notice"></span></div>
    141         <div style="margin-top:20px">
    142             <button id="j_connect">建立连接</button>
    143             <button id="j_close">关闭连接</button>
    144         </div>
    145         <div style="margin-top:20px">
    146             <input type="text" value="" placeholder="请输入接收人的标记" id="j_receiveId" />
    147             <input type="text" value="" placeholder="请输入发送内容" id="j_content" />
    148             <button id="j_send">单发</button>
    149             <button id="j_sendAll">群发</button>
    150         </div>
    151         <div style="margin-top:20px">
    152             <button id="j_room1">进入room1</button>
    153             <button id="j_room2">进入room2</button>
    154         </div>
    155         <div style="margin-top:20px">
    156             <input type="text" value="" placeholder="请输入发送内容" id="j_content2" />
    157             <button id="j_sendRoom1">给room1发送消息</button>
    158             <button id="j_sendRoom2">给room2发送消息</button>
    159         </div>
    160         <div style="margin-top:20px">
    161             <input type="text" value="" placeholder="请输入发送内容" id="j_content3" />
    162             <button id="j_btn">调用控制器中的方法</button>
    163         </div>
    164         <div>
    165             <ul id="j_Msg"></ul>
    166         </div>
    167     </div>
    168     <input type="hidden" value="" id="j_connectionId" />
    169 </body>
    170 </html>
    View Code

    非代理

      1 @{
      2     Layout = null;
      3 }
      4 
      5 <!DOCTYPE html>
      6 
      7 <html>
      8 <head>
      9    
     10     <meta name="viewport" content="width=device-width" />
     11     <title>Index</title>
     12     <script src="~/Scripts/jquery-3.3.1.min.js"></script>
     13     <script src="~/Scripts/jquery.signalR-2.3.0.js"></script>
     14     <script type="text/javascript">
     15         $(function () {
     16 
     17 
     18             //一. 初始化信息
     19             //默认路径
     20             //1. 与服务器路径进行匹配
     21             var conn = $.hubConnection();
     22             //2. 手动创建代理类
     23             var proxy = conn.createHubProxy("MySpecHub1");
     24 
     25 
     26             //指定路径
     27             ////1. 与服务器路径进行匹配
     28             //var conn = $.hubConnection("/myhub1", { useDefaultPath: false }); //对应的路径为"/myhub1"
     29             //////var conn = $.hubConnection("/myhub1");   //对应的路径为"/myhub1/signalr"
     30             ////2. 手动创建代理类
     31             //var proxy = conn.createHubProxy("MySpecHub1");
     32 
     33 
     34             //二. 定义客户端的方法
     35             //1 接受用户登录成功后的提示
     36             proxy.on("LoginSuccessNotice", function (data, connectionId) {
     37                 $("#j_Msg").append("<li>" + data + "</li>");
     38             });
     39             //2 接收点对点用户发送来的消息
     40             proxy.on("receiveMsg", function (data) {
     41                 $("#j_Msg").append("<li>" + data + "</li>");
     42             });
     43             //3. 接收自己的connectionId
     44             proxy.on("ReceiveOwnCid", function (connectionId) {
     45                 //把用户ConnectionID存起来
     46                 $("#j_connectionId").val(connectionId);
     47             });
     48 
     49 
     50 
     51             //三. 主动事件
     52             //1.建立连接
     53             $("#j_connect").click(function () {
     54                 conn.start().done(function () {
     55                     $("#j_notice").html("连接成功");
     56                 }).fail(function () {
     57                     console.log('Could not connect');
     58                 });
     59             });
     60             //2.断开连接
     61             $("#j_close").click(function () {
     62                 conn.stop();
     63             });
     64 
     65             //3.点对点发送消息
     66             $("#j_send").click(function () {
     67                 var state = conn.state;
     68                 if (state == 1) {
     69                     //调用服务器端方法
     70                     proxy.invoke("SendSingleMsg", $("#j_receiveId").val(), $("#j_content").val());
     71                 } else if (state == 0) {
     72                     $("#j_notice").html("正在连接中,请稍等");
     73                 } else if (state == 2) {
     74                     $("#j_notice").html("正在重连,请稍等");
     75                 } else if (state == 4) {
     76                     $("#j_notice").html("掉线了,请重新连接");
     77                 }
     78 
     79             });
     80             //4.群发消息
     81             $("#j_sendAll").click(function () {
     82                 var state = conn.state;
     83                 if (state == 1) {
     84                     //调用服务器端方法
     85                     proxy.invoke("SendAllMsg", $("#j_content").val());
     86                 } else if (state == 0) {
     87                     $("#j_notice").html("正在连接中,请稍等");
     88                 } else if (state == 2) {
     89                     $("#j_notice").html("正在重连,请稍等");
     90                 } else if (state == 4) {
     91                     $("#j_notice").html("掉线了,请重新连接");
     92                 }
     93 
     94             });
     95 
     96             //5.进入room1
     97             $("#j_room1").click(function () {
     98                 //调用服务器端方法
     99                 proxy.invoke("EnterRoom", "room1");
    100             });
    101             //6.进入room2
    102             $("#j_room2").click(function () {
    103                 //调用服务器端方法
    104                 proxy.invoke("EnterRoom", "room2");
    105             });
    106             //7. 给room1中的用户发送消息
    107             $("#j_sendRoom1").click(function () {
    108                 proxy.invoke("SendRoomNameMsg", "room1", $('#j_content2').val());
    109             });
    110             //8. 给room2中的用户发送消息
    111             $("#j_sendRoom2").click(function () {
    112                 proxy.invoke("SendRoomNameMsg", "room2", $('#j_content2').val());
    113             });
    114 
    115             //9. 调用控制器中的方法发送信息
    116             $("#j_btn").click(function () {
    117                 var myConnectionId = $("#j_connectionId").val();
    118                 var msg = $('#j_content3').val();
    119                 console.log(myConnectionId);
    120                 $.post("/Hub/MySendAll", { myConnectionId: myConnectionId, msg: msg }, function (data) {
    121                     if (data == "ok") {
    122                         alert("发送成功");
    123                     }
    124                 });
    125 
    126             });
    127 
    128 
    129 
    130 
    131             //四. 监控事件
    132             //1. 连接断开的方法
    133             conn.disconnected(function () {
    134                 $("#j_notice").html("连接中断");
    135             });
    136 
    137 
    138             //五. 其它事件
    139      
    140 
    141         });
    142     </script>
    143 </head>
    144 <body>
    145     <div>
    146         <div><span>提示:</span><span id="j_notice"></span></div>
    147         <div style="margin-top:20px">
    148             <button id="j_connect">建立连接</button>
    149             <button id="j_close">关闭连接</button>
    150         </div>
    151         <div style="margin-top:20px">
    152             <input type="text" value="" placeholder="请输入接收人的标记" id="j_receiveId" />
    153             <input type="text" value="" placeholder="请输入发送内容" id="j_content" />
    154             <button id="j_send">单发</button>
    155             <button id="j_sendAll">群发</button>
    156         </div>
    157         <div style="margin-top:20px">
    158             <button id="j_room1">进入room1</button>
    159             <button id="j_room2">进入room2</button>
    160         </div>
    161         <div style="margin-top:20px">
    162             <input type="text" value="" placeholder="请输入发送内容" id="j_content2" />
    163             <button id="j_sendRoom1">给room1发送消息</button>
    164             <button id="j_sendRoom2">给room2发送消息</button>
    165         </div>
    166         <div style="margin-top:20px">
    167             <input type="text" value="" placeholder="请输入发送内容" id="j_content3" />
    168             <button id="j_btn">调用控制器中的方法</button>
    169         </div>
    170         <div>
    171             <ul id="j_Msg"></ul>
    172         </div>
    173     </div>
    174     <input type="hidden" value="" id="j_connectionId" />
    175 </body>
    176 </html>
    View Code

      PS:SignalR 与WebSocket原生代码编写的相比较,最大的改进之处有:

      ①:单发和群发可以对应不同的服务器端方法,不再需要自行约定规则来识别了,同样客户端接收也很灵活了,可以指定方法接收。

      ②:不需要自己创建登录标记,默认会生成一个ConnectionId。

      ③:ConnectionId会自动记录,不需要单独声明一个集合来存储。

      ④:写起来太爽了(^_^)。

      

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则将保留追究法律责任,如需代码请在评论处留下你的邮箱
  • 相关阅读:
    [转贴]Linux内核LTS长期支持版生命周期
    【转贴】Debian 10 "buster" 正式发布
    [百度经验]重置组策略的方法
    【转贴】我对测试工作的一些认识
    【转贴】使用sar进行性能分析
    【转贴】龙芯内核发展策略 已经支持k8s
    【转贴】优秀的龙芯Docker虚拟机解决方案
    Windows 2016 安装单机版本Oracle ASM 的简单说明
    【转贴】中标麒麟操作系统(龙芯版)与360安全浏览器完成产品兼容性互认证测试
    AQTime教程
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/9304308.html
Copyright © 2020-2023  润新知