Отображение подключений 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);
    }
}
Другие вопросы по тегам