Как получить идентификатор соединения клиента signalR на стороне сервера?
Мне нужно получить идентификатор соединения клиента. Я знаю, что вы можете получить его со стороны клиента, используя $.connection.hub.id
. Мне нужно войти в веб-службу, которая обновляет записи в базе данных, в свою очередь отображая обновление на веб-странице. Я новичок в signalR и stackoverflow, поэтому любые советы будут оценены. На моей клиентской веб-странице у меня есть следующее:
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var notify = $.connection.notificationHub;
// Create a function that the hub can call to broadcast messages.
notify.client.broadcastMessage = function (message) {
var encodedMsg = $('<div />').text(message).html();// Html encode display message.
$('#notificationMessageDisplay').append(encodedMsg);// Add the message to the page.
};//end broadcastMessage
// Start the connection.
$.connection.hub.start().done(function () {
$('#btnUpdate').click(function () {
//call showNotification method on hub
notify.server.showNotification($.connection.hub.id, "TEST status");
});
});
});//End Main function
</script>
все работает до тех пор, пока я не хочу обновлять страницу с помощью signalR. Функция show show в моем концентраторе такова:
//hub function
public void showNotification(string connectionId, string newStatus){
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<notificationHub>();
string connection = "Your connection ID is : " + connectionId;//display for testing
string statusUpdate = "The current status of your request is: " + newStatus;//to be displayed
//for testing, you can display the connectionId in the broadcast message
context.Clients.Client(connectionId).broadcastMessage(connection + " " + statusUpdate);
}//end show notification
как я могу отправить connectionid на мой веб-сервис?
Надеюсь, я не пытаюсь сделать что-то невозможное. Спасибо заранее.
Ответы
Ответ 1
Ответ тейлора работает, однако он не учитывает ситуацию, когда у пользователя открыты несколько вкладок веб-браузера, и поэтому у него несколько идентификаторов подключения.
Чтобы исправить это, я создал Concurrent Dictionary, где ключ словаря - это имя пользователя, а значение для каждого ключа - список текущих соединений для данного пользователя.
public static ConcurrentDictionary<string, List<string>> MyUsers = new ConcurrentDictionary<string, List<string>>();
При подключении - добавление соединения в словарь глобального кэша:
public override Task OnConnected()
{
Trace.TraceInformation("MapHub started. ID: {0}", Context.ConnectionId);
var userName = "testUserName1"; // or get it from Context.User.Identity.Name;
// Try to get a List of existing user connections from the cache
List<string> existingUserConnectionIds;
ConnectedUsers.TryGetValue(userName, out existingUserConnectionIds);
// happens on the very first connection from the user
if(existingUserConnectionIds == null)
{
existingUserConnectionIds = new List<string>();
}
// First add to a List of existing user connections (i.e. multiple web browser tabs)
existingUserConnectionIds.Add(Context.ConnectionId);
// Add to the global dictionary of connected users
ConnectedUsers.TryAdd(userName, existingUserConnectionIds);
return base.OnConnected();
}
При отключении (закрытии вкладки) - удаление подключения из словаря глобального кэша:
public override Task OnDisconnected(bool stopCalled)
{
var userName = Context.User.Identity.Name;
List<string> existingUserConnectionIds;
ConnectedUsers.TryGetValue(userName, out existingUserConnectionIds);
// remove the connection id from the List
existingUserConnectionIds.Remove(Context.ConnectionId);
// If there are no connection ids in the List, delete the user from the global cache (ConnectedUsers).
if(existingUserConnectionIds.Count == 0)
{
// if there are no connections for the user,
// just delete the userName key from the ConnectedUsers concurent dictionary
List<string> garbage; // to be collected by the Garbage Collector
ConnectedUsers.TryRemove(userName, out garbage);
}
return base.OnDisconnected(stopCalled);
}
Ответ 2
Когда клиент вызывает функцию на стороне сервера, вы можете получить идентификатор своего подключения через Context.ConnectionId
. Теперь, если вы хотите получить доступ к этому идентификатору соединения через механизм вне концентратора, вы можете:
- Просто позвольте Hub вызывать ваш внешний метод, проходящий в идентификаторе соединения.
- Управляйте списком подключенных клиентов, например,
public static ConcurrentDictionary<string, MyUserType>...
, добавив в словарь OnConnected
и удалив его из OnDisconnected
. После того, как у вас есть список пользователей, вы можете запросить его через внешний механизм.
Пример 1:
public class MyHub : Hub
{
public void AHubMethod(string message)
{
MyExternalSingleton.InvokeAMethod(Context.ConnectionId); // Send the current clients connection id to your external service
}
}
Пример 2:
public class MyHub : Hub
{
public static ConcurrentDictionary<string, MyUserType> MyUsers = new ConcurrentDictionary<string, MyUserType>();
public override Task OnConnected()
{
MyUsers.TryAdd(Context.ConnectionId, new MyUserType() { ConnectionId = Context.ConnectionId });
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
MyUserType garbage;
MyUsers.TryRemove(Context.ConnectionId, out garbage);
return base.OnDisconnected(stopCalled);
}
public void PushData(){
//Values is copy-on-read but Clients.Clients expects IList, hence ToList()
Clients.Clients(MyUsers.Keys.ToList()).ClientBoundEvent(data);
}
}
public class MyUserType
{
public string ConnectionId { get; set; }
// Can have whatever you want here
}
// Your external procedure then has access to all users via MyHub.MyUsers
Надеюсь, это поможет!
Ответ 3
Я прошу разницы в повторном подключении. Клиент остается в списке, но подключится. Я делаю обновление статического списка для повторного подключения, чтобы решить эту проблему.