I'm trying to implement an HttpStreaming comet push server in ASP.net. I am implementing an IHttpAsyncHandler that holds onto an http request and periodically sends messages down to the connected client. The connection with the client can be held open for a very long time (lets say 30 minutes). The issue that I am having is that since I don't end the request for a very long time, the handler technical is still running. So when the app pool recycles or ends, my asp.net application doesn't end gracefully. Meaning, Application_End never gets called in Global.asax since it is waiting for all handlers to complete before it gets called. My handler hasn't completed yet since it is holding on to the request. Eventually, the application just gets killed by IIS.
Here a sample HttpHandler that runs infinitely until something tells it to stop.
public class StreamingSocketHandler3 : IHttpHandler
{
private static readonly AppDomainShutdown Instance = AppDomainShutdown.Instance;
public void ProcessRequest(HttpContext context)
{
long c = 1;
bool stop = false;
Instance.OnStop += delegate()
{
stop = true;
};
while (!stop)
{
LogThis.Log.LogThis("loop: " + c, LogThis.eloglevel.debug);
c++;
System.Threading.Thread.Sleep(1000);
}
}
public bool IsReusable
{
get
{
return true;
}
}
}
If a client connects to this handler and then I stop the app pool, the web app doesn't end until the app gets completely killed by IIS sometime later.
The obvious answer to this problem is to listen to some sort of application end event and then end the request. I can't hook into Global.asax Application_end as it doesn't get called until the handler ends. The following blog post (link) provides some other alternatives (see q5 at the end of the post). I have tried the AppDomain.DomainUnload event suggest with no luck. I have also tried the IRegisteredObject interface suggested. That doesn't work either. I have built the following class that implements IRegisteredObject for testing.
public sealed class AppDomainShutdown : IRegisteredObject
{
public event Action OnStop;
// Singleton reference
public static readonly AppDomainShutdown Instance = new AppDomainShutdown();
// Singleton private Constructor
private AppDomainShutdown()
{
// Register the object
HostingEnvironment.RegisterObject(this);
}
public void Stop(bool immediate)
{
// Whats it want us to do?
if (!immediate)
{
// Do some code to handle graceful
// if OK
LogThis.Log.LogThis("Not Immediate Stop called", LogThis.eloglevel.debug);
OnStop();
HostingEnvironment.UnregisterObject(this);
}
else
{
// Do some code to force down (THREAD.ABORT)
// Mandatory
LogThis.Log.LogThis("Immediate Stop called", LogThis.eloglevel.debug);
OnStop();
HostingEnvironment.UnregisterObject(this);
}
}
}
If I make a call to this class from Global.asax without the handler starting, I get the application stop notification as expected. However, placing the same call in the HttpHandler (see the handler code), the OnStop event never fires.
What's frustrating is that I have seen a couple of forums posts on the web were the posts had my exact issue and the poster supposedly was able to implement IRegisteredObject and end the HttpRequest so the app could shutdown.
Please note that the HttpHandler code I'm presenting is simply for testing that I can end the ProcessRequest method in response to the application ending.
Related
I am currently working on UWP application. This App needs to communicate even when app is closed.
So, I am using AppServiceConnection for the same. I open an AppServiceConnection and it invokes OnBackgroundActivated of App. I store this connection and add request received handler in the app's one class object and I also initialize few object to perform some task when request received handler is called.
When other .exe sends message to UWP app using app service connection after opening the connection, then request received handler is called. I take deferral on request received handler also.
Inside RequestReceived handler, I need to call a method of class which was being instantiated on opening connection. But by the time, I go to that statement in RequestReceived handler to call that method.
App goes to suspended state. Hence object is no longer is there in memory.
Now, I have few question.
If I have taken deferral on App ReqeustRecevied handler, then App should not go into suspended state.
is it correct assumption? or It doesn't really matter.
Once App is about to get in suspended state as then I request extend session using
https://learn.microsoft.com/en-us/windows/uwp/launch-resume/run-minimized-with-extended-execution
but my request access is getting denied every time.
Could anybody explaination for above two points and would appreciate if can provider better solution.
Code Sample
// Main UWP App class
public sealed partial class App : Application, IDisposable
{
protected async override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
base.OnBackgroundActivated(args);
var taskDeferral = arg.TaskInstance.GetDeferral();
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails details &&
details.CallerPackageFamilyName == Package.Current.Id.FamilyName)
{
AppServiceConnectionHelper.Connection = (args.TaskInstance.TriggerDetails as AppServiceTriggerDetails).AppServiceConnection;
IntializeNonUICommunciaiton(args);
var instanceToHandleRequest = new HandleRequestReceivedClass();
AppServiceConnectionHelper.Connection += instanceToHandleRequest.OnRequestReceived;
}
}
}
=======================================
public Class HandleRequestReceivedClass
{
List<MessageHandler> handlers =new List<MessageHandler>();
public async void OnBridgeClientPluginMessage(AppServiceConnection
sender, AppServiceRequestReceivedEventArgs args)
{
var messageDeferral = args.GetDeferral();
try
{
handlers[args.Request.Message](); // app goes to suspended state and
can't find handler which are delegate defined in the app.
}
finally
{
messageDeferral.complete();
}
}
I'm using SignalR and have a Hub where I've overwritten the OnConnected method. The reason is, the server publishes data every 60 seconds. But when a new client connects, I want to publish the data immediately.
In a simplified form:
public class CustomerPublisher : Hub
{
public void Publish(Customer customer)
{
Clients.All.receive(customer);
}
public override Task OnConnected()
{
return base.OnConnected();
}
}
The OnConnected method is never called (I tested by debugging). I've read in several places (like here) that the client needs to have a connected handler, like:
$.connection.onlineHub.client.connected = function(){...}
However, I have a WPF client, so in .NET I tried adding this, on top of the event I was already handling:
hubProxy.On<object>("receive", OnCustomerReceived); // this line was already present
hubProxy.On<object>("connected", OnConnected); // I added this
This didn't help. But I do have a connection, because after some time, I start receiving data.
Is there anything I'm missing? How can I get SignalR to call the OnConnected method when my .NET client connects?
You can try to use StateChanged event like this :
Connection.StateChanged += Connection_StateChanged;
void Connection_StateChanged(StateChange obj)
{
if (obj.NewState == ConnectionState.Connected)
{
//do somethign here
}
}
You ll receive Connecting and Connected states
In my case, the problem was Autofac. I had set it up in my own way (due to the way I had set up WCF duplex before using SignalR). But it is important to follow the docs. Even though a connection was made, and my client received data, I never entered the OnConnected method. After following the documentation, I the method was called.
In my MVC application, I have a controller which creates an instance of a Model object. Within the Model, I have a timer which executes a function every 700ms. When I run the controller within my website, I continue to see the timer running even after I close the browser window. (The timer starts/stops a serial port connection, and I have a light that indicates when there is communication).
The communication actually never stops until I restart IIS entirely.
Is there some way in which I can check whether the controller is still in use, and thereby stop the timer if the controller is no longer active?
Here is the model code:
public CheckPulses(int interval) {
CheckPulsesTimer(interval);
}
public void CheckPulsesTimer(int interval) {
Timer timer = new Timer();
timer.Enabled = true;
timer.AutoReset = true;
timer.Interval = interval;
timer.Elapsed += new ElapsedEventHandler(GetPulses);
}
And here is the Controller code:
public ActionResult Index() {
CheckPulses Pulses = new CheckPulses(700);
return View();
}
I thought perhaps I should just add something to the GetPulses event, which happens every time the timer interval expires, and check there whether the controller is still in use. If it isn't, stop the timer. However, I'm not sure how to write that check. Can anyone point me in the right direction?
I think the core issue here is in your understanding of what a controller being "in use" is.
It seems as if you are imagining the following:
The user types in your URL and the browser connects to the server.
The server routes the request to an MVC Controller, which keeps a connection to the browser, and responds to each subsequent request using this connection.
The user closes their browser, or navigates away - and the Controller closes down the connection, and disposes.
This is not, however, what actually happens. Web traffic is generally a series of isolated, individual request/response pairings. There is no ongoing connection between the browser and the server.
The more correct sequence is something like the following:
The user types in your URL, and the browser connects to your server on port 80 (or 443 for https), passing in a valid HTTP request, containing a URL, HTTP verb, and other info.
Your server uses the HTTP request's information to route the request to a specific piece of executable code (in this case, your Controller's specific Action method).
In order to execute, the server creates an Instance of your Controller, then fires it's Action method, using the HTTP request's information as parameters.
Your Controller code executes, and an HTTP response message is sent back to the browser.
The Controller is .Dispose()'d by the ControllerFactory.
The issue you're experiencing is not caused by your Controller being "active" - because that's not something Controllers do. You are, instead, creating a Timer within your Model - and the Model, therefore, continues to live on in memory, unable to be killed by GarbageCollection. See this answer for more details.
In essence, think of it like this:
Your Controller is acting as a factory. When you call its Index method, it's equivalent to telling the factory "produce 1 Model". Once the Model rolls out of the factory, the factory is free to shut down, turn off the lights, and send all the workers home (the Controller is .Dispose()'d).
The Model, however, lives on in memory. In most cases, the Model should probably die off due to GarbageCollection once it goes out of scope - but because you have an active Timer inside of it, something is preventing that process from executing - and so the Model continues to live in your server's memory, happily firing its code again and again on each timer expiration.
Note that this has nothing to do with whether or not the user is still on your web-site, or whether their browser is still open, etc. It has to do with the nature of variable scoping and GarbageCollection on your server. You have now started a process which will continue until told to stop.
A potential solution:
In order to fix this, you might consider the following:
Create your Timer inside of a Singleton object. Something like:
public static class ConnectionManager {
private static bool initialized = false;
private static DateTime lastStartedAt = DateTime.Now;
private static Timer timer = null;
private static void Initialize() {
if (timer == null) {
timer = new Timer() {
AutoReset = true,
};
timer.Elapsed += new ElapsedEventHandler(GetPulses);
}
}
public static void Start(int interval) {
lastStartedAt = DateTime.Now;
Initialize(); // Create a timer if necessary.
timer.Enabled = true;
timer.Interval = interval;
}
public static void Stop() {
timer.Enabled = false;
}
}
Change your Controller to something like this:
public ActionResult Index() {
ConnectionManager.Start(700);
return View();
}
When you handle the expiration of the Timer event, check to see how long ago the lastStartedAt event occurred. If it is more than X minutes, do not process, and fire ConnectionManager.Stop().
This will give you the ability to keep your serial activity polling on a rolling expiration. In other words - each time a user requests your Index page, you will refresh the timer, and ensure that you are listening. After X minutes, though, if no users have made a request - you can simply shut down the timer and the associated port.
You are likely not stopping or disposing the Timer. This will cause it to not be garbage collected and stay active for the duration the application stays running.
You should stop and dispose of the Timer within the Controller's Dispose method.
You have several options:
Override Dispose method on the controller and dispose your model with calling Pulses.Dispose()
Override OnActionExecuted method on the controller and do disposing
Create custom action filter and implement OnActionExecuted method and assign it to controller or action.(You can place reference to your model into TempData and then in your ActionFilter check if filterContext.Controller.TempData["MyReferenceToModel"] != null, then use it to dispose timer)
Implement IDisposable interface in your Model and use it inside using statement
Hope this helps,
Dima
You can stop the Timer with JQuery+ MVC.
Jquery can respond to the unload event of the page like so:
$(window).unload(function() {
alert('Handler for .unload() called.');
});
You can make ajax calls to your controllers. We also need to create a property to access the timer. I'll assume you called your Timer Property MyTimer
Timer MyTimer { get; set; }
So you need to create a controller that returns void and calls the Stop() method of your Timer.
[HttpPost]
public void KillTimer()
{
MyTimer.Stop();
}
Now if you add an Ajax call to your unload event we created earlier, then we should be able to kill the timer whenever the page closes.
$.ajax(
{
type: "POST",
url: "/Home/Index",
,
success: function(result)
{
alert("Timer Closed");
},
error : function(req, status, error)
{
alert("Sorry! We could not receive your feedback at this time.");
}
});
The success and error functions are not necessary. I included them only for demonstration.
I also guessed the name of your controller to be Home. You will need to update the URL if it is something different. You could do this with pure javascript. JQuery helps to save you from writing lots of tedious browser compatibility code. (I hear opera is a pain with the unload part)
To address the issue of potential browser crashes and unexpected events you can wire up your controller to check if the timer has exceeded a max time limit.
Good Luck !
Sources :
JQuery Unload()
Invoking ASP.NET MVC Actions from JavaScript using jQuery
JQuery Ajax()
i have a windows service that get user details and save the result into log text file. and, my problem is when i shut down or log off my system, i also would like to save the time that i down my system into that log file. but, i don't know how to do that.
I checked the winproc method to detect shutdown operation but i was not able to use it on window service, on googling found it can be used with forms only. how can we detect user have clicked shutdown or log off and do some action.
so,please give me some idea or suggestion on that.
i have used it for logoff but on log entry is made when i logoff the system
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
this.RequestAdditionalTime(250000); //gives a 25 second delay on Logoff
if (changeDescription.Reason == SessionChangeReason.SessionLogoff)
{
// Add your save code here
StreamWriter str = new StreamWriter("D:\\Log.txt", true);
str.WriteLine("Service stoped due to " + changeDescription.Reason.ToString() + "on" + DateTime.Now.ToString());
str.Close();
}
base.OnSessionChange(changeDescription);
}
For a shutdown, override the OnShutdown method:
protected override void OnShutdown()
{
//your code here
base.OnShutdown();
}
For a logoff:
First, add an event handler to Microsoft.Win32.SystemEvents.SessionEnded in the Service Constructor:
public MyService()
{
InitializeComponent;
Microsoft.Win32.SystemEvents.SessionEnded += new Microsoft.Win32.SessionEndedEventHandler(SystemEvents_SessionEnded);
}
Then add the handler:
void SystemEvents_SessionEnded(object sender, Microsoft.Win32.SessionEndedEventArgs e)
{
//your code here
}
This should catch any ended session, including the console itself (the one running the services).
Tl;dr
In your service set
CanShutdown = true;
then override
protected override void OnShutdown()
{
//Your code here
//Don't forget to call ServiceBase OnShutdown()
base.OnShutdown();
}
Now the extended answer
I know I'm bringing this up from the dead but I found it helpful and hope to add a little to the topic. I'm implementing a WCF duplex library hosted in a Windows Service and came across this thread because I needed to detect, from within the windows service, when a user logs off or shuts down the computer. I'm using .Net Framework 4.6.1 on Windows 7 and Windows 10. Like previously suggested for shutdown what worked for me was overriding ServiceBase.OnShutdown() like so:
protected override void OnShutdown()
{
//Your code here
//Don't forget to call ServiceBase OnShutdown()
base.OnShutdown();
}
Remember to add the following to your service's constructor to allow the shutdown event to be caught:
CanShutdown = true;
Then to capture when a user logs off, locks the screen, switches user, etc. you can just override the OnSessionChange method like so:
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
if (changeDescription.Reason == SessionChangeReason.SessionLogoff)
{
//Your code here...
//I called a static method in my WCF inbound interface class to do stuff...
}
//Don't forget to call ServiceBase OnSessionChange()
base.OnSessionChange(changeDescription);
}
And of course remember to add the following to your service's constructor to allow catching of session change events:
CanHandleSessionChangeEvent = true;
You should override OnShutdown in your service
// When system shuts down
protected override void OnShutdown()
{
// Add your save code here
base.OnShutdown();
}
You might also want to override OnStop
// When the user presses stops your service from the control panel
protected override void OnStop()
{
// Add your save code here too
base.OnStop();
}
Edit:
If you really want to listen to the shutdown event Microsoft.Win32.SystemEvents.SessionEnding is the way to go.
Maybe you can use this. Poll the method in question every now and then (1 second interval) and you'll be able to do what you want.
You need RegisterServiceCtrlHandlerEx API call.
Disclaimer
Maybe this answer wont be useful when Windows changes the Shut down behavior again.
Which notifications occur when computer Shutsdown?
As far as i was able to find out, three notifications occur when the computer shuts down:
SessionLogoff
SessionLock
Suspend
The first two are delivered by OnSessionChange(), and the last by OnPowerEvent().
Of course, a natural choice is Suspend, but care should be taken that this notification is also sent when the computer is sent to Sleep.
How to get notified?
To get the notifications posted above you have to allow the notifications in the service constructor as follows:
public Service1()
{
InitializeComponent();
CanHandlePowerEvent = true;
CanHandleSessionChangeEvent = true;
}
and then override the respective
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
// Do something
base.OnSessionChange(changeDescription);
}
protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
{
// Do something
return base.OnPowerEvent(powerStatus);
}
What about ServiceBase.OnShutdown()?
Well, this is not called when a computer is shut down by clicking on "Shut down" in the Power options.
Indeed, as written in this official post,
When you shut down your computer, your computer actually enters a hibernation state instead of a full shutdown.
and further, according to the documentation:
Use OnShutdown to specify the processing that occurs when the system shuts down.
This event occurs only when the operating system is shut down, not when the computer is turned off.
OnShutdown is expected to be overridden when the CanShutdown property is true.
So, if manually shutting down the computer does not really shuts down the computer, but it puts it in an hibernation state, what can shut down the computer?
The post quoted above has again the answer:
Full shutdown only occurs when you restart a computer or when other event causes the computer to process a full shutdown.
And indeed, i could get the OnShutdown() method called on my service by setting
CanShutdown = true;
and overriding to OnShutdown
protected override void OnShutdown()
{
// Do something
base.OnShutdown();
}
and then restarting my computer.
Well, enough with this post. I only hope the shut down behavior of Windows does not change soon...
I was creating a http module and while debugging I noticed something which at first (at least) seemed like weird behaviour.
When I set a breakpoint in the init method of the httpmodule I can see that the http module init method is being called several times even though I have only started up the website for debugging and made one single request (sometimes it is hit only 1 time, other times as many as 10 times).
I know that I should expect several instances of the HttpApplication to be running and for each the http modules will be created, but when I request a single page it should be handled by a single http application object and therefore only fire the events associated once, but still it fires the events several times for each request which makes no sense - other than it must have been added several times within that httpApplication - which means it is the same httpmodule init method which is being called every time and not a new http application being created each time it hits my break point (see my code example at the bottom etc.).
What could be going wrong here? Is it because I am debugging and set a breakpoint in the http module?
It have noticed that it seems that if I startup the website for debugging and quickly step over the breakpoint in the httpmodule it will only hit the init method once and the same goes for the eventhandler. If I instead let it hang at the breakpoint for a few seconds the init method is being called several times (seems like it depends on how long time I wait before stepping over the breakpoint). Maybe this could be some build in feature to make sure that the httpmodule is initialized and the http application can serve requests , but it also seems like something that could have catastrophic consequences.
This could seem logical, as it might be trying to finish the request and since I have set the break point it thinks something have gone wrong and try to call the init method again? Soo it can handle the request?
But is this what is happening and is everything fine (I am just guessing), or is it a real problem?
What I am specially concerned about is that if something makes it hang on the "production/live" server for a few seconds a lot of event handlers are added through the init and therefore each request to the page suddenly fires the eventhandler several times.
This behaviour could quickly bring any site down.
I have looked at the "original" .net code used for the httpmodules for formsauthentication and the rolemanagermodule, etc... But my code isn't any different that those modules uses.
My code looks like this.
public void Init(HttpApplication app)
{
if (CommunityAuthenticationIntegration.IsEnabled)
{
FormsAuthenticationModule formsAuthModule = (FormsAuthenticationModule) app.Modules["FormsAuthentication"];
formsAuthModule.Authenticate += new FormsAuthenticationEventHandler(this.OnAuthenticate);
}
}
Here is an example how it is done in the RoleManagerModule from the .NET framework:
public void Init(HttpApplication app)
{
if (Roles.Enabled)
{
app.PostAuthenticateRequest += new EventHandler(this.OnEnter);
app.EndRequest += new EventHandler(this.OnLeave);
}
}
Does anyone know what is going on?
(I just hope someone out there can tell me why this is happening and assure me that everything is perfectly fine) :)
UPDATE:
I have tried to narrow down the problem and so far I have found that the init method being called is always on a new object of my http module (contrary to what I thought before).
I seems that for the first request (when starting up the site) all of the HttpApplication objects being created and their modules are all trying to serve the first request and therefore all hit the eventhandler that is being added.
I can't really figure out why this is happening.
If I request another page all the HttpApplication's created (and their modules) will again try to serve the request causing it to hit the eventhandler multiple times.
But it also seems that if I then jump back to the first page (or another one) only one HttpApplication will start to take care of the request and everything is as expected - as long as I don't let it hang at a break point.
If I let it hang at a breakpoint it begins to create new HttpApplication's objects and starts adding HttpApplications (more than 1) to serve/handle the request (which is already in process of being served by the HttpApplication which is currently stopped at the breakpoint).
I guess or hope that it might be some intelligent "behind the scenes" way of helping to distribute and handle load and / or errors. But I have no clue.
I hope some out there can assure me that it is perfectly fine and how it is supposed to be?
It's normal for the Init() method to be called multiple times. When an application starts up, the ASP.NET Worker process will instantiate as many HttpApplication objects as it thinks it needs, then it'll pool them (e.g. reuse them for new requests, similar to database connection pooling).
Now for each HttpApplication object, it will also instantiate one copy of each IHttpModule that is registered and call the Init method that many times. So if 5 HttpApplication objects are created, 5 copies of your IHttpModule will be created, and your Init method called 5 times. Make sense?
Now why is it instantiating 5 HttpApplication objects say? Well maybe your ASPX page has links to other resources which your browser will try to download, css, javascript, WebResource.aspx, maybe an iframe somewhere. Or maybe the ASP.NET Worker Process 'is in the mood' for starting more than 1 HttpApplication object, that's really an internal detail/optimisation of the ASP.NET process running under IIS (or the VS built in webserver).
If you want code that's guaranteed to run just once (and don't want to use the Application_StartUp event in the Global.asax), you could try the following in your IHttpModule:
private static bool HasAppStarted = false;
private readonly static object _syncObject = new object();
public void Init(HttpApplication context)
{
if (!HasAppStarted)
{
lock (_syncObject)
{
if (!HasAppStarted)
{
// Run application StartUp code here
HasAppStarted = true;
}
}
}
}
I've done something similar and it seems to work, though I'd welcome critiques of my work in case I've missed something.
Inspect the HttpContext.Current.Request to see, for what request the module's init is fired. Could be browser sending multiple request.
If you are connected to IIS, do check IIS logs to know whether any request is received for the time you are staying at the break point.
Here is a bit of explanation as to what you should use, when, and how they work.
When to use Application_Start vs Init in Global.asax?
Edit: More reading
The ASP Column: HTTP Modules
INFO: Application Instances, Application Events, and Application State in ASP.NET
Examle above locks the IHttpModule for all requests, and then, it frezes the whole application.
If your IHttpModule calls request several times is needed to call HttpApplication method CompleteRequest and dispose the HttpApplication instance of the IHttpModule in EndRequest event in order to remove instance of the HttpApplication like this:
public class TestModule :IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
context.EndRequest += new EventHandler(context_EndRequest);
}
void context_EndRequest(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
app.CompleteRequest();
app.Dispose();
}
void context_BeginRequest(object sender, EventArgs e)
{
//your code here
}
#endregion
}
If you need that IHttpModule requests every time without rerequest on postback use this code above.