I have application level variable stored in a cache in global.asax file:
void Application_Start(object sender, EventArgs e)
{
Application.Lock();
List<MyClassName> myobjects = new List<MyClassName>();
HttpContext.Current.Cache["List"] = myobjects;
Application.UnLock();
}
And I access it and change it in a page:
protected void Page_Load(object sender, EventArgs e)
{
List<MyClassName> myobjects = (List<MyClassname>)HttpContext.Current.Cache["List"];
//change myobjects...
HttpContext.Current.Cache["List"] = myobjects;
}
Question is:
*Suppose we have two users(browsers) running this site at the same time (for example they are chatting). How to notify another user when one user changes myobjects variable?*
We could check myobjects and compare this with old one periodically using timer. But it is not the best way.
So is there better way to notify another user when global variable is changed?
For a webserver,considering the statelessness of http protocol, there is nothing like user or connected user, just a request and its response. When you say, you want to notify a user, you are trying to say that server will initiate the communication which is not possible.
An idea could be, you could use long-polling, i.e. keep the request continuously alive and whenever you want to notify something to user, you feed in the data but again it is not a scalable solution - at least for low resource servers (of course google can afford it)
In order to notify anything, there user must be connected to the server - i.e. the communication must be initiated from client end.
Another alternative is, using sockets. You could write a custom server and make your web application notify the custom server that the value has changed. The custom server in turn will notify the clients using sockets (Web sockets). This is rather complicated mechanism though. You could use websockets provided by html 5 or use addons like flash/java to work out the socket communication.
Related
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.
I'm trying to count the amount of online users.
This is the code:
protected void Application_Start()
{
...
Application["OnlineUsers"] = 0;
}
private void Session_Start(object sender, EventArgs e)
{
Application.Lock();
Session["O"] = "OO"; // Need to have something in the session
Application["OnlineUsers"] = (int)Application["OnlineUsers"] + 1;
Application.UnLock();
}
private void Session_End(object sender, EventArgs e)
{
Application.Lock();
Application["OnlineUsers"] = (int)Application["OnlineUsers"] - 1;
Application.UnLock();
}
There are <b>#Context.ApplicationInstance.Application["OnlineUsers"].ToString()</b> users online
It kind of works, but I always have the value that is greater than the actual amount of users online.
And even worse: in Opera refreshing the page N times increases the amount of online users by N!
It is important to note here that the ASP.NET is trying to be extremely efficient storing sessions for users. If ASP.NET doesn’t have a reason to remember who you are, it won’t.
When we request a page first time, a session object will be created and its session identifier will be sent to web-browser so browser can store session identifier in cookie (for identiity of request). If that page is again submitted/postedback then the same session identifier from the cooike will be available to the app-server and it assume that this is an old-request. But in your case (you are refereshing a page), it means web-browser issue a fresh request (and of-course the request type will be GET) without sending cookies. So, it is better to redirect the user to a specific page on first request.
The Session_End event handle will be called when Session get timeout (default value is 20 minutes) even after that client (browser) is closed (or ends the session).
I know its not quite what you are asking but you can query the PerformanceCounter on IIS for this info
(Razor Example)
#using System.Diagnostics
#{
var perf = new PerformanceCounter("ASP.NET", "State Server Sessions Active");
}
<h2>About</h2>
<p>
#perf.NextValue()
</p>
I didn't check but your access to this might need a windows/service account in your app pool.
You can also Increment and Decrement you own Performance counters and make them available to system admins via the tools they use to monitor Websites etc.
The SqlMembershipProvider has a facility for counting logged on users, which would mean you probably already have the data sitting in your database if you are using it to manage your forms authentication.
You could also consider having your pages emit an ajax pulse every 'period of time' and count that. or have some applet, silverlight, flash etc doing the same.
I am experimenting with FormsAuthentication (using ASP.NET MVC2) and it is working fairly well.
However, one case I can't work out how to deal with is validating the user identity on the server to ensure it is still valid from the server's perspective.
eg.
User logs in ... gets a cookie/ticket
Out of band the user is deleted on the server side
User makes a new request to the server. HttpContext.User.Identity.Name is set to the deleted user.
I can detect this fine, but what is the correct way to handle it? Calling FormsAuthentication.SignOut in the OnAuthorization on OnActionExecuting events is too late to affect the current request.
Alternatively I would like to be able to calls FormsAuthentication.InvalidateUser(...) when the user is deleted (or database recreated) to invalidate all tickets for a given (or all) users. But I can't find an API to do this.
In the global.asax, add an handler for AuthenticateRequest. In this method, the forms authentication has already taken place and you're free to modify the current principal before anything else happens.
protected void Application_AuthenticateRequest(object sender, EventArgs e) {
IPrincipal principal = HttpContext.Current.User;
if (!UserStillValid(principal)) {
IPrincipal anonymousPrincipal = new GenericPrincipal(new GenericIdentity(String.Empty), null);
Thread.CurrentPrincipal = anonymousPrincipal;
HttpContext.Current.User = anonymousPrincipal;
}
}
Just implement the UserStillValid method and you're done. It's also a good place to swap the generic principal with a custom one if you need to.
I want to save each request to the website. In general I want to include the following information:
User IP, The web site url, user-if-exist, date-time.
Response time, response success-failed status.
Is it reasonable to collect the 1 and 2 in the same action? (like same HttpModule)?
Do you know about any existing structure that I can follow that track every request/response-status to the website?
The data need to be logged to sql server.
Look in your web server logs.
How about IIS logs? they already have all the data items you listed so far
In BeginRequest Method you need to write the following code.
protected void Application_BeginRequest(object sender, EventArgs e)
{
//String s = HttpContext.Current.Request.Path;
//HttpContext.Current.RewritePath("Login.aspx");
String referrer = HttpContext.Current.Request.UrlReferrer;
String sourceIP = HttpContext.Current.Request.UserHostAddress;
String browser = HttpContext.Current.Request.UserAgent;
}
Is there a way to determine the number of users that have active sessions in an ASP.NET application? I have an admin/tools page in a particular application, and I would like to display info regarding all open sessions, such as the number of sessions, and perhaps the requesting machines' addresses, or other credential information for each user.
In global.aspx
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
Application["OnlineUsers"] = 0;
}
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
Application.Lock();
Application["OnlineUsers"] = (int)Application["OnlineUsers"] + 1;
Application.UnLock();
}
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate
// mode is set to InProc in the Web.config file.
// If session mode is set to StateServer or SQLServer,
// the event is not raised.
Application.Lock();
Application["OnlineUsers"] = (int)Application["OnlineUsers"] - 1;
Application.UnLock();
}
Note: The Application.Lock and Application.Unlock methods are used to prevent multiple threads from changing this variable at the same time.
In Web.config
Verify that the SessionState is "InProc" for this to work
<system.web>
<sessionState mode="InProc" cookieless="false" timeout="20" />
</system.web>
In your .aspx file
Visitors online: <%= Application["OnlineUsers"].ToString() %>
Note: Code was originally copied from http://www.aspdotnetfaq.com/Faq/How-to-show-number-of-online-users-visitors-for-ASP-NET-website.aspx (link no longer active)
ASP.NET Performance Counters like State Server Sessions Active (The number of active user sessions) should help you out. Then you can just read and display the performance counters from your admin page..
If you are using .net Membership you could use
Membership.GetNumberOfUsersOnline()
More about it: http://msdn.microsoft.com/en-us/library/system.web.security.membership.getnumberofusersonline.aspx
If you'd like to implement the same mechanism by yourself, you can define like a CurrentUserManager class and implement the singleton pattern here. This singleton object of class CurrentUserManager would be unique in the AppDomain. In this class you will create its self instance once, and you will prohibit the others from creating new instances of this class by hiding its constructor. Whenever a request comes to this object, that single instance will give the response. So, if you implement a list that keeps the records of every user (when a user comes in, you add him to the list; when he goes out, you remove him from the list). And lastly, if you want the current user count you could just ask the list count to this singleton object.
if you use sql server as the session state provider you can use this code to count the number of online users:
SELECT Count(*) As Onlines FROM ASPStateTempSessions WHERE Expires>getutcdate()
The way I've seen this done in the past is adding extra code to the Session_OnStart event in the Global.asax file to store the information in a session agnostic way, e.g. a database or the HttpApplicationState object. Depending upon your needs you could also use Session_OnEnd to remove this information.
You may want to initialise and clean up some of this information using the Application_Start and Application_End events.
The administration page can then read this information and display statistics etc.
This is explained in more depth at http://msdn.microsoft.com/en-us/library/ms178594.aspx and http://msdn.microsoft.com/en-us/library/ms178581.aspx.
You can use PerformanceCounter to get data from System.Diagnostics namespace. It allows you to get "Sessions Active" and much more. It allows you to get from local server as well as remote.
Here is an example of how to do it on local machine
void Main()
{
var pc = new PerformanceCounter("ASP.NET Applications", "Sessions Active", "__Total__");
Console.WriteLine(pc.NextValue());
}
or for remote server you would do:
void Main()
{
var pc = new PerformanceCounter("ASP.NET Applications", "Sessions Active", "__Total__", "ServerHostName.domain");
Console.WriteLine(pc.NextValue());
}
Performance Counters for ASP.NET provides full list of ASP.NET counters that you can monitor
Google Analytics comes with an API that can be implemented on your ASP.NET MVC Application.
It has RealTime functionality so the current amount of users on your website can be tracked and returned to your application.
Here's some information