ASP.NET SignalR 系列(四)之指定对象推送
在上一章讲到了广播推送,即所有订阅的用户都能收到,这种适合于信息广播。
接下来介绍如何给指定的对象推送
在讲这个之前先说明一下连接创建的基础知识
1、每个页面与服务端创建连接并启动时,这时服务端会产生一个connectionId作为与这个客户端连接的唯一标识。
2、这个connectionId将作为服务端向指定客户端推送的依据
3、同一个页面刷新后ConnectionId一样不变
4、这个connectionId是服务端自动生成的一个随机数,无法变更。
一、只推送给自己
为了与之前的例子区分,我们分别在服务端DemoHub新建个发送给自己的方法
public class DemoHub : Hub { /// <summary> /// 示例 /// </summary> /// <param name="content">广播的内容</param> public void Hello(string content) { Clients.All.show(content); //调用了前端js上定义的hello方法 } /// <summary> /// 推送给自己 /// </summary> /// <param name="content"></param> public void CallSelf(string content) { Clients.Caller.show(content); } }
这里使用了Caller,而不是All,表示只推送给呼叫方,即自己。
前端脚本逻辑不变,只是多了一个发送给自己的按钮的推送到服务端CallSelf事件而已。
//定义推送 $.connection.hub.start() .done(function () { $("#btn_sendCall").click(function () { chat.server.callSelf($("#content").val()); //将客户端的content内容发送到服务端 $("#content").val(""); }); });
结果:
我是在窗口2这边发送的,第一条点击“发送”按钮,是广播,两个窗口都能收到;第二条点击“发送给自己”,只有窗口2收到。
二、其他推送方式
其他推送方式对于客户端(前端)来说不变,主要就是服务端推送对象改变而已
以下列出其他推送方式:
- 所有连接的客户端
Clients.All.show(content); - 仅调用的客户端
Clients.Caller.show(content); - 除调用客户端之外的所有客户端
Clients.Others.show(content); - 特定的客户端标识的连接 id
Clients.Client(Context.ConnectionId).show(content); - 所有连接的客户端除外指定客户端,由连接 ID 标识
Clients.AllExcept(connectionId1, connectionId2).show(content); - 指定组中的所有连接的客户端
Clients.Group(groupName).show(content); - 指定组中的所有连接的客户端除外指定客户端,由连接 ID 标识。
Clients.Group(groupName, connectionId1, connectionId2).show(content); - 所有连接的客户端指定组中除调用客户端
Clients.OthersInGroup(groupName).show(content); - 所有客户端和组列表中的连接 Id
Clients.Clients(ConnectionIds).show(content); - 组的列表。
Clients.Groups(GroupIds).show(content); - 按名称的用户
Clients.Client(username).show(content); //这里的username我还没理解是怎么创建的 - (在 SignalR 2.1 中引入) 的用户名称的列表。
Clients.Users(new string[] { "myUser", "myUser2" }).show(content)
三、说说ConnectionId
前面已经说到,每个连接的页面都会产生不同的连接id,并且这个连接id是随机产生,不能自定义的,那我们如何获取呢
集线器中为我们提供了Context对象可以获取到这个连接id,先看下面简单的例子
/// <summary> /// 返回每个连接的id /// </summary> public void ReturnConnectionId() { Clients.Caller.show(Context.ConnectionId); }
前端:
//定义推送,返回各个客户端连接 $.connection.hub.start() .done(function () { $("#btn_sendConnectionId").click(function () { chat.server.returnConnectionId(); //将客户端的content内容发送到服务端 }); });
结果:
从结果可以看出,分别点击返回各自的id,返回了不同的id。这个就验证了系统给每个调用端分配一个不相同的随机ConnectionId。
那我们如何利用这个ConnnectionId来实现给某个客户端发送消息,比如一对一的聊天。这个就需要我们实现将人与ConnectionId信息建立对应关系。
如,我们假设张三和李四一对一聊天,那怎么实现,张三发送李四的消息只有李四收到呢,反之,亦然。即我们需要分别确定张三和李四的ConnectionId,并将它们的ConnnectionId与张三、李四建立对应关系。
即建立以下的对应关系
张三 张三的ConnectionId
李四 李四的ConnectionId
张三和李四是已知的,那么就需要在张三和李四分别与集线器建立连接生成ConnenctionId时,我们就要实现对应关系的建立。
这时候需要重载建立连接的方法:
服务端:
/// <summary> /// 发送给指定连接 /// </summary> /// <param name="toName"></param> /// <param name="content"></param> public void CallOne(string toName,string content) { //根据username获取对应的ConnectionId var connectionId = HttpContext.Current.Application[toName].ToString(); Clients.Client(connectionId).show(content); } /// <summary> /// 初次连接 /// </summary> /// <returns></returns> public override Task OnConnected() { string username = Context.QueryString["userName"]; //获取客户端发送过来的用户名 string connectionId = Context.ConnectionId; HttpContext.Current.Application.Add(username,connectionId); //存储关系 return base.OnConnected(); }
客户端:建立zhangsan.html和lisi.html 分别表示张三和李四的窗口
zhangsan.html
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> <link href="Content/bootstrap.min.css" rel="stylesheet" /> <script src="Scripts/jquery-1.10.2.min.js"></script> <script src="Scripts/jquery.signalR-2.3.0.min.js"></script> <script src="/signalr/hub/hubs"></script> <meta charset="utf-8"/> <style type="text/css"> body { margin: 20px; } .input { padding-left: 5px; } </style> </head> <body> <div> <h4>我是张三</h4> <p> <input type="text" id="content" placeholder="" class="input"/> <input type="button" value="发送给李四" class="btn btn-sm btn-info" id="btn_send"/> </p> <div> <h4>接收到的信息:</h4> <ul id="dataContainer"> </ul> </div> </div> <script language="javascript"> $(function() { var chat = $.connection.demoHub; //连接服务端集线器,demoHub为服务端集线器名称,js上首字母须改为小写(系统默认) //定义客户端方法,此客户端方法必须与服务端集线器中的方法名称、参数均一致。 //实际上是服务端调用了前端的js方法(订阅) $.connection.hub.qs = { 'userName': '张三' } chat.client.show=function(content) { var html = '<li>' + htmlEncode(content) + "</li>"; $("#dataContainer").append(html); } //定义推送 $.connection.hub.start() .done(function() { $("#btn_send").click(function() { chat.server.callOne("李四",$("#content").val()); //将客户端的content内容发送到服务端 $("#content").val(""); }); }); }); //编码 function htmlEncode(value) { var encodedValue = $('<div />').text(value).html(); return encodedValue; } </script> </body> </html>
lisi.html:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <link href="Content/bootstrap.min.css" rel="stylesheet" /> <script src="Scripts/jquery-1.10.2.min.js"></script> <script src="Scripts/jquery.signalR-2.3.0.min.js"></script> <script src="/signalr/hub/hubs"></script> <meta charset="utf-8" /> <style type="text/css"> body { margin: 20px; } .input { padding-left: 5px; } </style> </head> <body> <div> <h4>我是李四</h4> <p> <input type="text" id="content" placeholder="" class="input" /> <input type="button" value="发送给张三" class="btn btn-sm btn-info" id="btn_send" /> </p> <div> <h4>接收到的信息:</h4> <ul id="dataContainer"></ul> </div> </div> <script language="javascript"> $(function() { var chat = $.connection.demoHub; //连接服务端集线器,demoHub为服务端集线器名称,js上首字母须改为小写(系统默认) //定义客户端方法,此客户端方法必须与服务端集线器中的方法名称、参数均一致。 //实际上是服务端调用了前端的js方法(订阅) $.connection.hub.qs = { 'userName': '李四' } chat.client.show=function(content) { var html = '<li>' + htmlEncode(content) + "</li>"; $("#dataContainer").append(html); } //定义推送 $.connection.hub.start() .done(function() { $("#btn_send").click(function() { chat.server.callOne("张三", $("#content").val()); //将客户端的content内容发送到服务端 $("#content").val(""); }); }); }); //编码 function htmlEncode(value) { var encodedValue = $('<div />').text(value).html(); return encodedValue; } </script> </body> </html>
说明:
1、参数传递的方法
$.connection.hub.qs = { 'userName': '李四' }
2、后端获取参数的方法:
Context.QueryString["username"]
3、本例中利用Application对象来存储用户与ConnectionId的对应关系,当然,实际项目中也可以用其他方式存储。
示例结果:
本章结束!