Glengamoi (Forum) · AspHeute · .NET Heute (RSS-Suche) · AspxFiles (Wiki) · .NET Blogs

Onlinestatus des Users über RemoteScripting ermitteln

Geschrieben von: Rene Drescher-Hackel
Kategorie: ASP Tricks

This printed page brought to you by AlphaSierraPapa

Eine der interessantesten Fragen ist doch immer wieder, wieviele User jetzt gerade in unserem Webangebot "online" sind. Hier gibt es sicherlich verschiedene Ansatzpunkte, um diese Frage zu beantworten. Einerseits könnte man über Sessionvariablen verschiedene Events auslösen. Doch da ist wieder das Problem mit dem temporären Cookie, sowie das Problem mit dem TimeOut. Dann könnte man aber auch über ein "Null-definiertes" Frame (ein Frame mit der Größe 0 bzw. 1) einen Refresh auf eine bestimmte Seite setzen. Doch was machen wir, wenn der Browser keine Frames unterstützt (was aber fast kaum noch der Fall sein dürfte - von einigen Exoten mal abgesehen)?

Ich möchte Ihnen heute eine weitere Variante vorstellen, die die Vorzüge des RemoteScripting entsprechend nutzt. Um auch hier den Nachteil klar beim Namen zu nennen: Es wird ein Java-Applet initialisiert, sodaß clientseitige Javaunterstützung vorhanden sein muß. Das Java-Applet ist beim RemoteScripting die Brücke zwischen Client und Server.

Voraussetzungen

Die Voraussetzungen sind im wesentlichen in den Artikeln Sicherheitsaspekte bei der Gestaltung von ASP Sites ohne Cookies und RemoteScripting - Einführung, Tips und Tricks beschrieben. Ergänzend zum letztgenannten Artikel werde ich Ihnen dabei heute den asyncronen Funktionsaufruf mit vorstellen.

User erfassen

Da wir auf Cookies und Sessions verzichten, müssen wir wieder ein anderes Merkmal schaffen, an dem wir immer wieder unseren Client erkennen. Hier nutzen wir einfach wieder unsere ID, die wir entsprechend allen Links mitgeben. Die ID erzeugen wir dabei nach der gleichen Methode, wie im Artikel Sicherheitsaspekte bei der Gestaltung von ASP Sites ohne Cookies beschrieben:

Public Function ID_erzeugen()
	' erforderliche Variablen dimensionieren
	Dim z		' Zufallszahl
	Dim t		' Zeit
	Dim d		' Datum
	Dim ip		' IP des jeweiligen IUSR
	Dim tempID	' die spätere ID in der temporären Variablen
	tempID = ""
	Do
	RANDOMIZE(TIME())
	' 1. Parameter - eine Zufallszahl bis 999.999.999
	z = Int(999999999 * RND + 1)
	' 2. Parameter - eine Zahl aus der aktuellen Zeit erstellt
	t = Replace(TIME(),":","")
	' 3. Parameter - eine Zahl aus dem aktuellen Datum erstellt
	d = Replace(DATE(),".","")
	' 4. Parameter - eine Zahl aus der IP des Besuchers erstellt
	ip = Replace(Request.ServerVariables("REMOTE_ADDR"),".","")
	' ID erzeugen
	tempID = z & t & d & ip
	' Nach erfolgreicher Erzeugung der ID kann diese jetzt in die DB übernommen werden
	sql = "INSERT INTO tblcount(sessionid) VALUES('" & tempID & "');"
	call dbconnect()
	On Error Resume Next
	Conn.Execute(sql)
	On Error Goto 0
	loop Until Err=0
	call dbclose()
	' ID an die Function übergeben
	'ID_erzeugen = tempID
	' oder mit ID Startseite aufrufen
	Response.Redirect "index.asp?ID="& tempID
End Function

Die erzeugte ID wird dabei in einer Datenbank abgespeichert. Hier habe ich eine Access-Datenbank erstellt, die die folgende Tabelle (tblCount) enthält:

Neben der ID, die wir im Feld sessionid ablegen, speichern wir auch noch die letzte Aktivität des Clients im Feld lastaction, wobei dieses Feld als "DATETIME" (DATE[Standarddatum]) definiert ist. Weiterhin speichern wir den Onlinestatus im Feld connected, das wir lediglich auf "Ja/Nein" eingestellt haben. Da das Feld connected sogenannte "Prozeßdaten" enthält, ist es von der Sache her nicht zwingend erforderlich.

Die Datenbankverbindung ist auch hier wieder in einer kleinen Prozedur zusammengefaßt.

Private Sub dbconnect()
 Dim strConnStr
 ' Datenbankverbindung aufbauen (mit Fehlerprüfung)
 If IsObject("Conn") = FALSE Then ' Object erstellen
   Set Conn = Server.CreateObject("ADODB.Connection")
   strConnStr = "Provider=Microsoft.Jet.OLEDB.4.0;" &_
        "Data Source=" & Server.MapPath("db/aspproject.mdb")
   Conn.Open strConnStr
 End If
End Sub

Hat der User unser Webangebot erstmals aufgerufen, wird dann folgender Eintrag in der Datenbank erzeugt:

Das heißt also, daß jeder User, der unser Webangebot aufruft, automatisch einen eigenen und über die ID ihm zuordenbaren Datensatz erzeugt. Jetzt benötigen wir lediglich eine Routine, die abgleicht, ob der Datensatz noch aktuell ist (lastaction), der User also noch "online" ist.

Useraktivstatus prüfen

In der Datei onliner.asp haben wir die eigentliche Funktionalität wieder zusammengefaßt. Hier ist dann auch die Function "online_upd()" angelegt, die den User Status überprüft.

Public Function online_upd(ByVal ID)
    ' Function aktualisiert eigenen Datensatz und 
    ' 1.) den Zeitstempel aktualisieren
    sql1 = "UPDATE tblCount SET lastaction ='" & now() & "' , connected = true " 
    ' 1a.) in Abhängigkeit zum Client über die SessionID
    sql1 = sql1 & " WHERE sessionid ='" & ID & "';" 
    ' deaktiviert "inaktive" Sessions
    sql2 = "UPDATE tblCount SET connected = false " 'inaktive Sessions abmelden
    ' wo die letzte Aktion länger als 10 Sek. zurückliegt
    sql2 = sql2 & " WHERE connected = true AND lastaction < DATEadd(""s"",-10,NOW) ;"
    ' Datenbankverbindung aufbauen
    call dbconnect()
    ' und Abfragen ausführen
    conn.Execute(sql1)
    conn.Execute(sql2)
    ' zum Schluß die Anzahl der aktiven Sessions an die Function zurück geben
    online_upd = conn.Execute("SELECT COUNT(sessionid) FROM tblCount WHERE connected = true;")(0)
    call dbclose()
End Function

Als Parameter übergeben wir wertmäßig an die Funktion die ID, also ByVal.

Dann formulieren wir erst unsere einzelnen SQL-Statements. Hier müssen wir als erstes den der ID entsprechenden Datensatz "aktualisieren". Die Aktualisierung erfolgt über das Feld lastaction, wobei einfach nur der Zeitwert neu gesetzt wird.

Als zweites deaktivieren wir "inaktive Sitzungen", solche also, deren letzte Aktualisierung schon länger zurück liegt. Als Zeitgrenze wählen wir hier einmal 10 Sekunden, das heißt, alle Einträge im Feld lastaction, die älter als 10 Sekunden sind, sind "inaktiv". Deshalb wird connected dann auf "false" gesetzt.

Clientseitige Einbettung

Natürlich müssen wir das ganze irgendwie auch vom Client(-browser) aus aufrufen können. Wie eingangs schon angesprochen, könnte man das ganze natürlich über Frames entsprechend realisieren. Wir wollen hier aber wieder die Vorzüge des RemoteScriptings nutzen.

Hinsichtlich der Einbettung der RemoteScripting-Funktionalität sei hier nur nochmal auf den oben aufgeführten Artikel zum RemoteScripting verwiesen.

Um eventuelle Fehler jetzt abzufangen, erfolgt der Aufruf der Serverfunktion asyncron, das heißt, unsere JavaScript-Funktion ruft die Serverfunktion auf, wartet aber nicht auf das vom Server zurückgegebene Ergebnis (die Funktion wird nur "angestossen"). Damit das Ergebnis aber dennoch angezeigt wird, wird eine "Erfolgsfunktion" und eine "Mißerfolgsfunktion" im Aufruf der Serverfunktion mit angegeben. Praktisch sieht das ganze wie folgt aus:

// Datei mit der Serverfunktionalität
var url = "onliner.asp";
// Function, die die Serverfunktion aufruft
function jetztonline(myid)
{
  var retval = RSExecute(url,"online_upd",myid,online,fehler);
}

Die Variable url enthält die Angabe auf unsere RemoteScripting Datei, die die Serverfunktionalität bereithält - im heutigen Beispiel onliner.asp.

In der Funktion "jetztonline()" rufen wir die RSExecute-Methode auf, wobei die Funktion "jetztonline()" die ID des Users aus der URL mit übernimmt, da dieser Wert ja von unserer Serverfunktion online_upd erwartet wird.

Bekommen wir ein Ergebnis zurück, dann wird die (Erfolgs)Funktion "online" aufgerufen, andernfalls - wenn der Aufruf der Funktion "jetztonline(myid)" einen Fehler erzeugte - die (Mißerfolgs)Funktion "fehler". Die Funktion "online" übernimmt dabei den Rückgabewert (retval) der RSExecute-Methode aus der Function "jetztonline()".

Unsere Funktion "online" sieht folgendermaßen aus:

// Function, die im Erfolgsfall (kein Fehler) aufgerufen wird
function online(retval)
{
  document.all.online.innerHTML = "zur Zeit online:" + retval.return_value;
}

Im BODY Tag unseres Dokumentes haben wir den Eintrag

<div id=online ></div>

Innerhalb diesen Bereiches geben wir dann unser Ergebnis des ürsprünglichen Serverfunktionsaufrufes aus, das wir mit

document.all.online.innerHTML = "zur Zeit online:" + retval.return_value;

sichtbar machen können.

Jetzt müssen wir nur noch ein "Refresh" unseres Funktionsaufrufes realisieren, damit der Onlinestatus immer aktuell ist. Da der eigentliche Refresh nicht auf die Seite gerichtet sein muß ( wie z.B. in der Frames-Variante), damit die Funktion "jetztonline()" neu ausgeführt wird, müssen wir eine Lösung in die Funktion "online" einfügen, die die Funktion "jetztonline" in bestimmten, kontinuierlichen Abständen aufrufen wird. Hier ist uns die JavaScript-Anweisung

setTimeout("jetztonline('<%=id%>')",9000);

behilflich. Im ersten Teil benennen wir die Funktion, die aufzurufen ist (mit all ihren Argumenten - hier die ID des Clients), im zweiten Teil geben wir an, nach welcher Zeit die Funktion, die wir im ersten Teil benannt haben, aufzurufen ist. Die Angabe des Zeitintervalls erfolgt dabei hier in Millisekunden, so daß in unserem konkreten Fall alle 9 Sekunden die Funktion "jetztonline" aufgerufen wird (alles größer oder gleich 10 Sekunden würde eine Abmeldung zur Folge haben. Wer also den Wert von 9000 msec. erhöhen möchte, muß dann auch die Zeitparameter in der Funktion online_upd() entsprechend anpassen.).

Falls der Aufruf der Funktion "jetztonline" einen Fehler hervorruft, wird die Fehlerfunktion aufgerufen. Will man das Rückgabeergebnis retval in der Fehlerfunktion auswerten, so muß diese auch den Wert mitübernehmen. In unserem Beispiel ist aber gerade die Fehlerauswertung des Rückgabewerte völlig uninteressant. Wir benötigen bestenfalls nur eine alternative Anzeige beim Client, ohne jedoch eine "alert"-Box aufzurufen. Nach Möglichkeit soll doch der Client gar nicht mitbekommen, daß es jetzt gerade einen Fehler gab.

Unsere Fehlerfunktion haben wir daher folgendermaßen definiert:

// Function, die im Fall eines RemoteScripting-Fehlers aufgerufen wird
function fehler()
{
  document.all.online.innerHTML = "zur Zeit online:...bitte warten...";
  setTimeout("jetztonline('<%=id%>')",3000);} 
}

Wurde ein Fehler in der Funktion "jetztonline" zurückgegeben, so wird nicht der aktuelle Onlinestatus beim Client angezeigt, sondern alternativ "...bitte warten ...".

Wir warten einfach 3 Sekunden, und fragen dann erneut beim Server an. Wird kein Fehler zurückgegeben, bekommen wir die gewünschte Anzeige im Browser.

Damit jetzt das Ganze durch den Client ohne großes Zutun in Gang gesetzt wird, müssen wir irgendwo unsere Funktion "jetztonline" einmal aufrufen. Im weiteren ruft sie sich - wie wir oben ja gesehen haben - selbständig auf. Es bietet sich also das "onload"-Event an, sodaß im BODY Tag folgender Eintrag hinzukommt:

<BODY onload = "jetztonline('<%=id%>')">

Damit wären wir fertig. Wir bekommen jetzt immer die Anzahl aller Benutzer angezeigt, die im Zeitrahmen von ±10 Sekunden in unserem Angebot "online" sind.

Zusammenfassung

Über RemoteScripting den Onlinestatus zu erfassen ist eine durchaus interessante Sache und zeigt einmal mehr, was alles möglich ist. Zu bedenken bleibt aber eines dennoch: wir fragen permanent die Datenbank ab, und das in relativ kurzen Zeitperioden. Wenn Sie die hier vorgestellte Variante einsetzen wollen, so würde ich Ihnen eine separate Datenbank (Access) empfehlen, damit die Zugriffszahlen gewissermaßen verteilt werden können. Beim Einsatz des MS-SQL-Servers bietet es sich an, einiges an Arbeit auf den SQL-Server zu verlagern.

Hinsichtlich des Einsatzes des MySQL-Servers hat die Praxis gezeigt, daß es hier dem Datenbank-Server scheinbar egal ist, wie oft und in welchen Zeitintervallen eine Abfrage erfolgt - ein Punkt, der sicherlich für dessen Einsatz spricht. Im kleinen "Stresstest" hat sich gezeigt, daß sowohl die Access-Datenbank, als auch die Anwendung ohne größere Probleme die Aufgaben bewältigt. Nach ca. 62 Aufrufen in 7 Sekunden war allerdings am Windows 2000 Professional PC "Feierabend" - der IE hat hier dann seinen weiteren Dienst versagt.

In entsprechender Kombination könnte man dann noch eine Benutzertabelle abfragen und sich so den Benutzernamen des (angemeldeten) Benutzers anzeigen lassen. Über eine entsprechende Verlinkung ließe sich dann sogar ein kleines Kommunikationssystem realisieren.

This printed page brought to you by AlphaSierraPapa

Download des Codes

Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20010628.zip

Verwandte Artikel

Remote Scripting (I)
http:/www.aspheute.com/artikel/20010125.htm
RemoteScripting - Einführung, Tips und Tricks
http:/www.aspheute.com/artikel/20010606.htm
Sicherheitsaspekte bei der Gestaltung von ASP Sites ohne Cookies
http:/www.aspheute.com/artikel/20010601.htm

 

©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.