第四节:SignalR灵魂所在Hub模型及再探聊天室样例
一. 整体介绍
二. 从零搭建
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().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");
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="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模型,再写一次聊天室,并补充几个新功能。
效果图如下:
包括的功能有:
①:连接成功后通知所有人包括自己登录成功。
②:离线后,通知除了自己以外的所有人已经离开。
③:通过输入接收人的SessionId,实现一对一发送信息。
④: 通过点击群发按钮,向除了自己以外的所有人发送信息。
⑤:可以进入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 //[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 }
控制器代码
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 }
代理
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>
非代理
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>
PS 与WebSocket原生代码编写的相比较,最大的改进之处有:
①:单发和群发可以对应不同的服务器端方法,不再需要自行约定规则来识别了,同样客户端接收也很灵活了,可以指定方法接收。
②:不需要自己创建登录标记,默认会生成一个SessionID。
③:SessionId会自动记录,不需要单独声明一个集合来存储。
④:写起来太爽了(^_^)。
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 :
- 声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,如需代码请在评论处留下你的邮箱