在SignalR中向特定用户发送消息

10

我有一个SignalR服务器(控制台应用程序)和一个客户端应用程序(Asp.net MVC5)

在OAuth Membership中,我如何向特定用户发送消息。

实际上,我无法从Hub请求上下文解析发送者用户。

Context.User.Identity.Name

我的中心

public class UserHub : Hub
{

    #region Hub Methods
    public void LoggedIn(string userName, string uniqueId, string ip)
    {
        Clients.All.userLoggedIn(userName, uniqueId, ip);
    }
    public void LoggedOut(string userName, string uniqueId, string ip)
    {
        var t = ClaimsPrincipal.Current.Identity.Name;
        Clients.All.userLoggedOut(userName, uniqueId, ip);
    }
    public void SendMessage(string sendFromId, string userId, string sendFromName, string userName, string message)
    {
        Clients.User(userName).sendMessage(sendFromId, userId, sendFromName, userName, message);
    }
    #endregion
}

启动中心类(Program.cs)

class Program
{
    static void Main(string[] args)
    {
        string url = string.Format("http://localhost:{0}", ConfigurationManager.AppSettings["SignalRServerPort"]);
        using (WebApp.Start(url))
        {
            Console.WriteLine("Server running on {0}", url);
            Console.ReadLine();
        }
    }
}

即使您已经接受了下面的答案,我建议您查看这个链接:https://dev59.com/y2Ik5IYBdhLWcg3wKLWd#21355406 - Wasp
4个回答

30

通过创建一个类来保持connectionIduserName的关联,因为我们知道Signalr只有每个已连接对等体的connectionId信息。

创建一个名为UserConnection的类。

Class UserConnection{
  public string UserName {set;get;}
  public string ConnectionID {set;get;}
}

声明一个列表

List<UserConnection> uList=new List<UserConnection>();

在客户端连接时将用户名作为查询字符串传递

$.connection.hub.qs = { 'username' : 'anik' };
将具有连接到此列表的用户推送到已连接方法。
public override Task OnConnected()
{
    var us=new UserConnection();
    us.UserName = Context.QueryString['username'];
    us.ConnectionID =Context.ConnectionId;
    uList.Add(us);
    return base.OnConnected();
}
从发送消息中搜索用户名称, 从列表中检索用户连接ID, 然后发送。
var user = uList.Where(o=>o.UserName ==userName);
if(user.Any()){
   Clients.Client(user.First().ConnectionID ).sendMessage(sendFromId, userId, sendFromName, userName, message);
}

演示


2
这可以通过实现自定义的IUserIdProvider实现更加简单地完成,您可以在其中处理您的查询字符串,然后使用Clients.User方法。无需维护任何连接ID的单独列表。 - Wasp
对于 .Net 客户端,可以使用 var connection = new HubConnection(url, queryString)。例如:var connection = new HubConnection("http://localhost:8080/","name=abc") - ibubi
这个 sendMessage 方法能否是动态的?有没有办法将该方法名称作为 string 传递,类似于我们使用 Invoke 方法的方式? - Sreekumar P
1
我这里有两个问题。1.) 假设用户打开了一个新标签页,那么就会有两个不同的连接ID。因此,在这种情况下,我们将为单个用户拥有两个连接ID,并且必须向两个连接发送消息。对吗?2.) 维护列表是一个好习惯。假设我们有成千上万的用户,那么这个列表将非常大。是否有其他优化的方法来实现相同的结果? - Ankush Jain
1
@AnkushJain 1) 在将用户分配到列表之前,根据他/她的用户ID检查用户的连接是否存在。如果用户存在,则可以更新连接ID或丢弃新的连接ID,这取决于您。2) 如果列表很大,则使用查询速度更快的小型数据库。这完全基于您的系统要求。 - Anik Islam Abhi

11

所有这些答案都过于复杂。我只是覆盖了"OnConnected()"方法,获取唯一的Context.ConnectionId,并立即将其广播回客户端javascript以便客户端存储并在随后的调用中与hub服务器一起发送。

public class MyHub : Hub
{
    public override Task OnConnected()
    {
        signalConnectionId(this.Context.ConnectionId);
        return base.OnConnected();
    }

    private void signalConnectionId(string signalConnectionId)
    {
        Clients.Client(signalConnectionId).signalConnectionId(signalConnectionId);
    }
}
在 JavaScript 中:
$(document).ready(function () {

    // Reference the auto-generated proxy for the SignalR hub. 
    var myHub = $.connection.myHub;

    // The callback function returning the connection id from the hub
    myHub.client.signalConnectionId = function (data) {
        signalConnectionId = data;
    }

    // Start the connection.
    $.connection.hub.start().done(function () {
        // load event definitions here for sending to the hub
    });

});

1
我认为你的解决方案存在问题。如果用户在开始某个操作之前离开页面,那怎么办?新的连接 ID 不同,因此不会被发送给该用户,对吧?因此,保存连接 ID 和用户 ID 之间的关系是有意义的。这样,即使出现这种情况,你也能收到消息,因为你使用了不变的用户 ID 作为参考。 - eestein

1

1
在ChatHub类中,针对特定用户使用以下内容。
public Task SendMessageToGroup(string groupName, string message)
    {

        return Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId}: {message}");
    }

    public async Task AddToGroup(string groupName)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, groupName);

        await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has joined the group {groupName}.");
    }

    public async Task RemoveFromGroup(string groupName)
    {
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);

        await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has left the group {groupName}.");
    }

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