无法使用SignalR向特定用户发送消息

3

我无法通过代码将消息发送给特定用户。 Clients.All 可以正常工作,Clients.AllExcept(userId) 也可以正常工作,但是 Clients.User(userId) 不能正常工作。

我的 Hub:

public class MessagingHub : Hub
{
    public override Task OnConnected()
    {
        var signalRConnectionId = Context.ConnectionId;
        // for testing purpose, I collect the userId from the VS Debug window
        System.Diagnostics.Debug.WriteLine("OnConnected --> " + signalRConnectionId);
        return base.OnConnected();
    }
}

我从代码后台发送消息的控制器:

public void PostMessageToUser(string ConnectionId)
{
    var mappingHub = GlobalHost.ConnectionManager.GetHubContext<MessagingHub>();

    // doesn't works
    mappingHub.Clients.User(ConnectionId).onMessageRecorded();

    // doesn't works
    mappingHub.Clients.Users(new List<string>() { ConnectionId }).onMessageRecorded();

    // works
    mappingHub.Clients.All.onMessageRecorded();

    // works (?!)
    mappingHub.Clients.AllExcept(ConnectionId).onMessageRecorded();

}

如何在JS上初始化我的中心:

var con, hub;
function StartRealtimeMessaging()
{
    con = $.hubConnection();
    hub = con.createHubProxy('MessagingHub');

    hub.on('onMessageRecorded', function () {
        $(".MessageContainer").append("<div>I've received a message!!</div>");
    });
    con.start();
}

最后,我如何向中心发送一条(空)消息:

function TestSendToUser(connectionId)
{
    $.ajax({
        url: '/Default/PostMessageToUser',
        type: "POST",
        data: { ConnectionId: connectionId},// contains the user I want to send the message to
    });
}

因此,mappingHub.Clients.All.onMessageRecorded(); 可以完美地工作,但是 mappingHub.Clients.User(ConnectionId).onMessageRecorded();mappingHub.Clients.Users(new List<string>() { ConnectionId}).onMessageRecorded(); 不起作用。

但是有趣的是,mappingHub.Clients.AllExcept(ConnectionId).onMessageRecorded(); 可以正常工作:所有连接的用户都会收到消息,除了给定的用户ID,这意味着用户ID 是有效的,并且已经成功识别该用户。那么为什么 Clients.User(ConnectionId) 不起作用呢?

3个回答

5
如果您想向特定的连接发送消息,并且想要使用ConnectionId,请确保使用Clients.Client而不是Clients.User
示例代码如下:
public void PostMessageToUser(string connectionId)
{
    var mappingHub = GlobalHost.ConnectionManager.GetHubContext<MessagingHub>();

    // Like this
    mappingHub.Clients.Client(connectionId).onMessageRecorded();

    // or this
    mappingHub.Clients.Clients(new List<string>() { connectionId }).onMessageRecorded();
}

你知道为什么这个有效而User()不行吗? - OmniOwl

4
我遇到了同样的问题,我无法让.User(ConnectionId)起作用。
我刚刚花了几天时间尝试让SignalR仅向请求该作业的客户端报告长时间处理作业的进度。也就是说,它不是大多数示例所描述的聊天应用程序。
我发现的任何“长时间处理进度报告”示例都只在hub中有一个作业模拟。我有一个控制器正在做真正的工作,需要从控制器发送消息,而不是从hub发送。
我使用这个答案https://dev59.com/y2Ik5IYBdhLWcg3wKLWd#21222303作为解决你所述问题的变通方法,但为了让那些偶然发现这个答案的人受益,我包括了我用于长时间处理作业的所有代码片段。
这种变通方法之所以优雅,是因为它使用了.Group()功能。通过将每个组ID设置为内部userID,可以使用.Group(userID)发送消息,而无需在SignalR外部单独维护userID/connectionID关系列表。
可能有一种方法可以在SignalR中维护关系,而不使用.Group()功能,但我还没有找到它。
使用隐藏类型将userID传递给视图,然后使其在js中可用。
 <input type="hidden" value="@ViewBag.UserID" id="userID" />

然后在js hub脚本中使用以下代码,在hub连接启动时将userID发送到hub。

$.connection.hub.start()
  .done(function () {
    var userID = document.getElementById('userID').value;
    $.connection.myHub.server.announce(userID);
   })
  .fail(function () { alert("Hub failed to start.") });

该中心点有一个语句,将userID和connectionID与groupID关联起来,然后groupID与userID相同。
 public class MyHub : Hub
   {
       public void Announce(string userID)
       {
           Groups.Add(Context.ConnectionId, userID);
       }
   }

在设置了hub上下文后,要从控制器发送消息(注意,这里不是从hub发送消息,而是在控制器中向客户端报告长时间处理请求的进度),需要使用.Group()和内部userID。

var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
string fileMessage = "Some message";
hubContext.Clients.Group(userID).hubMessage(fileMessage);

使用js将消息放置在div中,然后在视图中显示。
$.connection.myHub.client.hubMessage = function (message) {
   $("#hubMessages").html(message);
}

'#hubMessages'是视图中的一个div。示例使用.append,每次发送消息都会使该div增长,.HTML将div中的任何内容替换为新消息。

 <div id="hubMessages"></div>

任何想要学习MVC和SignalR的人,如果你正在阅读这篇答案,请注意:Caleb制作了一系列介绍SignalR的视频教程https://youtu.be/kr8uHeNjOKw。对于初学者来说,我建议您花一个小时观看这些视频。请保留HTML标签。

+1 个赞,但是...仅创建一个用户组是一种变通方法,而不是真正的解决方案。我无法相信 Clients.User(srUserId) 不起作用... - Matthieu Charbonnier
好的,我终于找到了答案。请看我的回答。(希望它也能帮到你;) - Matthieu Charbonnier
你说得对,我应该使用“解决方法” - 我已经修改了答案。而且在找到真正的答案方面做得很好。 .Group() 解决方法的优雅之处在于它使 SignalR 管理内部 userID/ConnectionID 关系,这意味着 userID 可以直接用于消息请求中,而无需单独维护关系。作为测试解决方法,我已将 connectionID 添加到用户 db 表中。我不想在实时 db 中这样做。 - Brian.S
好的,我明白了。这很不错。当用户有多个连接时,它会将这些连接添加到同一组中。 - Matthieu Charbonnier
我已经使用这个链接 https://forums.asp.net/t/1985531.aspx?how+to+encrypt+and+decrypt+password+in+mvc+4 实现了userID的加密。花了我两天时间才找到!但是通过使用提供的函数,我在5分钟内就搞定了。我会在其他地方使用这些函数,这是一个不错的发现。 - Brian.S
显示剩余4条评论

0

我遇到了同样的问题。

我改成了:

Clients.User(connectionId).SendAsync(CallbackDefinition.DirectMessage, directMessageResult);

到:

Clients.Client(connectionId).SendAsync(CallbackDefinition.DirectMessage, directMessageResult);

它可以工作:D

感谢:Matthieu Charbonnier


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接