$(function () {
//определяем наш хаб
var hub = $.connection.notificationHub;
//метод хаба на клиенте, который будет вызван у всех подключенных клиентов, когда ему будет дана команда с сервера
hub.client.sendNotification = function (message) {
//выводим сообщение подключенным клиентам(в моем случае это будут только админы)
$("#hubMessageConteiner")
.append("<li>" + message + "<i title='Закрыть' class='fa fa-close fa-lg cursor-pointer'></i></li>")
.find("li").show(200);
};
//создаем подключение к хабу, это метод start, если пользователь смог подключится к хабу, то вызывается функция done, если нет то будет вызываться функция fail
$.connection.hub.start().done(function () {
$('#Hub').click(function () {
hub.server.send("Это push уведомление");
});
});
});
public class NotificationHub:Hub
{
//хранилище подключений(тут я храню только администраторов)
private readonly static ConnectionMapping<string> _connections = new ConnectionMapping<string>();
//сейчас я вызываю этот метод с клиента, но его легко можно и вызвать на сервере,
//допустим в каком-нибудь методе контроллера, что я в последствии и собираюсь сделать,
//потому что мне надо, чтобы при добавлении новости уведомления получал только администратор или модератор
public void Send(string message)
{
var user = Context.User;
//получаем всех подключенных админов и каждому отправляем уведомление
foreach (var connectionId in _connections.GetConnections())
{
//если это сам админ то не надо отправлять
if (!user.IsAdmin())
{
//тут мы вызываем метод на клиенте, который и отобразит наше уведомление
Clients.Client(connectionId).sendNotification(message);
}
}
}
public override Task OnConnected()
{
//Хаб имеет доступ к контексту + я использую Identity (ссылку дал выше читайте), следовательно могу получить доступ к информации о подключенном пользователе:
var user = Context.User;
if (user.IsAdmin())
{
string userId = Context.User.GetUserId();
//запихиваем нашего подключенного пользователя если он админ в хранилище подключений, кстати каждому подключившемуся выдается ConnectionId, который можно получить из контекста и да он уникален и выдается каждый раз новый при подключении
_connections.Add(userId, Context.ConnectionId);
}
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
var user = Context.User;
if (user.IsAdmin())
{
string userId = user.GetUserId();
//если пользователь(админ) отключился, то убираем его из хранилища
_connections.Remove(userId, Context.ConnectionId);
}
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
var user = Context.User;
if (user.IsAdmin())
{
string userId = user.GetUserId();
if (!_connections.GetConnections(userId).Contains(Context.ConnectionId))
{
_connections.Add(userId, Context.ConnectionId);
}
}
return base.OnReconnected();
}
}
//на всякий случай добавлю класс хранилища подключений, но он такой же как в документации
public class ConnectionMapping<T>
{
private readonly Dictionary<T, HashSet<string>> _connections = new Dictionary<T, HashSet<string>>();
public int Count
{
get
{
return _connections.Count;
}
}
public void Add(T key, string connectionId)
{
lock (_connections)
{
HashSet<string> connections;
if (!_connections.TryGetValue(key, out connections))
{
connections = new HashSet<string>();
_connections.Add(key, connections);
}
lock (connections)
{
connections.Add(connectionId);
}
}
}
public IEnumerable<string> GetConnections(T key)
{
HashSet<string> connections;
if (_connections.TryGetValue(key, out connections))
{
return connections;
}
return Enumerable.Empty<string>();
}
//добавил только вот этот метод для получения всех подключивщихся администраторов
public IEnumerable<string> GetConnections()
{
HashSet<string> connections = new HashSet<string>();
if (_connections.Any())
{
foreach (var connection in _connections)
{
foreach (var connectionId in connection.Value)
{
connections.Add(connectionId);
}
}
}
return connections;
}
public void Remove(T key, string connectionId)
{
lock (_connections)
{
HashSet<string> connections;
if (!_connections.TryGetValue(key, out connections))
{
return;
}
lock (connections)
{
connections.Remove(connectionId);
if (connections.Count == 0)
{
_connections.Remove(key);
}
}
}
}
}