欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-01-12 14:31:46
一. 整体介绍 本节:开始介绍SignalR另外一种通讯模型Hub(中心模型),它是一种RPC模式,允许客户端和服务器端各自自定义方法并且相互调用,对开发者来说相当友好。 该节包括的内容有: ①:从零搭建 ②:Hub模型和URL匹配,默认模式和指定路径 ③:服务器端代码介绍 ④:客户端的两种模式,代 ......

一. 整体介绍

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

 

二. 从零搭建

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

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

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

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

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

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

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

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

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

三. 模型和URL匹配

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

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

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

   那么如何指定路径:

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

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

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

 

四. 服务器端代码介绍

   前端页面的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(方法名))],这样就不会存在问题了,如下图:

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

 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("", "");

截图几段代码:

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

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

 

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

 1. 必备JS文件的引入

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

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

2. 代理JS代码的生成

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

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

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

  可能会用人问,我把自动生成的这个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灵魂所在Hub模型及再探聊天室样例

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

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

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

   配合第二个截图简单分析一下这块源代码,首先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; 与自动生成的代理类建立连接。

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

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

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

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

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

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

代码如下:

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

通过Fiddler检测一下。

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

7. 其它方法

  同PersistentConnection模式中相同,比如开启和检测断线。

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

 

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

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

1. 基本使用

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

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

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

4 默认路径匹配

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

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

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

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

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

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

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

 

七. 第三方调用 

  上面介绍的所有代码都是直接基于 Hub模型这个类来通信的,但在很多情况下,我们可能建立连接后,在别的页面进行通信,而这个页面没法直接和Hub类相连接,我需要借助控制器里的Action来通信,那么问题来了,如何通过调用控制器里的方法来实现发送信息的功能呢?

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

  代码如下:

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

 

八. 聊天室样例

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

  效果图如下:

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

  包括的功能有:

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

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

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

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

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

  ⑥:跳转到新页面,然后在新页面实现向指定人发送消息。

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

Hub模型代码

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

控制器代码

第四节:SignalR灵魂所在Hub模型及再探聊天室样例
 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         #region 03-第三方页面
39         /// <summary>
40         /// 第三方页面
41         /// </summary>
42         /// <param name="connectionId">当前用户的连接标记</param>
43         /// <returns></returns>
44         public ActionResult OtherIndex(string connectionId)
45         {
46             Session["connectionId"] = connectionId;
47             return View();
48         }
49         #endregion
50 
51 
52         /****************************************************下面是第三方调用方法****************************************************************/
53 
54 
55         #region 01-向所有人发送消息
56         /// <summary>
57         /// 向所有人发送消息
58         /// </summary>
59         /// <param name="msg">发送的信息</param>
60         public string MySendAll(string msg)
61         {
62             string myConnectionId = Session["connectionId"].ToString();
63             //Hub模式
64             var hub = GlobalHost.ConnectionManager.GetHubContext<MySpecHub1>();
65             hub.Clients.AllExcept(myConnectionId).receiveMsg($"用户【{myConnectionId}】发来消息:{msg}");
66             return "ok";
67         } 
68         #endregion
69 
70 
71 
72     }
73 }
View Code

代理

第四节:SignalR灵魂所在Hub模型及再探聊天室样例
  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                 //把用户ConnectionID存起来
 35                 $("#j_connectionId").val(connectionId);
 36             };
 37             //2 接收点对点用户发送来的消息
 38             proxy.client.receiveMsg = function (data) {
 39                 $("#j_Msg").append("<li>" + data + "</li>");
 40             }
 41 
 42             //三. 主动事件
 43             //1.建立连接
 44             $("#j_connect").click(function () {
 45                 conn.start().done(function () {
 46                     $("#j_notice").html("连接成功");
 47                 }).fail(function () {
 48                     console.log('Could not connect');
 49                 });
 50             });
 51             //2.断开连接
 52             $("#j_close").click(function () {
 53                 conn.stop();
 54             });
 55             //3.点对点发送消息
 56             $("#j_send").click(function () {
 57                 var state = conn.state;
 58                 if (state == 1) {
 59                     //调用服务器端方法
 60                     proxy.server.sendSingleMsg($("#j_receiveId").val(), $("#j_content").val());
 61                 } else if (state == 0) {
 62                     $("#j_notice").html("正在连接中,请稍等");
 63                 } else if (state == 2) {
 64                     $("#j_notice").html("正在重连,请稍等");
 65                 } else if (state == 4) {
 66                     $("#j_notice").html("掉线了,请重新连接");
 67                 }
 68 
 69             });
 70             //4.群发消息
 71             $("#j_sendAll").click(function () {
 72                 var state = conn.state;
 73                 if (state == 1) {
 74                     //调用服务器端方法
 75                     proxy.server.SendAllMsg($("#j_content").val());
 76                 } else if (state == 0) {
 77                     $("#j_notice").html("正在连接中,请稍等");
 78                 } else if (state == 2) {
 79                     $("#j_notice").html("正在重连,请稍等");
 80                 } else if (state == 4) {
 81                     $("#j_notice").html("掉线了,请重新连接");
 82                 }
 83 
 84             });
 85             //5.进入room1
 86             $("#j_room1").click(function () {
 87                 //调用服务器端方法
 88                 proxy.server.EnterRoom("room1");
 89             });
 90             //6.进入room2
 91             $("#j_room2").click(function () {
 92                 //调用服务器端方法
 93                 proxy.server.EnterRoom("room2");
 94             });
 95             //7. 给room1中的用户发送消息
 96             $("#j_sendRoom1").click(function () {
 97                 proxy.server.SendRoomNameMsg("room1", $('#j_content2').val());
 98             });
 99             //8. 给room2中的用户发送消息
100             $("#j_sendRoom2").click(function () {
101                 proxy.server.SendRoomNameMsg("room2", $('#j_content2').val());
102             });
103 
104 
105 
106 
107 
108 
109 
110             //四. 监控事件
111             //1. 连接断开的方法
112             conn.disconnected(function () {
113                 $("#j_notice").html("连接中断");
114             });
115 
116             //五. 其它事件
117             //1. 跳转到第三方页面
118             $("#j_btn").click(function () {
119                 var connectionId = $("#j_connectionId").val();
120                 window.location.href = "/Hub/OtherIndex?connectionId=" + connectionId;
121 
122             });
123 
124         });
125     </script>
126 </head>
127 <body>
128     <div>
129         <div><span>提示:</span><span id="j_notice"></span></div>
130         <div style="margin-top:20px">
131             <button id="j_connect">建立连接</button>
132             <button id="j_close">关闭连接</button>
133         </div>
134         <div style="margin-top:20px">
135             <input type="text" value="" placeholder="请输入接收人的标记" id="j_receiveId" />
136             <input type="text" value="" placeholder="请输入发送内容" id="j_content" />
137             <button id="j_send">单发</button>
138             <button id="j_sendAll">群发</button>
139         </div>
140         <div style="margin-top:20px">
141             <button id="j_room1">进入room1</button>
142             <button id="j_room2">进入room2</button>
143         </div>
144         <div style="margin-top:20px">
145             <input type="text" value="" placeholder="请输入发送内容" id="j_content2" />
146             <button id="j_sendRoom1">给room1发送消息</button>
147             <button id="j_sendRoom2">给room2发送消息</button>
148         </div>
149         <div style="margin-top:20px">
150             <button id="j_btn">跳转到新页面</button>
151         </div>
152         <div>
153             <ul id="j_Msg"></ul>
154         </div>
155     </div>
156     <input type="hidden"  value="" id="j_connectionId"/>
157 </body>
158 </html>
View Code

 非代理

第四节:SignalR灵魂所在Hub模型及再探聊天室样例
  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                 //把用户ConnectionID存起来
 39                 $("#j_connectionId").val(connectionId);
 40             });
 41             //2 接收点对点用户发送来的消息
 42             proxy.on("receiveMsg", function (data) {
 43                 $("#j_Msg").append("<li>" + data + "</li>");
 44             });
 45 
 46 
 47 
 48             //三. 主动事件
 49             //1.建立连接
 50             $("#j_connect").click(function () {
 51                 conn.start().done(function () {
 52                     $("#j_notice").html("连接成功");
 53                 }).fail(function () {
 54                     console.log('Could not connect');
 55                 });
 56             });
 57             //2.断开连接
 58             $("#j_close").click(function () {
 59                 conn.stop();
 60             });
 61 
 62             //3.点对点发送消息
 63             $("#j_send").click(function () {
 64                 var state = conn.state;
 65                 if (state == 1) {
 66                     //调用服务器端方法
 67                     proxy.invoke("SendSingleMsg", $("#j_receiveId").val(), $("#j_content").val());
 68                 } else if (state == 0) {
 69                     $("#j_notice").html("正在连接中,请稍等");
 70                 } else if (state == 2) {
 71                     $("#j_notice").html("正在重连,请稍等");
 72                 } else if (state == 4) {
 73                     $("#j_notice").html("掉线了,请重新连接");
 74                 }
 75 
 76             });
 77             //4.群发消息
 78             $("#j_sendAll").click(function () {
 79                 var state = conn.state;
 80                 if (state == 1) {
 81                     //调用服务器端方法
 82                     proxy.invoke("SendAllMsg", $("#j_content").val());
 83                 } else if (state == 0) {
 84                     $("#j_notice").html("正在连接中,请稍等");
 85                 } else if (state == 2) {
 86                     $("#j_notice").html("正在重连,请稍等");
 87                 } else if (state == 4) {
 88                     $("#j_notice").html("掉线了,请重新连接");
 89                 }
 90 
 91             });
 92 
 93             //5.进入room1
 94             $("#j_room1").click(function () {
 95                 //调用服务器端方法
 96                 proxy.invoke("EnterRoom", "room1");
 97             });
 98             //6.进入room2
 99             $("#j_room2").click(function () {
100                 //调用服务器端方法
101                 proxy.invoke("EnterRoom", "room2");
102             });
103             //7. 给room1中的用户发送消息
104             $("#j_sendRoom1").click(function () {
105                 proxy.invoke("SendRoomNameMsg", "room1", $('#j_content2').val());
106             });
107             //8. 给room2中的用户发送消息
108             $("#j_sendRoom2").click(function () {
109                 proxy.invoke("SendRoomNameMsg", "room2", $('#j_content2').val());
110             });
111 
112 
113 
114             //四. 监控事件
115             //1. 连接断开的方法
116             conn.disconnected(function () {
117                 $("#j_notice").html("连接中断");
118             });
119 
120 
121             //五. 其它事件
122             //1. 跳转到第三方页面
123             $("#j_btn").click(function () {
124                 var connectionId = $("#j_connectionId").val();
125                 window.location.href = "/Hub/OtherIndex?connectionId=" + connectionId;
126 
127             });
128 
129         });
130     </script>
131 </head>
132 <body>
133     <div>
134         <div><span>提示:</span><span id="j_notice"></span></div>
135         <div style="margin-top:20px">
136             <button id="j_connect">建立连接</button>
137             <button id="j_close">关闭连接</button>
138         </div>
139         <div style="margin-top:20px">
140             <input type="text" value="" placeholder="请输入接收人的标记" id="j_receiveId" />
141             <input type="text" value="" placeholder="请输入发送内容" id="j_content" />
142             <button id="j_send">单发</button>
143             <button id="j_sendAll">群发</button>
144         </div>
145         <div style="margin-top:20px">
146             <button id="j_room1">进入room1</button>
147             <button id="j_room2">进入room2</button>
148         </div>
149         <div style="margin-top:20px">
150             <input type="text" value="" placeholder="请输入发送内容" id="j_content2" />
151             <button id="j_sendRoom1">给room1发送消息</button>
152             <button id="j_sendRoom2">给room2发送消息</button>
153         </div>
154         <div style="margin-top:20px">
155             <button id="j_btn">跳转到新页面</button>
156         </div>
157         <div>
158             <ul id="j_Msg"></ul>
159         </div>
160     </div>
161     <input type="hidden" value="" id="j_connectionId" />
162 </body>
163 </html>
View Code

 

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

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

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

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

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

  

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 :
  • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,如需代码请在评论处留下你的邮箱