What is the event when the user leaves the site in signalr? - asp.net

What the user is leaving the event site in order to change their status from online to offline and send a message to clients states that he left the site.

You can tie into the disconnected event.
SO in your hub add the method:
public override Task OnDisconnected()
{
// The Context.ConnectionId is the identity of the disconnected client
return Clients.All.userOffline(/* Something here to identify the user */);
}

Related

Signalr Personal Chat

I am still new to signalr. I want to make chat application with signalR and having a problem with the personal chat and groups because the connection id always changes every time the user and reconnect.
Question : How we know which id user is reference the specific user. if the connection id always change.
(because if the user open another tab in their browser the signalR will recognize them as another user. How do we know that the id is the same person)
Is there any unique property from signalR that we can save in database so when the user login when can recognize the user ?
You can use UserNames of the logged in users. Depending on the technology you use, you can utilize different methods. The way I do it in MVC is like the following:
Hub:
public class ChatHub :Hub
{
public void SendMessage(string sender,string receiver, string msg)
{
MessageService messageService = new MessageService();
long msgId = messageService.Add(sender, receiver, msg);
Clients.User(receiver).receiveMessage(sender, msg, msgId);
Clients.User(sender).receiveMessage(sender, msg, msgId);
}
}
View:
$('#send').click(function () {
hub.invoke("SendMessage", "#User.Identity.Name", "#ViewBag.Receiver", $('#message').val());
});
The sender UserName can be obtained by #User.Identity.Name (in MVC) and the receiver UserName can be read when the sender is opening the receiver chat.
Update:
Regarding Asp.Net Web Forms, check if the following helps:
SignalR-with-ASP-NET-One-to-one-and-Group-Chat
SignalR is doing what it should do with each connect/reconnect. Your user is establishing a new connection, so they get a new connection ID.
If they open in a new tab that will create a new connection. You have to work out users being currently connected and how you want to handle.
If you take a look at the docs you can see they have examples for mapping users to connections.

send a message to a user who has been disconnected for a while in signalR

Consider a situation when Implementing a messaging with SignalR, which a users disconnects for a while, and you want to send her again the message she missed.
Here is my simple hub :
[HubName("ContosoChatHub")]
public class ContosoChatHub : Hub
{
}
And I broadcast the message:
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ContosoChatHub>();
hubContext.Clients.All.AddCallForAgent(new { });
First thing that comes to my mind is adding this to your hub:
private static ConcurrentDictionary<string, Queue<Message>> UnconfirmedMessages;
The string type would map the user id (user should be authenticated).
Whenever you send a message to a user you would add it to its queue
as well.
Then the user/client would send a confirmation to the hub
with the received message ids (i.e: hey, I got messages 1, 2, 3 and
4). At that time you would remove those messages from the queue.
When the client reconnects, it would ask the hub for all unconfirmed messages. That would take you to step 2 again.

SignalR not firing "disconnected" event

I'm using SignalR to push updates out to connected web clients. I listen to the disconnected event in order to know when I should start my reconnection logic
$.connection.hub.disconnected(function() {
// Initiate my own reconnection logic
});
The SignalR hub is hosted in in IIS (along with my site)
[assembly: OwinStartup(typeof(Startup))]
namespace MyNamespace.SignalR
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
Upon connection, the client calls a server method to join a group
public class MyHub : Hub
{
public void JoinGroup(string groupName)
{
Groups.Add(Context.ConnectionId, groupName);
}
}
And then I push messages to this group:
context.Clients.Group(groupName).sendMessage();
If I manually recycle the application pool in IIS, SignalR starts trying to reconnect and I eventually receive a disconnected event on the client side if it fails (after the timeout).
However, my problem is that if I manually restart the website in IIS, I do not receive any disconnected event at all, and I can't see in the logs that SignalR has detected any connection problem at all. How can I detect that I have been disconnected?
I know I should probably persist the group connections somehow, since that is saved in memory I guess. But that shouldn't affect the initial problem that the client receives no notification of the disconnection? Shouldn't the client side signalr code throw some kind of exception/event?
disconnected fires first when the built in logic for reconnection have timed out. You also need to listen to the recconect event, something like i did here
https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/blob/ReconnectOnClosed/SignalR.EventAggregatorProxy.Client.JS/jquery.signalR.eventAggregator.js#L157
So I finally found out what the problem is and how to solve it. Some background though:
At the moment we manually release new versions of our application by going to "Basic settings..." under the website in IIS and changing the "Physical Path" from C:\websites\version1 to C:\websites\version2. Apparently this gives the same behavior as doing a restart of the website in IIS (not a hard reset, not stopping the website, not recycling the app pool) and according to this: "does NOT shut the site down, it merely removes the Http.sys binding for that port". And no matter how long we wait, the connected clients never receive any kind of indication that they should reconnect.
So the solution is to recycle the application pool after each release. The "lost" clients will receive disconnected events and reconnect to the new version of the site.

Asp.net session never expires when using SignalR and transport mode long polling

We have a web application that uses SignalR for its notification mechanism.The problem is when we are browsing our web application using IE ,SignalR uses Long Polling as its transport type thus sends back requests to our web server therefore Session never expires no matter how long the browser is idle.
We were thinking that maybe we could catch the requests in Global.asax and see if they were from SingalR and set the session timeout to the remaining time (Which I don't think it's a straightforward solution).
Is there any other solution the we are missing ?
The workaround I am currently using is an IHttpModule to check if the request is a Signalr request, if so remove the authentication cookie, this will prevent the ASP.net session timeout from being reset, so if your Session Timeout is 20min and the only requests are Signalr the users session will still timeout and the user will have to login again.
public class SignalRCookieBypassModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.PreSendRequestHeaders += OnPreSendRequestHeaders;
}
private bool IsSignalrRequest(string path)
{
return path.IndexOf("/signalr/", StringComparison.OrdinalIgnoreCase) > -1;
}
protected void OnPreSendRequestHeaders(object sender, EventArgs e)
{
var httpContext = ((HttpApplication)sender).Context;
if (IsSignalrRequest(httpContext.Request.Path))
{
// Remove auth cooke to avoid sliding expiration renew
httpContext.Response.Cookies.Remove(DefaultAuthenticationTypes.ApplicationCookie);
}
}
public void Dispose()
{
}
}
I feel this is a real hack solution so would love so other ideas to prevent session timeout renew when data is pushed to the client from the server, or a when javascript client polls an endpoint for data.
If you take a look at the description of the SignalR protocol I wrote a while ago you will find this:
» ping – pings the server
...
Remarks: The ping request is not really a “connection management request”. The sole purpose of this request is to keep the ASP.NET session alive. It is only sent by the the JavaScript client.
So, I guess the ping request is doing its job.
I here post #Simon Mourier's commented solution, with his approval, as a CW answer, as I find the suggested approach the most appropriate and less intrusive, as it just disables the Session for SignalR requests.
A positive side effect is that the request will be processed faster as the Session object doesn't need to be initiated and loaded.
It still uses a IHttpModule for the work, and the preferable place is likely the AcquireRequestState event (not personally tested yet though), or at an event raised earlier, before making use of the Session object.
Do note using this approach that one might need to test that the Session object is available before access any of its members or stored objects.
public class SignalRSessionBypassModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.AcquireRequestState += OnAcquireRequestState;
}
private bool IsSignalrRequest(string path)
{
return path.IndexOf("/signalr/", StringComparison.OrdinalIgnoreCase) > -1;
}
protected void AcquireRequestState(object sender, EventArgs e)
{
var httpContext = ((HttpApplication)sender).Context;
if (IsSignalrRequest(httpContext.Request.Path))
{
// Run request with Session disabled
httpContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
}
}
public void Dispose()
{
}
}
Here is another completely different approach, simple, yet quite efficient.
Instead of relying on Session/Auth cookies to decide whether a user has timed out, use the Cache object. This have more or less no side effects and work just like if the user simply logged out.
By simply add this small snippet somewhere in the beginning of your web app code, where of course SignalR don't go, you will be able to check if the cache item is there and reinitiate it (with the same expiration time as the Session timeout is set), and if not, just do a logout and remove cookies/session variables.
if (Request.IsAuthenticated) {
if (Cache[Context.User.Identity.Name] == null) {
// Call you logout method here...,
// or just:
// - Sign out from auth;
// - Delete auth cookie
// - Remove all session vars
} else {
// Reinitiate the cache item
Cache.Insert(Context.User.Identity.Name,
"a to you usable value",
null,
DateTime.Now.AddMinutes(Session.Timeout),
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
null
);
}
And within your user login method, you just add this, to create the cache item for the first time
// Insert the cache item
Cache.Insert(Context.User.Identity.Name,
"a to you usable value",
null,
DateTime.Now.AddMinutes(Session.Timeout),
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
null
);
It's more stable and maintainable -in my view- to have your own "session like timeout" . Set your .NET session timeout to infinity since you'll not be using it and then create a global JavaScript counter (in your layout or master page) to track the time passing while the browser is idle (obviously setTimeout or setInterval every few seconds would do the trick). Make sure to have the counter reset on every web request (that should happen automatically since all JavaScript variables would reset). In case you have pages that depend on web services or Web API, make sure to reset your global JavaScript counter on every call. If the counter reaches your desired timeout without being reset, that means that the session is expired and you can logout the user. With this approach you'll have full control over the session lifetime which enables you to create a logout timer popup to warn the user that the session is about to expire. SignalR would perfectly fit with this approach since the JavaScript timer would keep ticking.

Does SignalR send response to all the connections including the current connection, or all but excluding the current?

Let's say for example I've a button that displays alert to all the users. When I click the button, all the connected clients will see the alert.
And let's say this is done by calling a Server Method from client, which calls a function on Client that show an alert.
For example:
public class DemoHub : Hub {
public void ShowAlert(String message) {
Clients.All.showAlertMessage(message);
}
}
This is a server method in hub, that calls the client function to show alert message.
My question is, does the alert will be shown to the current client too? I mean, if I click the button, do I see the alert myself in this case?
Note: the above scenario is not a real scenario, that is just a similar illustration of my issue as my current scenario is rather complex to explain.
To broadcast to ALL connections including the caller you would use
Clients.All.showAlertMessage(message);
To broadcast to ALL EXCEPT the caller you will want to use the method
Clients.Others.showAlertMessage(message);
Can find more documentation on this at http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#callfromhub
This method above will broadcast to all connections except the current one which I believe is what you are looking for

Resources