Отображение подключений SignalR к пользователям
Рассмотрим веб-приложение, такое как Facebook, которое может отправлять пользователям уведомления в реальном времени.
Как лучше всего использовать asp.net SignalR для отслеживания того, какие идентификаторы соединений принадлежат тому или иному пользователю, даже если пользователь отключается или подключается позже?
4 ответа
Проверьте следующее сообщение в блоге:
Отображение соединений ASP.NET SignalR с реальными пользователями приложений
Вкратце, вы добавляете идентификаторы подключения к пользователю на OnConnected
метод и удалить это соединение на OnDisconnected
метод. Имейте в виду, что пользователь приложения может иметь несколько подключений. Таким образом, вы должны иметь отношения один ко многим между вашим пользователем и идентификаторами соединений. Приведенный выше пост подробно объясняет это с примером.
Я сделал это для внутреннего приложения. Я сделал так, что когда пользователь подключается, у меня есть сервер, который просит пользователя зарегистрироваться самостоятельно. Таким образом, я знаю, что не только пользователь подключен и его signalR connnectionID, но они также могут сообщить мне любую другую информацию (например, имя пользователя или что-то еще).
Когда они воссоединятся, я прошу их сделать это снова.
SignalR будет поддерживать один и тот же connectionID для каждого клиента, даже если он переподключится, что приятно. Пересоединение не совпадает с первоначальным соединением. Новые соединения указывают на нового клиента, но переподключение происходит на том же клиенте.
В моем приложении я поддерживал отдельный словарь для многопоточной безопасности, в котором я отслеживал, какой пользователь и какой идентификатор соединения делал. Таким образом, я могу сказать: "О, отправить сообщение пользователю ABC" и посмотреть его connectionID. Затем действуйте на объекте клиентов Hub в 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 по его имени пользователя и добавляю его в список подключенных пользователей. Когда он отключается, я удаляю его.
Я столкнулся с некоторыми случайными исключениями с состояниями 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 на соединения
Каждый клиент, подключающийся к концентратору, получает уникальный идентификатор подключения. Вы можете получить это значение в свойстве Context.ConnectionId контекста концентратора. Если вашему приложению необходимо сопоставить пользователя с идентификатором соединения и сохранить это сопоставление, вы можете использовать один из следующих способов:
- Поставщик идентификатора пользователя (SignalR 2)
- Хранение в памяти, например, словарь
- Группа SignalR для каждого пользователя
Постоянное внешнее хранилище, такое как таблица базы данных или хранилище таблиц Azure. Каждая из этих реализаций показана в этом разделе. Вы используете методы OnConnected, OnDisconnected и OnReconnected класса Hub для отслеживания состояния подключения пользователя.
UserID Provider
Если вы используете стандартный поставщик членства ASP.NET (IPrincipal.Identity.Name
) вы можете просто сделать следующее, чтобы получить доступ к клиенту на основе учетной записи пользователя. Если у вас есть собственная система пользователя, вы можете создать своего собственного провайдера.
public class MyHub : Hub
{
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
}