SignalR 消息被发送到每个打开的页面实例。如何只发送给确切的一个?

SignalR message being sent to every opened page instance. How to send to only exact one?

这个问题已经回答了很多次了,但我还是没明白。

我正在使用此页面中的示例:https://www.codeproject.com/Articles/1124691/SignalR-Progress-Bar-Simple-Example-Sending-Live-D

但是,我稍微修改了 SendProgress 方法,只将消息发送到特定连接,而不是所有客户端:

 public static void SendProgress(string connectionId, string progressMessage, int progressCount, int totalItems)
    {                   
        var hubContext = GlobalHost.ConnectionManager.GetHubContext<RealTimeProgressBar.ProgressHub>();          
        var percentage = (progressCount * 100) / totalItems;           
        hubContext.Clients.Client(connectionId).AddProgress(progressMessage, percentage + "%");
    }

我正在从覆盖的 OnConnected 方法中获取连接 ID:

 public override Task OnConnected()
    {
        Environments.ConnectionId = Context.ConnectionId;
        return base.OnConnected();
    }

我目前将其存储在静态 class Environments 中,我想有更好的方法,但我还不知道 :)

问题是,当我在浏览器中打开我的网页的两个实例时,所有这些实例的进度条仍然显示,尽管这些页面上的 connectionIds 不同(在 SendProgress函数)。

这里有什么问题?

也许,我应该在 js 代码中更改一些内容?

$(function () {
            // Reference the auto-generated proxy for the hub.
            var progress = $.connection.progressHub;
            console.log(progress);

            // Create a function that the hub can call back to display messages.
            progress.client.AddProgress = function (message, percentage) {
                ProgressBarModal("show", message +  " " + percentage);
                $('#ProgressMessage').width(percentage);
                if (percentage == "100%") {
                    ProgressBarModal();
                }
            };

            $.connection.hub.start().done(function () {
                var connectionId = $.connection.hub.id;
                console.log(connectionId);
            });

        });

因为我不太确定 js funcs 是否真的使用 Environments.ConnectionId 或其他东西.. 谢谢!

编辑:我想要实现的是为每个打开的页面发送唯一的进度条。例如当我按下第一页上的按钮时,我开始收集数据,进度条反映了这一点。我还在浏览器中打开另一个页面,并从另一个来源收集数据,因此进度条也应该反映该页面的情况。

我的控制器方法:

 public ActionResult Generate(InputDataViewModel viewModel, string connectionId)
        {
...
            var Tasks = getResultsWithProgressBar(viewModel.jobId, connectionId);
...
            return RedirectToAction("Details", new { id = job.JobDataId });   
        }

在这个 "getResult" 中的某处是一个实际调用进度条的函数 (SendProgress)

您的更改应该可以正常工作。
您使用的是静态 ConnectionId,因此只有您上次使用的浏览器,即您上次打开(或上次连接)的浏览器才能看到进度条和更新。

除了添加静态 Environment.ConnectionId 之外,这是我制作的唯一一个。

        //PUSHING DATA TO ALL CLIENTS
        // hubContext.Clients.All.AddProgress(progressMessage, percentage + "%");
        hubContext.Clients.Client(Environments.ConnectionId).AddProgress(progressMessage, percentage + "%");

因此,如果您打开 10 个浏览器 windows 并单击所有这些浏览器上的按钮,一个接一个,只有您最初最后打开的浏览器会看到进度条和更新,但是 它将看到来自单个进度条上显示的所有 10 个浏览器的更新

虽然很有趣!

当我尝试您的代码时,我看到了您最初观察到的行为。但是当我打开两个独立的隐身浏览器时,我看到了正确的行为。关闭 IIS Express 站点,重建并重试。

编辑: 如果您需要每个选项卡都有自己的进度条,那么您将需要以下两种方法之一:

  • 一种识别哪个rest请求来自哪个connectionId的方法。如果您想继续使用当前代码。只需将连接 ID 传递给 LongRunningProcess。客户端代码如下:

     var progress = $.connection.progressHub;
    var connectionId = $.connection.hub.id;                
    $.getJSON("/Home/LongRunningProcess", 
    {"connectionId":connectionId},
    function (data) {
        if (!data) {
            alert("Success");
        }
        else
        {
            alert(data);
        }    
    });
    

服务器代码为:

    public JsonResult LongRunningProcess(string connectionId)
    {
        //THIS COULD BE SOME LIST OF DATA
        int itemsCount = 100;

        for (int i = 0; i <= itemsCount; i++)
        {
            //SIMULATING SOME TASK
            Thread.Sleep(500);

            //CALLING A FUNCTION THAT CALCULATES PERCENTAGE AND SENDS THE DATA TO THE CLIENT
            Functions.SendProgress(connectionId, "Process in progress...", i , itemsCount);
        }

        return Json("", JsonRequestBehavior.AllowGet);
    }
  • 另一种选择是在服务器上调用 signalr 方法而不是 rest 方法。在 ProgressHub 中创建一个新的 hub 方法,如下所示:

     public void LongRunningHubMethod()
    {
        //THIS COULD BE SOME LIST OF DATA
        int itemsCount = 100;
    
        for (int i = 0; i <= itemsCount; i++)
        {
            //SIMULATING SOME TASK
            Thread.Sleep(500);
    
            //CALLING A FUNCTION THAT CALCULATES PERCENTAGE AND SENDS THE DATA TO THE CLIENT
            Functions.SendProgress(Context.ConnectionId, "Process in progress...", i, itemsCount);
        }
    }
    

然后客户端只需在点击时调用集线器方法,如下所示:

     var progress = $.connection.progressHub;
    var connectionId = $.connection.hub.id;
    progress.invoke('LongRunningHubMethod');