简体   繁体   中英

Scripting events no longer fire when a user leaves site or closes browser even though app is still active

A project I am working with implements three JAVA script "timers" from a Master Page to track the session time out and to do a "heartbeat" check. The "setTimerout" timer redirects the user to a warning page if the time out count reaches 1 and a half minutes before a session time out will occur. The two "setInterval" timers are implemented to track if the user's browser is sill active on the site. Following are several code snippets that implement the "timers" and the "Heartbeat" and "Heartbeat Check" handlers.

Master Page code to activate timers:

Dim BaseAccountTimeout As Single = CType((CType(CurrentSessionTimeout, Single) - 1.5), Single)
If Session.Item("UseSessionTimeout") Then
    Dim TimeoutAcount As String = CType(((BaseAccountTimeout * 60) * 1000), Integer).ToString
    Page.ClientScript.RegisterStartupScript(Me.GetType, "TimeoutScript", "setTimeout(""top.location.href = '" & GotoTimeoutPage & "'""," & TimeoutAcount & ");", True)
End If
If Session.Item("UseHeartBeat") Then
    Dim BaseHeartBeatTimeout As Single = CType(CurrentHeartBeatTimeout, Single)
    Dim BaseHeartBeatCheckTimeout As Single = CType((BaseHeartBeatTimeout + 1.5), Single)
    Dim TimeoutHeartBeat As String = CType(((BaseHeartBeatTimeout * 60) * 1000), Integer).ToString
    Dim TimeoutHeartBeatCheck As String = CType(((BaseHeartBeatCheckTimeout * 60) * 1000), Integer).ToString
    Page.ClientScript.RegisterStartupScript(Me.GetType, "HeartBeatScript", "setInterval(""ajax.post('/pages/heartbeat.ashx', {id: '" + Session.SessionID + "'}, function() {})""," & TimeoutHeartBeat & ");", True)
    Page.ClientScript.RegisterStartupScript(Me.GetType, "HeartBeatCheckScript", "setInterval(""ajax.post('/pages/heartbeatcheck.ashx', {id: '" + Session.SessionID + "'}, function() {})""," & TimeoutHeartBeatCheck & ");", True)
End If

Heartbeat handler code:

<%@ webhandler language="VB" class="HeartbeatHandler" %>
Imports System
Imports System.Web
Imports System.Data
Imports System.Data.SqlClient
Public Class HeartbeatHandler
Implements IHttpHandler
Public ReadOnly Property IsReusable As Boolean Implements IHttpHandler.IsReusable
    Get
        Return True
    End Get
End Property
Public Sub ProcessRequest(ByVal ctx As HttpContext) Implements IHttpHandler.ProcessRequest
    Dim sqlConnection1 As New System.Data.SqlClient.SqlConnection(ConfigurationManager.ConnectionStrings("ApplicationServices").ConnectionString)
    Dim cmd As New System.Data.SqlClient.SqlCommand
    Dim dreader As System.Data.SqlClient.SqlDataReader
    Dim GotOne As Boolean = False
    Dim ValuesString As String = ""
    Dim FieldsString As String = ""
    Dim id As String = ""
    id = ctx.Request.Params.Item("id")
    If IsNothing(id) Then id = ""
    If id = "" Then GoTo EXITHEARTBEAT
    cmd.Connection = sqlConnection1
    cmd.CommandType = System.Data.CommandType.Text
    If cmd.Parameters.Count = 0 Then
        cmd.Parameters.Add("@uID", System.Data.SqlDbType.NVarChar)
        cmd.Parameters.Add("@Date", System.Data.SqlDbType.DateTime)
        cmd.Parameters.Add("@IP", System.Data.SqlDbType.NVarChar)
    End If
    cmd.Parameters("@uID").Value = id
    cmd.Parameters("@Date").Value = CType(Now, DateTime)
    cmd.Parameters("@IP").Value = HttpContext.Current.Request.UserHostAddress
    Try
        cmd.CommandText = "SELECT SessionID FROM aspnet_Sessions WHERE SessionID = @uID"
        sqlConnection1.Open()
        dreader = cmd.ExecuteReader()
        If dreader.Read() Then GotOne = True
        dreader.Close()
    Catch ex As Exception
        ' do nothing here
    Finally
        sqlConnection1.Close()
    End Try
    If GotOne Then
        Try
            ValuesString = "LastRequest = @Date"
            cmd.CommandText = "UPDATE aspnet_Sessions SET " + ValuesString + " WHERE SessionID = @uID"
            sqlConnection1.Open()
            cmd.ExecuteNonQuery()
        Catch ex As Exception
            ' do nothing here
        Finally
            sqlConnection1.Close()
        End Try
    Else
        Try
            FieldsString = "(Record, LiveDate, SessionID, LastRequest, IPAddress)"
            ValuesString = "(NEWID(), @Date, @uID, @Date, @IP)"
            cmd.CommandText = "INSERT aspnet_Sessions " + FieldsString + " VALUES " + ValuesString
            sqlConnection1.Open()
            cmd.ExecuteNonQuery()
        Catch ex As Exception
            ' do nothing here
        Finally
            sqlConnection1.Close()
        End Try
    End If
EXITHEARTBEAT:
End Sub
End Class

Heartbeat check handler code:

<%@ webhandler language="VB" class="HeartbeatCheckHandler" %>
Imports System
Imports System.Web
Imports System.Data
Imports System.Data.SqlClient
Public Class HeartbeatCheckHandler
Implements IHttpHandler
Public ReadOnly Property IsReusable As Boolean Implements IHttpHandler.IsReusable
    Get
        Return True
    End Get
End Property
Public Sub ProcessRequest(ByVal ctx As HttpContext) Implements IHttpHandler.ProcessRequest
    Dim sqlConnection1 As New System.Data.SqlClient.SqlConnection(ConfigurationManager.ConnectionStrings("ApplicationServices").ConnectionString)
    Dim cmd As New System.Data.SqlClient.SqlCommand
    Dim dreader As System.Data.SqlClient.SqlDataReader
    Dim GotOne As Boolean = Nothing
    Dim ValuesString As String = ""
    Dim FieldsString As String = ""
    Dim LastRequest As DateTime = Nothing
    Dim TimeDifference As Long = 0
    Dim HeartBeatTimeout As Single = CommonCode.Common.DefaultHeartBeatTimeout
    Dim MissedHeartBeats As Single = CommonCode.Common.DefaultMissedHeartBeats
    Dim id As String = ""
    id = ctx.Request.Params.Item("id")
    If IsNothing(id) Then id = ""
    If id = "" Then GoTo EXITHEARTBEATCHECK
    cmd.Connection = sqlConnection1
    cmd.CommandType = System.Data.CommandType.Text
    Try
        cmd.CommandText = "SELECT HeartBeatTimeout, MaximumMissedHeartBeats FROM aspnet_WebSiteSettings"
        cmd.CommandType = System.Data.CommandType.Text
        sqlConnection1.Open()
        dreader = cmd.ExecuteReader()
        If dreader.Read() Then
            HeartBeatTimeout = dreader("HeartBeatTimeout")
            MissedHeartBeats = dreader("MaximumMissedHeartBeats")
        End If
        dreader.Close()
    Catch ex As Exception
        ' do nothing here
    Finally
        sqlConnection1.Close()
    End Try
    Dim MaximumDifference As Single = (HeartBeatTimeout * MissedHeartBeats) + 0.5
    If cmd.Parameters.Count = 0 Then cmd.Parameters.Add("@uID", System.Data.SqlDbType.NVarChar)
    cmd.Parameters("@uID").Value = id
    Try
        cmd.CommandText = "SELECT SessionID, LastRequest FROM aspnet_Sessions WHERE SessionID = @uID"
        cmd.CommandType = System.Data.CommandType.Text
        sqlConnection1.Open()
        dreader = cmd.ExecuteReader()
        If dreader.Read() Then
            If Not IsDBNull(dreader("LastRequest")) Then
                If dreader("LastRequest").ToString.Trim <> "" Then
                    LastRequest = dreader("LastRequest")
                    TimeDifference = DateDiff(DateInterval.Minute, LastRequest, CType(Now, DateTime))
                    If TimeDifference > MaximumDifference Then GotOne = True
                Else
                    GotOne = False
                End If
            Else
                GotOne = False
            End If
        Else
            GotOne = False
        End If
        dreader.Close()
    Catch ex As Exception
        ' do nothing here
    Finally
        sqlConnection1.Close()
    End Try
    If GotOne Then
        Try
            cmd.CommandText = "DELETE aspnet_Sessions WHERE SessionID = @uID"
            cmd.CommandType = System.Data.CommandType.Text
            sqlConnection1.Open()
            cmd.ExecuteNonQuery()
        Catch ex As Exception
            ' do nothing here
        Finally
            sqlConnection1.Close()
        End Try
        CommonCode.Common.DeleteFromSessionIDCollection(id)
        CommonCode.Common.SessionData(CommonCode.Common.DeleteSessionData, id)
        CommonCode.Common.DisposeSession()
    End If
EXITHEARTBEATCHECK:
End Sub
End Class

Following is the code on the Master page:

        var ajax = {};
    ajax.x = function () {
        try {
            return new ActiveXObject('Msxml2.XMLHTTP')
        } catch (e1) {
            try {
                return new ActiveXObject('Microsoft.XMLHTTP')
            } catch (e2) {
                return new XMLHttpRequest()
            }
        }
    };
    ajax.send = function (url, callback, method, data, sync) {
        var x = ajax.x();
        x.open(method, url, sync);
        x.onreadystatechange = function () {
            if (x.readyState == 4) {
                callback(x.responseText)
            }
        };
        if (method == 'POST') {
            x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        }
        x.send(data)
    };
    ajax.get = function (url, data, callback, sync) {
        var query = [];
        for (var key in data) {
            query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
        }
        ajax.send(url + '?' + query.join('&'), callback, 'GET', null, sync)
    };
    ajax.post = function (url, data, callback, sync) {
        var query = [];
        for (var key in data) {
            query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
        }
        ajax.send(url, callback, 'POST', query.join('&'), sync)
    };

I have discovered that the JAVA script timers only continue to fire while a browser is on the site. As soon as the user closes the browser or navigates away from the site, even though the web application is still running, the only event that files is the ASP.NET "Session_End" event in Global.asax when the session times out. None of the "timers" will fire which I am assuming is caused by the fact that the Master Page is no longer loaded so the scripts on the Master do not fire.

I don't really care about the "setTimeout" timer, since there is no need to redirect to a timeout page if the browser is not longer active on the site, so that script can stay on the Master page. However, the two "setInterval" timers are what I am trying to keep running so the app can detect if the user has left the site and then the app can remove the session data and temp directories for that session.

My question is this: Does anyone know of a way to run these two scripts from a service or some other way to implement them so they will continue to fire even though the browser is closed or has gone to another site?

Thanks! David

PS. A little background: The four global variables 'CurrentSessionTimeout', 'CurrentHeartBeatTimeout', 'DefaultHeartBeatTimeout', & 'DefaultMissedHeartBeats' and the two session variables 'UseSessionTimeout' & 'UseHeartBeat' are set when the app starts from values stored in an SQL database. However, neither of the handlers use session variables, but instead get any values they need directly from global variables or the database since the session could have ended before the handler fires, that is if a page is still loaded, which goes back to the original reason for this post. :)

I have come to the conclusion that there isn't a way to fire an event when a browser is not on the site. It appears that as 'cool' as the heart beat idea might be, there is no practical way to make it work from an ASP.NET application web site that uses code behind with page generated Javascripts. Hopefully this post will be helpful to other who might be considering this and keep them from wasting their time ;)

Currently the application's routines are being reworked to use the Session_End event instead. When the Session_End event fires, the session data is essentially gone, so you cannot use session variables for any cleanup routines. Instead the plan is to create some global App_Code variables that will be set in the Session_Start event and then use them in the Session_End event to remove any inactive session data and temp directories.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM