考虑一个像 facebook 这样的 Web 应用程序,它可以向用户发送实时通知。
使用asp.net SignalR跟踪哪些连接 ID 属于哪个用户,即使用户断开连接或稍后重新连接,最好的方法是什么?
考虑一个像 facebook 这样的 Web 应用程序,它可以向用户发送实时通知。
使用asp.net SignalR跟踪哪些连接 ID 属于哪个用户,即使用户断开连接或稍后重新连接,最好的方法是什么?
查看以下博客文章:
将 ASP.NET SignalR 连接映射到实际应用程序用户
简而言之,您将在该方法上向用户添加连接 ID,并在该OnConnected
方法上删除该连接OnDisconnected
。请记住,一个应用程序用户可以有多个连接。因此,您需要在用户和连接 ID 之间建立一对多的关系。上面链接的帖子通过示例对其进行了详细说明。
我为一个内部应用程序做了这个。我这样做的方式是,当用户连接时,我让服务器要求用户自己注册。通过这种方式,我知道不仅连接了用户和他们的 signalR connnectionID,而且他们还可以告诉我任何其他信息(如用户名或其他信息)。
当他们重新连接时,我要求他们再做一次。
SignalR 将保持每个客户端相同的连接 ID,即使他们重新连接也很好。重新连接与初始连接不同。新连接表示新客户端,但重新连接在同一客户端上。
在我的应用程序中,我维护了一个单独的线程安全字典,用于跟踪哪个用户和哪个 connectionID 在做什么。这样我可以说“哦,向用户 ABC 发送消息”并查找他们的 connectionID。然后在 signalR 中针对该 connectionID 对集线器的客户端对象进行操作。如果您这样做,您甚至可以在多个连接中拥有相同的“用户”。想象一下用户“abc”在两个浏览器选项卡中打开。如果您严格按照 connectionID 进行操作,从技术上讲,他们将是两个不同的用户。但是,通过维护某种本地集合对用户和连接进行分组,您现在可以为同一用户拥有多个连接。
我应该提一下,如果你这样做,你应该确保你的站点处理当它重新启动并丢失所有连接信息时发生的事情。对我来说,当有人重新连接时,我会要求他们再次重新识别自己。这样我就可以在服务器上线时重新构建我的本地字典而无需担心。它确实有更多的开销,因为现在您要求所有客户向您发送信息,但根据您的用户情况,这可能是交错的或成束的或以其他方式分布以帮助您处理负载。
通常,但是您获得本地信息(无论是通过要求用户提供它)还是通过 http 上下文会话信息,您都需要自己跟踪它。
好吧,我使用了另一种方法,我像这样扩展了 ApplicationUser 类:
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
//public int ApplicationUserId { get; set; }
//public string Name { get; set; }
//public string Address { get; set; }
//public string City { get; set; }
//public string State { get; set; }
//public string Zip { get; set; }
[Required]
public string Email { get; set; }
[Required]
public override string UserName { get; set; }
[NotMapped]
public string ConnectionId { get; set; }
[NotMapped]
public string ChattingUserConnectionId { get; set; }
//public string HomeTown { get; set; }
//public DateTime? BirthDate { get; set; }
}
在我的集线器中,我正在做类似的事情:
public class ChatHub : Hub
{
#region Data Members
private static ApplicationDbContext applicationDbContext = new ApplicationDbContext();
private static UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(applicationDbContext));
private static List<ApplicationUser> connectedUsers = new List<ApplicationUser>();
当用户连接到聊天时,我通过他的用户名获取他的 ApplicationUser 对象并将其添加到 connectedUsers 列表中。当他断开连接时,我将他删除。
我遇到了一些带有 EF 状态的随机异常,这使我每次访问它时都创建 ApplicationDbContext 和 UserManager,而不是将其设置在静态对象中:
private ApplicationUser GetCurrentUser()
{
ApplicationDbContext applicationDbContext = new ApplicationDbContext();
UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(applicationDbContext));
var userName = Context.User.Identity.GetUserName();
var user = userManager.FindByName<ApplicationUser>(userName);
return user;
}
编辑:
集线器代码在加载用户的子对象时存在一些问题。这个也用在 asp.net 模板中的代码会更好地工作,不需要 ApplicationDBContext:
private ApplicationUserManager _userManager
{
get
{
return HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
}
var user = _userManager.FindByName<ApplicationUser, string>(userName);
ASP.NET 的 SignalR 教程部分有一篇非常好的文章。我已经包含了下面的介绍性段落。
将 SignalR 用户映射到连接
每个连接到集线器的客户端都会传递一个唯一的连接 ID。您可以在集线器上下文的 Context.ConnectionId 属性中检索此值。如果您的应用程序需要将用户映射到连接 ID 并保留该映射,您可以使用以下方法之一:
- 用户 ID 提供者 (SignalR 2)
- 内存存储,例如字典
- 每个用户的 SignalR 组
永久的外部存储,例如数据库表或 Azure 表存储本主题中显示了这些实现中的每一个。您使用 Hub 类的 OnConnected、OnDisconnected 和 OnReconnected 方法来跟踪用户连接状态。
用户 ID 提供者
如果您使用 ASP.NET 的标准成员资格提供程序 ( IPrincipal.Identity.Name
),您只需执行以下操作即可基于用户帐户访问客户端。如果您有自己的用户系统,则可以创建自己的提供者。
public class MyHub : Hub
{
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
}