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"/> &nbsp;&nbsp;<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" /> &nbsp;&nbsp;<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的对应关系,当然,实际项目中也可以用其他方式存储。

示例结果:

 

本章结束!

posted @ 2018-08-30 20:35  黄明辉  阅读(3082)  评论(0编辑  收藏  举报