Echtzeitapplikationen im Web-Kontext am Beispiel

Werbung
Hochschule RheinMain
Fachbereich Design Informatik Medien
Studiengang Medieninformatik
Abschlussarbeit
zur Erlangung des akademischen Grades
Bachelor of Science (B.Sc.)
Echtzeitapplikationen im Web-Kontext
am Beispiel eines Multiplayer-Spiels
Vorgelegt von
am
Referent
Korreferent
Wasili Adamow
04. Oktober 2013
Prof. Dr. Jörg Berdux
Prof. Dr. Wolfgang Weitz
2
3
Erklärung gemäß ABPO
Ich erkläre hiermit,
• dass ich die vorliegende Abschlussarbeit selbstständig angefertigt,
• keine anderen als die angegebenen Quellen benutzt,
• die wörtlich oder dem Inhalt nach aus fremden Arbeiten entnommenen Stellen,
bildlichen Darstellungen und dergleichen als solche genau kenntlich gemacht
und
• keine unerlaubte fremde Hilfe in Anspruch genommen habe.
Wiesbaden, 04. Oktober 2013
Wasili Adamow
Erklärung zur Verwendung der Bachelor Thesis
Hiermit erkläre ich mein Einverständnis mit den im folgenden aufgeführten Verbreitungsformen dieser Abschlussarbeit:
Verbreitungsform
Einstellung der Arbeit in die Hochschulbibliothek mit Datenträger
Einstellung der Arbeit in die Hochschulbibliothek ohne Datenträger
Veröffentlichung des Titels der Arbeit im Internet
Veröffentlichung der Arbeit im Internet
Wiesbaden, 04. Oktober 2013
Wasili Adamow
Ja
Nein
×
×
×
×
4
Zusammenfassung
Das Internet entwickelt sich immer weiter und übernimmt immer mehr Bereiche
des alltäglichen Lebens. Dadurch wandern viele Applikationen, welche man bisher
noch lokal installieren musste, in das Netz ab und werden somit für immer mehr
Menschen zugänglich. Diese Arten von Diensten bringen viele Vorteile mit sich:
Sie sind von überall auf jedem Endgerät erreichbar, speichern zuverlässig Daten
ab und laufen so schnell, als wären sie lokal installiert. Jedoch steigt dadurch der
Anspruch an die Hardware und an die Technologien immer weiter, wodurch diese
sich immer weiterentwickeln müssen - was früher mit HTTP umgesetzt wurde,
ist für diese Applikationen schon viel zu langsam. Diese Arbeit soll an einem
Beispiel aufzeigen, wie man mit den heutigen Mitteln eine effiziente, verteilte WebApplikation entwickeln kann, welche zudem in Echtzeit auf nahezu jedem Gerät
lauffähig ist.
2
Inhaltsverzeichnis
1
Einleitung
5
2
Spielkonzept
7
2.1
Grundlegende Spielmechanik . . . . . . . . . . . . . . . . . . . . . . .
7
2.2
Spielmechanik im Mehrspieler-Modus . . . . . . . . . . . . . . . . . .
9
3
4
5
Echtzeitkommunikation im Web
13
3.1
HTTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
3.2
AJAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
3.3
Websockets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
3.4
WebRTC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
3.5
Vergleich Websockets, Socket.IO & WebRTC . . . . . . . . . . . . . . .
20
3.6
Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23
Anforderungsanalyse
25
4.1
Benutzerinteraktionen . . . . . . . . . . . . . . . . . . . . . . . . . . .
25
4.2
Spielelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
4.3
Echtzeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
4.4
Kompatibilität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
Konzept
35
5.1
Gesamtüberblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
5.2
Netzwerkkommunikation . . . . . . . . . . . . . . . . . . . . . . . . .
37
5.2.1
38
Infrastruktur . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
4
Inhaltsverzeichnis
5.2.2
6
7
Nachrichtentypen
. . . . . . . . . . . . . . . . . . . . . . . . .
41
5.3
Datenverwaltung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
43
5.4
Struktur der Clients . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
49
5.5
Ablauf der Kommunikation . . . . . . . . . . . . . . . . . . . . . . . .
52
Implementierung
65
6.1
Auswahl der Technologien . . . . . . . . . . . . . . . . . . . . . . . . .
65
6.2
Netzwerkschicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
66
6.3
Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
6.4
Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
74
Schlusswort
77
Kapitel 1
Einleitung
Das Internet und dessen Funktionalitäten wachsen immer weiter. Während zu
Beginn der Entwicklungen vor allem der schnelle Transport von einfachen Textnachrichten im Vordergrund stand, haben sich heutzutage diese Nachrichten soweit
entwickelt, dass man mit ihnen alle erdenklichen Daten verschicken kann. Durch
die immer besser werdende Hardware und die damit schnellere Übertragung von
Daten durch das Netz, etabliert sich seit einigen Jahren auch das Cloud Computing,
welches als Grundlage für verschiedene Dienste über das Internet dient. Hierbei
werden nach dem National Institute of Standards and Technology (NIST) drei Formen
unterschieden: Platform as a Service (PaaS), Infrastructure as a Service (IaaS) und Software as a Service (SaaS) [12]. Neben dem Sichern von Daten im Netz (Cloud Storage),
was zum Bereich IaaS zählt, ist vor allem das Auslagern von Software sehr verbreitet und wird von vielen Internetdiensten angeboten - ein Beispiel hierfür sind
Textverarbeitungsprogramme.
Diese Verschiebung der Dienste in das Netz hat sowohl für die Nutzer als auch für
die Anbieter einige Vorteile. Die Nutzer können, ungeachtet der genutzten Geräte,
von überall auf ihre Daten zugreifen, diese bearbeiten und wieder abspeichern. Dies
ist wiederum auch ein Vorteil für die Anbieter, da diese immer mehr Dienste direkt
im Browser anbieten können und somit nicht für jedes System unterschiedliche
Software produzieren müssen. Jedoch steigen hierdurch auch die Ansprüche an
die Technologien, auf denen diese Dienste basieren. Die Server müssen viel mehr
Daten abspeichern, verwalten und anbieten können, ohne dabei an Geschwindigkeit
zu verlieren. Auch das Netzwerk muss eine immer größer werdende Datenmenge
übertragen können. Zuletzt müssen die verschiedenen Dienste auch auf allen Endgeräten die gleichen Funktionalitäten anbieten, weshalb diese bestimmte Technologien
unterstützen müssen. Da all dies in der Praxis meist nicht umsetzbar ist, müssen
Kompromisse eingegangen und Alternativen zur Verfügung gestellt werden, damit
5
6
Einleitung
möglichst viele Nutzer die Dienste verwenden können.
Diese Arbeit befasst sich mit der Thematik von Software im Netz und zeigt dabei am
Beispiel eines Multiplayer-Spiels auf, mithilfe welcher Technologien momentan eine
Applikation im Web-Kontext umgesetzt werden kann. Hierbei liegt der Fokus auf
der Kommunikation in Echtzeit - es werden jedoch auch andere Bereiche der Server
und Clients behandelt. Die Entwicklung eines Spiels hat dabei mehrere Vorteile:
Zum einen ist es bei einem Spiel einfach, Probanden zu finden und durch diese
wertvolle Verbesserungsvorschläge zu erhalten. Dies liegt vor allem daran, dass
die Zielgruppe bei einem Spiel sehr breit gestreut ist und auch viele technikaffine
Personen einschließt. Zum anderen gibt es nur sehr wenige andere Anwendungsfälle, in denen der Benutzer eine Verzögerung als so störend wahrnimmt, wie bei
einem Spiel, in dem teilweise Millisekunden über den Ausgang einer Spielrunde
entscheiden können. Dadurch ergibt sich eine optimale Umgebung, in der man eine
verteilte Applikation mit dem Fokus auf die Echtzeit implementieren kann.
Hierzu wird in dieser Arbeit zuerst die Spielmechanik beschrieben, welche in die
Bereiche Einzelspieler- und Mehrspielermodus aufteilt ist. Danach zeigt die Entwicklung der Technologien für die Internetkommunikation auf, wie die beiden heutigen
Ansätze für die Echtzeitkommunikation entstanden sind und welche älteren Technologien als Fallback genutzt werden können. Als nächstes werden die durch das
Spiel gestellten Anforderungen gesammelt und analysiert, bevor sie im nächsten
Schritt zum Konzept umgewandelt werden. Dieses Kapitel ist zuerst in die Bereiche
Netzwerk, Server und Client aufgeteilt. Danach werden die dabei entstandenen
Informationen verbunden, sodass ein Gesamtkonstrukt entsteht, an dem dann gezeigt wird, wie die einzelnen Komponenten miteinander kommunizieren können.
Basierend auf diesem Konzept werden zuerst die verschiedenen Technologien ausgewählt, welche für die Implementierung genutzt werden. Danach folgen kurze
Übersichten über die Umsetzungen der einzelnen Bereiche der Applikation. Wie
sich die Technologien bewährt haben und was in Zukunft noch bevorsteht, wird im
Schlussteil dargestellt.
Kapitel 2
Spielkonzept
In diesem Kapitel wird die Spielmechanik der Applikation beschrieben, welche
in dieser Arbeit beispielhaft umgesetzt wird. Dazu wird zunächst erläutert, wie
dieses Spiel aussieht und welche Möglichkeiten der Benutzer hat. Diese Erläuterung
bezieht sich zunächst auf die grundlegende Spielmechanik, wie sie bereits in anderen
Spielen im Einzelspieler-Modus auftaucht. Danach wird dieses Konzept so erweitert,
dass sich daraus ein Mehrspieler-Modus ergibt, in dem mehrere Benutzer parallel
zusammen und gegeneinander spielen können.
2.1
Grundlegende Spielmechanik
Die Vorlage für das Spiel wurde im Juni 1994 vom japanischen Spieleentwickler
Taito Corporation als „Puzzle Bobble“ veröffentlicht [1]. Als Grundlage für das Spiel
dient ein rechteckiges Spielfeld, welches in Abbildung 2.1 dargestellt ist. Auf diesem
werden vom oberen Spielfeldrand aus mehrere Reihen von verschiedenfarbigen
Kugeln in das Spielfeld hineingeschoben. Am unteren Rand befindet sich mittig eine
weitere Kugel, welche durch einen Klick auf das Spielfeld auf die oberen Kugeln
geschossen werden kann. Der Spieler besitzt einen Kugelvorrat mit maximal fünf
Kugeln, welche in einem Zusatzfeld unter dem Spielfeld dargestellt werden.
Beim Abschuss einer Kugel fliegt diese immer in Richtung des Punktes los, auf den
geklickt wurde. Gelangt die Kugel im Flug entweder an den linken oder rechten
Spielfeldrand, so prallt diese ab und ändert die horizontale Richtung. Das geschieht
solange, bis die Kugel auf eine andere Kugel oder auf den oberen Spielfeldrand trifft.
In beiden Fällen wird sie auf den nächsten freien Platz im Raster gesetzt und ist nun
Teil der Kugelreihen.
Danach wird überprüft, ob die gesetzte Kugel an eine gleichfarbige Kugelgruppe an7
8
Spielkonzept
Abbildung 2.1: Das Spielfeld mit allen relevanten Objekten
grenzt, sodass sich nun eine Gruppe von mindestens drei gleichfarbigen Kugeln bildet. Wenn dies der Fall ist, werden alle Kugeln dieser Gruppe vom Spielfeld entfernt.
Dabei kann es passieren, dass Kugeln ohne Verbindung zum oberen Spielfeldrand
zurückbleiben und somit auf dem Spielfeld schweben. Diese Kugeln werden dann
ebenfalls entfernt, wie in Abbildung 2.2 zu sehen ist.
Abbildung 2.2: Zunächst wird die Gruppe der gelben Kugeln entfernt, wodurch
die drei blauen Kugeln schweben. Diese werden daher als nächstes vom Spielfeld
genommen
Nachdem das Spielfeld, falls benötigt, aufgeräumt wurde, erhält der Benutzer die
nächste Kugel am unteren Spielfeldrand und kann mit dieser das Spiel fortsetzen.
Hat der Nutzer mit seiner zuletzt abgeschossenen Kugel keine Gruppe gebildet,
so wird eine Kugel vom Vorrat entfernt. Wird die letzte Kugel vom Kugelvorrat
verschossen, so wird vom oberen Spielfeldrand eine neue Reihe von Kugeln nachgeschoben. Dann werden mögliche schwebende Kugeln entfernt und der Kugelvorrat
Spielkonzept
9
wird wieder mit fünf neuen Kugeln aufgefüllt.
Während des Spiels ist es möglich, dass man eine Kugelfarbe komplett vom Spielfeld
entfernt, wo-durch diese Farbe nicht mehr neu produziert wird. Dadurch nehmen
nachfolgende Kugeln sowohl am unteren Spielfeldrand als auch in den Reihen
diese Farbe nicht mehr an. Das sorgt dafür, dass das Spielfeld übersichtlicher wird
und man sich dem Ziel des Spiels schrittweise nähern kann. Jedoch hat das auch
Auswirkungen auf den Spielverlauf, da für jede entfernte Kugelfarbe eine weitere
Reihe beim Verbrauchen des Vorrats hinzugefügt wird.
Ziel des Spiels ist es, alle Kugelreihen vom Spielfeld zu entfernen. Dabei erhält man
je nach benötigter Zeit oder verschossenen Kugeln unterschiedlich viele Punkte und
kann sich so mit anderen Spielern messen. Erreicht man hingegen mit einer Kugel
den unteren Rand des Spielfelds, so hat man das Spiel verloren und erhält keine
Punkte.
2.2
Spielmechanik im Mehrspieler-Modus
Das Spiel soll nun so erweitert werden, dass mehrere Benutzer zusammen und
gegeneinander spielen können. Hierzu verbindet der Benutzer sich zunächst mit
einem Server und befindet sich danach im Hauptmenü des Spiels, welches in Abbildung 2.3 dargestellt ist. Hier kann er seinen Benutzernamen sehen und ändern und
bekommt eine Liste der aktuellen Spiellobbys dargestellt. Aus dieser Liste kann er
sich einen Eintrag aussuchen und beitreten oder eine eigene Spiellobby erstellen.
Abbildung 2.3: Im Hauptmenü sieht der Benutzer seinen Namen und die aktuellen
Spiellobbys
10
Spielkonzept
Sobald der Benutzer eine Spiellobby auswählt oder eine eigene Lobby erstellt, gelangt
er in das Lobby-Menü, welches in Abbildung 2.4 zu erkennen ist. Hier sieht der
Benutzer zunächst den Namen der Lobby und eine Liste der aktuellen Teilnehmer.
Unter dieser Liste befindet sich eine Schaltfläche um wieder ins Hauptmenü zu
gelangen. Ist der Benutzer der Ersteller der Lobby, so kann er andere Benutzer aus
der Lobby entfernen oder auch das Spiel starten.
Abbildung 2.4: In der Spiellobby hat der Ersteller die Möglichkeit, andere Spieler zu
entfernen und das Spiel zu starten
Im Spiel hat jeder Benutzer ein eigenes Spielfeld und sieht zusätzlich noch eine
verkleinerte Darstellung der anderen Spielfelder. Dadurch ist es bereits möglich, das
Spiel parallel zu nutzen und zu sehen, was auf den anderen Spielfeldern passiert –
man kann also parallel spielen. Für die Interaktion zwischen den Spielern sorgen
die Extras, welche bei besonders vielen entfernten Kugeln auf dem eigenen Spielfeld
erscheinen. Ein Extra ist eine besondere Kugel ohne Farbe, welche beim Entfernen
einer angrenzenden Gruppe eingesammelt wird und in das Inventar des aktuellen
Benutzers übergeht. Der Benutzer kann dann die einzelnen Extras sequentiell in
der Reihenfolge des Einsammelns nutzen und diese entweder auf sich selbst oder
andere Teilnehmer des Spiels anwenden. Beim Anwenden verursacht jedes Extra
eine Aktion, welche dem jeweiligen Ziel schaden oder helfen kann.
Entfernt der Benutzer mit einem Schuss zehn gleichfarbige oder aber fünf schwebende Kugeln, so verwandelt sich eine zufällige andere Kugel auf dem Spielfeld in
ein Extra, was durch einen Buchstaben auf der nun farblosen Kugel dargestellt wird.
Die verschiedenen Typen der Extras sind in Tabelle 2.1 zu erkennen. Sammelt der
Benutzer das Extra durch das Entfernen benachbarter Kugeln ein, so landet es in
seinem Inventar, wobei neue Extras immer hinten angefügt werden. Der Benutzer
Spielkonzept
11
kann nun das erste Extra durch Drücken einer Spielertaste (1-9) anwenden und
so beispielsweise eine Reihe von seinem Spielfeld entfernen und sich helfen oder
einem Mitspieler eine Reihe hinzufügen und diesen behindern. Dabei unterscheiden
sich die einzelnen Extras in der Art, wie sie wirken. Manche Extras wirken direkt,
während andere eine gewisse Wirkdauer haben, über welche der jeweilige Effekt
anhält. Letztere sind mit der jeweiligen Dauer in der Tabelle vermerkt.
Symbol
A
R
C
F
S
Bedeutung
Reihe hinzufügen
Reihe entfernen
Steuerung verdrehen
Schnellere Kugel
Langsamere Kugel
Effekt
Neue Kugelreihe von oben
Oberste Kugelreihe wird entfernt
Steuerung 10s lang invertiert
Kugel 10s lang doppelt so schnell
Kugel 10s lang halb so schnell
Tabelle 2.1: Die einzelnen Extras mit ihren jeweiligen Effekten
Der Oberfläche müssen nun weitere Elemente hinzugefügt werden, um so die neuen
Funktionalitäten darstellen zu können, wie in Abbildung 2.5 zu sehen ist. Neben
den anderen Spielfeldern und dem Inventar des Spielers werden außerdem noch
aktive Extras mit der verbleibenden Dauer angezeigt. Im abgebildeten Fall wurde die
Steuerung des Benutzers verdreht („C“-Extra) und die Kugelgeschwindigkeit erhöht.
Durch das zweite „C“-Extra wird die Steuerung wieder richtiggestellt, solange
beide Extras aktiv sind. Nach fünf Sekunden würde der Benutzer also wieder eine
verdrehte Steuerung für die verbleibenden drei Sekunden erhalten. Diese Regeln
bilden die Grundlage für die Applikation und werden in den folgenden Kapiteln
analysiert, erweitert und umgesetzt.
12
Spielkonzept
Abbildung 2.5: Die Oberfläche wurde um die Spielfelder der anderen Benutzer, das
Inventar und aktive Extras erweitert
Kapitel 3
Echtzeitkommunikation im Web
Benutzer wollen Anwendungen, welche ohne merkbare Verzögerung auf Eingaben
reagieren und passende Ausgaben liefern, weshalb das Arbeiten in Echtzeit eine
grundlegende Anforderung an viele Anwendungen ist. Sobald diese Anwendungen
über mehrere Systeme und Benutzer verteilt werden, muss auch die Kommunikation
zwischen den einzelnen Stellen so verzögerungsarm verlaufen, dass die einzelnen
Benutzer in ihrem Arbeitsfluss nicht behindert werden. Doch vor allem im WebUmfeld war die ursprüngliche Entwicklung nicht darauf ausgelegt, dass Benutzer
in Echtzeit miteinander kommunizieren können - ein Beispiel hierfür ist das in
dieser Arbeit thematisierte Mehrspieler-Spiel. Deshalb gab es in den letzten Jahren
vermehrt Technologien, welche diese Lücke schließen sollten. Diese Technologien
bilden die Grundlage für die Entwicklung von Echtzeit-Anwendungen im WebKontext und sollen deshalb aufgezeigt werden.
3.1
HTTP
Als Grundlage für die Kommunikation im Internet dient das Hypertext Transfer
Protocol (HTTP) derzeit in der Version 1.1. Auf Basis dieses Standards kommunizieren auch heutzutage noch Browser, Server und Web-Applikationen miteinander –
"HTTP is the common language of the modern global Internet."[8].
HTTP selbst ist ein Protokoll, welches eine zuverlässige Datenübertragung sicherstellt. Es baut auf dem bereits vorhandenen Protokoll TCP auf, welches wiederum
über IP und die darunterliegende Netzzugriff-Schicht eine Verbindung herstellen.
Durch die Nutzung dieser Elemente entsteht eine zuverlässige Verbindung, über
welche die Applikationen kommunizieren können.
13
14
Echtzeitkommunikation im Web
Abbildung 3.1: Die Kommunikation zwischen dem Client und dem Server mittels
HTTP per Request und Response
Für die Kommunikation schickt der Client eine Anfrage (Request) mit einer Methode, einem Ziel und der genutzten Protokoll-Version, wie in Abbildung 3.1 zu
dargestellt ist. Diese Daten werden als Befehl (Command) in der ersten Zeile der
Anfrage gesammelt. Danach folgt der Kopf (Header) der Anfrage, welcher weitere
Informationen enthalten kann: was für ein Format wird erwartet, wer schickt die
Anfrage und welche Sprache wird bevorzugt.
Der Server erhält diesen Request, verarbeitet ihn passend und sendet eine Antwort
(Response) zurück. Je nach Art des Servers kann dabei die Antwort aus einer statischen Datei gelesen oder auch dynamisch erstellt werden. Die Antwort enthält
in der obersten Zeile zunächst den Status der Antwort ebenfalls mit der genutzten
Protokoll-Version sowie mit einem Status-Code und der passenden Status-Nachricht.
Danach folgt auch in der Antwort ein Header, in dem weitere Informationen zusammengefasst sind. Mögliche Inhalte des Headers sind das Datum, der Typ und
die Länge der Antwort, sowie Informationen über den Server. Zusätzlich zu diesen
beiden Bereichen gibt es noch den eigentlichen Inhalt der Antwort: den Body. Hier
befindet sich beispielsweise bei der Abfrage einer Internetseite der HTML-Quelltext.
3.2
AJAX
Wie man in Abbildung 3.1 erkennen kann, muss zunächst der Client eine Anfrage
an den Server schicken, um eine Antwort zu erhalten. Das bedeutet auch, dass
nur der Client für eine Aktualisierung des Inhaltes sorgen kann, selbst wenn sich
Echtzeitkommunikation im Web
15
serverseitig etwas verändert hat. Dies führt natürlich zu einem Problem, weil viele
Web-Applikation mit Live-Daten arbeiten, welche sich ständig aktualisieren können
– so wird beispielsweise ein Live-Ticker für ein Sportevent ständig mit neuen Daten
versorgt, welche dann direkt beim Benutzer angezeigt werden sollen.
Eine Möglichkeit wäre das ständige neu Laden der gesamten Seite mit den aktuellen
Daten. Dies hat jedoch enorme Nachteile bei der Geschwindigkeit, da die gesamte
Datenmenge neu geladen werden muss – das schließt beispielsweise auch Bilder
mit ein, die sich nicht verändert haben. Ziel ist es jedoch, nur die Daten anzufordern,
die sich seit der letzten Aktualisierung verändert haben. Eine Möglichkeit hierfür
wird am 18. Februar 2005 von Jesse James Garrett vorgestellt und trägt das Akronym
AJAX („Asynchronous JavaScript and XML“).
Diese Technik ermöglicht es, nur bestimmte Daten asynchron vom Server anzufordern und im Client zu aktualisieren. Hierdurch ergibt sich eine neue Möglichkeit,
mit der Entwickler nur die Daten laden können, die wirklich aktualisiert werden
müssen. Dabei werden die einzelnen Anfragen wiederum vom Client initiiert, aber
ohne dass die Seite selbst neu geladen werden muss. Diese Anfrage wird dann ebenfalls über ein HTTP-Request an den Server gesendet, welcher dann eine Response
zurückschickt. Die Daten aus dem Body können dann im Client ausgelesen und die
Seite passend aktualisiert werden [7].
Durch dieses Vorgehen verringert sich die Menge der benötigten Daten in dem zuvor
beschriebenen Szenario (Live-Ticker) massiv, da nur die Informationen übertragen
werden, die sich geändert haben. Doch obwohl die Technik sehr innovativ war und
die Art der Web-Applikationen massiv erweitert hat, nutzt sie weiterhin HTTP als
Kommunikationsweg und ist deshalb auch an die Restriktionen dieses Protokolls
gebunden. So muss weiterhin der Client einen Request abschicken, um neue Daten
zu erhalten. Zwar funktioniert dies asynchron (der Client muss nicht aktiv auf den
Response warten), jedoch kann der Server die Antwort nicht direkt zum Client
durchleiten, weshalb der Client im Hintergrund den Status durch zyklisches Abfragen ermitteln muss („polling“). Somit ist AJAX keine optimale Lösung für Echtzeitanwendungen, auch wenn sie als etablierter Standard und Ausweich-Technologie
(„Fallback“) bis heute einen festen Platz in Web-Applikationen einnimmt.
Eine erweiterte Form von polling ist das „long polling“. Auch hier sendet der Client
eine Anfrage an den Server, die dieser verarbeitet und direkt beantworten kann.
Liegen die angeforderten Informationen noch nicht vor, schickt der Server jedoch
keine leere Nachricht zurück, wie es bei HTTP und polling üblich wäre, sondern
hält die Verbindung solange offen, bis die passenden Informationen zur Verfügung
stehen - erst danach wird eine Antwort zurückgeschickt. Das bedeutet zwar, dass
der Server ohne Verzögerung neue Informationen an den Client leiten kann, dafür
16
Echtzeitkommunikation im Web
aber sehr viele Ressourcen benötigt, da für jede offene Anfrage eine Verbindung
gehalten werden muss.
3.3
Websockets
Ein großes Problem bei HTTP und AJAX ist, dass für jeden Datenaustausch eine
Verbindung aufgebaut werden muss, über welche die beiden Nachrichten geschickt
werden. Diese Verbindung ist jedoch nur für ein einmaliges Austauschen der Nachrichten verfügbar und wird danach vom Server geschlossen. Zwar wurde das
Problem bereits im Update von HTTP 1.0 auf 1.1 adressiert, indem die Verbindung
aktiv gehalten werden kann, jedoch nur für die Daten einer einzigen Anfrage [6].
Will man mehrere unabhängige Daten anfordern, so müssen diese immer über eigene
Requests/Repsonses angefragt und geliefert werden. Das bedeutet auch, dass jedes
Mal alle Informationen im Header geschickt werden müssen, wodurch ein sehr
großer Overhead entsteht, welcher viele redundante und teilweise uninteressante
Informationen enthält. Vor allem bei kurzen Nachrichten (weniger als 32 Byte)
verursacht der Header ein Vielfaches der Datenmenge, die im Body enthalten ist.
Dadurch entsteht ein unnötiger Overhead, welcher besonders in modernen WebApplikationen eine starke Verlangsamung verursacht.
Diese Probleme der unzähligen Verbindungen und der redundanten Header werden
seit einigen Jahren von verschiedenen Entwicklerteams behandelt und resultierten
2011 in einem neuen Standard: dem Websocket Protokoll [5]. Wie in Abbildung 3.2
zu erkennen ist, benötigt dieses Protokoll zuerst einen Handshake, welcher vom
Client initiiert wird. Dafür sendet dieser eine Nachricht, welche komplett zu HTTP
kompatibel ist und somit auf dem gleichen Port laufen kann, an den Server und
übergibt einige Parameter für die Verbindung. Zunächst sind das, wie bei HTTP üblich, die angefragte Ressource und der Host. Hinzu kommt dann noch eine Anfrage
für die Verwendung des Websocket-Protokolls (Upgrade), das darauf aufbauende
eigene Protokoll und die gewünschte Protokoll-Version. Der Server kann dann diese
Daten wiederum verarbeiten und sendet, falls das Protokoll unterstützt wird, eine passende Nachricht mit dem Status-Code 101 und einigen Informationen zum
Upgrade.
Nach diesem Upgrade steht eine Verbindung mit einem Voll-Duplex zur Verfügung,
auf welcher sowohl der Client Daten an den Server senden, als auch der Server
ohne vorherige Anfragen vom Client direkt eine Nachricht an den Client zustellen
kann. Diese Socket-Verbindung sorgt dafür, dass Web-Applikationen ohne Anfrage
direkt Daten vom Server beziehen können. Außerdem wird die Verbindung solange
offen gehalten, bis eine Seite sie gezielt schließt. Das bedeutet auch, dass nach dem
Echtzeitkommunikation im Web
17
Abbildung 3.2: Der Handshake des Websocket Protokolls basiert auf HTTP Request
und Response
einmaligen Handshake und Upgrade keine weiteren Overheads mehr gesendet
werden müssen: alle Nachrichten enthalten nur die Daten, welche die Applikation
tatsächlich benötigt. Zwar können nach Anwendungsfall auch Informationen wie
Nachrichtentyp oder Topic mit in die Nachricht aufgenommen werden, jedoch sind
diese Angaben, im Gegensatz zum Header von HTTP, immer abhängig von der
Nachricht selbst und können zwischen einzelnen Nachrichten variieren.
Das Websocket Protokoll wird aufgrund dieser Vorteile heutzutage verstärkt in
Web-Applikationen genutzt und deshalb auch von sehr vielen Browsern und ServerTechnologien unterstützt. So gibt es für alle gängigen Programmiersprachen eigene
Bibliotheken, mit denen das Aufsetzen eines Servers erleichtert wird. Eine der verbreitetsten Umgebungen für Web-Applikationen ist dabei node.js auf Basis von
JavaScript, welches über verschiedenste Erweiterungen nicht nur als HTTP-Server
sondern auch als Websocket-Server bertrieben werden kann. Dabei reichen die
einzelnen Erweiterungen von einer einfachen Serverumgebung, welche nur das
Websocket Protokoll unterstützen und die einzelnen Nachrichten pur, also komplett ohne Overhead, verschicken, bis hin zu Lösungen für Server- und Clientseite.
Letztere liefern dabei auch Unterstützung für ältere Browser, wie ein Fallback auf
AJAX und Long-Polling. Eine bekannte Erweiterung ist socket.io1 , welche außer für
node.js2 auch für andere Programmierumgebungen zur Verfügung steht und das
Erstellen von Echtzeit-Applikation für das Web nochmals vereinfacht.
Mittlerweile hat sich das Websocket-Protokoll jedoch so weit verbreitet, dass fast
1 http://socket.io/
2 JavaScript
Bibliothek für Entwicklung von Netzwerk-Applikationen; http://nodejs.org/
18
Echtzeitkommunikation im Web
alle aktuellen Browser diese Verbindungsart unterstützen. Lediglich der Android
Standard Browser und Opera Mini verweigern in der aktuellen Version noch die Zusammenarbeit. Eine genauere Übersicht über die Unterstützung durch die einzelnen
Browser ist in Tabelle 3.1 zu erkennen.
IE
8.0
9.0
10.0
Firefox
21.0
22.0
23.0
Chrome
26.0
27.0
28.0
Safari
5.0
5.1
6.0
Opera Mini
Android
Blackberry
Opera Mobile
5.0
6.0
7.0
4.0
4.1
4.2
7.0
10.0
12.0
12.1
14.0
Opera
12.0
12.1
15.0
Chrome
Mobile
iOS Safari
4.2-4.3
5.0-5.1
6.0-6.1
Firefox
Android
28.0
23.0
Tabelle 3.1: Unterstützung des Websocket-Protokolls durch einzelne Browser (aktuelle Version zuletzt). Grün = unterstützt, gelb = teilweise unterstützt, rot = nicht
unterstützt [4]
3.4
WebRTC
Obwohl das Websocket Protokoll bereits viele Lösungen für die Echtzeitkommunikation im Web-Kontext bereitstellt, eine Vielzahl von Verbesserungen gegenüber
anderen Technologien liefert und stetig ausgebaut wird, gibt es bereits neue Ansätze,
mit denen man die Echtzeitkommunikation im Web-Kontext verbessern will. Eine
Arbeitsgruppe um das World Wide Web Consortium (W3C) und die Internet Engineering Task Force (IETF) arbeitet derzeit an Web Real-Time Communication, kurz
WebRTC. Nach dieser Technologie soll, im Gegensatz zu Websockets und HTTP, die
Web-Applikation nicht über einen Server, sondern direkt zwischen zwei oder mehr
Browsern kommunizieren. Die dadurch geschaffenen Peer-to-Peer-Verbindungen
sollen dabei um ein Vielfaches schneller sein als der Umweg über einen Server [10].
Eine Möglichkeit für die Verbindung ist ein Dreieck, bestehend aus einem Server,
welcher für die Vermittlung verantwortlich ist, und zwei Clients, welche dann einen
oder mehrere Daten-Streams untereinander aufbauen und darüber kommunizieren
können, wie in Abbildung 3.3 zu sehen ist.
Ziel der Entwickler ist es außerdem, nicht nur einfache Daten verschicken zu können,
sondern ebenfalls eine API zum Auslesen von Medienquellen (Kamera, Mikrofon)
zur Verfügung zu stellen, mit deren Hilfe Entwickler Audio und Video auslesen und
über eigene Streams versenden können. So verwundert es auch nicht, dass die erste
Echtzeitkommunikation im Web
19
Abbildung 3.3: Durch WebRTC wird die Verbindung von dem Server nur vermittelt,
die Kommunikation erfolgt direkt zwischen den beiden Clients mit Hilfe von DatenStreams [10]
große Demonstration der Technologie einen Live-Video-Chat á la Skype3 zwischen
zwei Personen zeigt, wobei die Besonderheit darin liegt, dass alles direkt über den
Browser zur Verfügung gestellt wird. Ziel der Entwickler ist es außerdem, einen
Standard zu schaffen, welcher über verschiedene Client- und Servertypen hinweg
sogar einfache Telefonanlagen mit Computern verbinden können soll.
Jedoch ist das größte Problem an der Technologie heutzutage, dass sie sich noch in
der Entwicklung befindet und frühestens Ende 2013 in einer ersten Version offiziell
erscheint. Bis dahin gibt es nur Empfehlungen für die Nutzung der verschiedenen
Komponenten, welche beispielsweise zwischen den einzelnen Browsern sehr unterschiedlich sind und daher dafür sorgen, dass Mozilla Firefox und Google Chrome
unterschiedliche Implementierungen benötigen, um miteinander kommunizieren
zu können. Allgemein gibt es derzeit nur wenige unterstützende Browser: Mozilla
Firefox ab der aktuellen Version 23.0 und Google Chrome ab Version 25.0, außerdem
unterstützt die Beta des mobilen Browsers Google Chrome in der Version 29.0 bereits
als erstes WebRTC auf mobilen Geräten. Dennoch ist es noch ein weiter Weg, bis
eine Abdeckung erreicht wird, wie sie das Websocket-Protokoll bereits hat.
Ein weiterer aktueller Nachteil ist, dass der Verbindungsaufbau zwischen zwei Clients noch sehr viele Anpassungen benötigt, damit es zu keinen Problemen kommt.
Für etwas Abhilfe sorgt hier eine erste Bibliothek: PeerJS4 . Diese Bibliothek unter3 http://www.skype.com/de/
4 http://www.peerjs.com/
20
Echtzeitkommunikation im Web
stützt die Vermittlung der Verbindung und fügt die APIs von Mozilla Firefox und
Google Chrome so zusammen, dass man für beide Browser einheitliche Befehle
nutzen kann, wodurch Entwickler stark entlastet werden.
Dennoch bleibt festzuhalten, dass diese Technologie im aktuellen Zustand noch zu
große Probleme aufweist und somit nur bedingt genutzt werden kann. Jedoch soll
diese Technologie trotzdem für diese Arbeit berücksichtigt und getestet werden,
da die zukünftigen Aussichten und die Möglichkeiten, welche diese Technologie
liefern könnte, beeindruckend sind und einen weiteren, sehr wichtigen Schritt in
der Entwicklung des Web darstellen können.
3.5
Vergleich Websockets, Socket.IO & WebRTC
Nachdem geklärt ist, welche Technologien aktuell zur Umsetzung von Echtzeitkommunikationen im Web zur Verfügung stehen, soll nun noch überprüft werden,
welche Vorteile und Nachteile die jeweiligen Alternativen bieten. Zum einen soll
vor allem die Latenz der Verbindungsarten durch die Round-Trip-Time ermittelt
werden, da dies der wichtigste Faktor für die verzögerungsfreie Kommunikation ist.
Die Round-Trip-Time (RTT) misst dabei die Zeit, welche ein Paket vom Client zum
Server und wieder zurück braucht. Jedoch spielen auch die Übertragungsgeschwindigkeit, die Zuverlässigkeit und die Implementierung eine wichtige Rolle.
Websockets sind mittlerweile sehr weit verbreitet und haben dadurch, wie bereits
erwähnt, sehr viele verschiedene Implementierungen zu bieten, welche verschiedene Szenarien abdecken. Eine einfache Bibliothek stellt dabei „WebSocket Node“
von Brian McKelvey bereit, welches serverseitig die aktuellen Websocket Protokolle
umsetzt [11]. Eine mächtigere Alternative stellt Socket.IO dar, welches neben dem
eigentlichen Websocket Protokoll auch andere Technologien wie AJAX als Fallback
nutzt und diese sowohl auf Server- als auch auf Client-Seite passend implementiert
zur Verfügung stellt[13]. Dieses Komplettpaket bietet viele Erweiterungen im Vergleich zum eigentlichen Websocket- Protokoll und sichert vor allem die Verwendung
in älteren Browsern ab, welche noch keine Websockets unterstützen. Bei WebRTC
hingegen gibt es nur die bereits erwähnte Bibliothek „PeerJS“, welche, bedingt
durch den Entwicklungsstand von WebRTC an sich, nur wenige Funktionalitäten
bereitstellt [3].
Die Implementierung bei Websockets ist im Allgemeinen durch die verschiedenen Bibliotheken schnell abgeschlossen. Bei beiden Implementierungen basieren
serverseitig die Websocket-Verbindungen zunächst auf einem HTTP-Server, welcher standardmäßig von node.js unterstützt wird. Auf diesem Server wird dann ein
Websocket-Server aufgesetzt, welcher auf dem gleichen Port lauscht und eingehende
Echtzeitkommunikation im Web
21
Ereignisse an die jeweiligen Listener weiterleitet. Dafür warten beide Implementierungen auf eingehende Verbindungen und liefern im passenden Listener den
Socket mit, welcher dann gespeichert und genutzt werden kann. Auf diesem Socket
werden dann weitere Listener angemeldet, welche dann ein Verbindungsende abfangen und ankommende Nachrichten je nach Nachrichtentyp verwalten können.
Das Verschicken von Nachrichten wird ebenfalls über die einzelnen Sockets ausgeführt, indem die Senden-Methode für jeden Socket aufgerufen wird. Clientseitig
setzt Websocket Node auf die Standardimplementierung von Websockets, während
SocketIO eine eigene Bibliothek mitliefert, die neben den Fallbacks auch einige
Vereinfachungen für Websockets bereithält. Dennoch arbeiten beide Implementierungen nach demselben Prinzip: Es wird eine Verbindung zu einem bestimmten
Server mit Adresse und Port geöffnet, welche dann ebenfalls über vorgegebene
Listener Ereignisse abfangen und verarbeiten kann. Auch das Versenden von Nachrichten ist mit einem Methodenaufruf auf den Socket zu erledigen. Beide Seiten der
Verbindung sorgen dafür, dass die gesamte Kommunikation mit Websockets sehr
einfach zu implementieren ist.
Bei WebRTC mit PeerJS benötigt man ebenfalls zunächst einen Server, welcher die
Verbindung zwischen den beiden Clients austauscht. Dieser Server basiert ebenfalls
auf node.js und ist durch einen zweizeiligen Code aufgesetzt. Die Clients benötigen
dann die Adresse und den Port des Servers und verbinden sich mit diesen Daten
auf den Server. Wie bereits erwähnt, muss dabei ein Client einen Peer-Server mit
einer einmaligen ID erstellen, auf den sich der andere Client dann verbinden kann.
Hierbei fehlt aber ein Austausch der einzelnen IDs, wodurch man bereits vor dem
Verbindungsaufbau die IDs fest hinterlegen muss. Auch die Kommunikation über
die aufgebaute Peer-to-Peer-Verbindung ist in der aktuellen Version dürftig, da es
nur einen unverlässlichen Nachrichtentyp gibt. Insgesamt ist die Struktur noch sehr
starr, was aber zweifellos an dem aktuellen Entwicklungsstand von WebRTC liegt.
Neben der Implementierung und den dabei entstehenden Problemen ist vor allem
die Übertragungsgeschwindigkeit von essentiellem Interesse, da diese maßgeblich
dafür verantwortlich ist, ob die Applikation in Echtzeit abläuft. Zwar spielen auch
Faktoren wie die Implementierung der eigentlichen Spiele-Logik eine große Rolle,
jedoch ist dies vom eigentlichen Netzwerk-Bereich abgekapselt und stellt eine
eigene Thematik dar. Für die Untersuchung der Geschwindigkeit soll also nur die
Übertragungsgeschwindigkeit erfolgen, wobei hier die RTT entscheidend ist.
Für diesen Test wird eine Nachricht vom Client an den Server gesendet und von
diesem sofort wieder zurückgeschickt - ein solcher Server wird auch Echo-Server
genannt. Bei WebRTC dient ein Client als Server und schickt ebenfalls einkommende
Nachrichten direkt wieder zurück. Damit man dabei feststellen kann, wie lange die
Nachricht unterwegs war, beginnt jede Nachricht mit einem Zeitstempel mit der
22
Echtzeitkommunikation im Web
aktuellen UNIX-Systemzeit, also der Zeit seit dem 1. Januar 1970 00:00 Uhr UTC.
Dieser Zeitstempel liefert eine auf die Millisekunde genaue Zeitangabe, welche
dann vom Client wieder gelesen und mit dem aktuellen Zeitstempel verrechnet
werden kann. Daraus ergibt sich dann die RTT für diese Nachricht. Um außerdem
eine bessere Übersicht über die Übertragungsgeschwindigkeit abhängig von der
Nachrichtengröße zu erhalten, variiert die Nachrichtengröße von 25 bis 214 Byte.
Pro Nachrichtengröße werden dabei jeweils 1.000 Nachrichten verschickt und zum
Schluss gemittelt, um so eine bessere Aussagekraft zu erzielen. Außerdem werden
die Tests sowohl im Intranet über WLAN und Kabel durchgeführt, als auch über
einen Server im Internet – auch hier wurden die Werte gemittelt. Insgesamt wurden
dreimal drei Tests mit 10 Runden und 1.000 Schritten durchgeführt.
Die gesammelten Ergebnisse sind im Abbildung 3.4 zu erkennen. Dabei fällt auf,
dass bei kleineren Nachrichtengrößen sowohl die pure Implementierung der Websockets, wie auch SocketIO ähnliche Laufzeiten haben. Bei größeren Datenmengen
hingegen benötigt SocketIO etwas mehr Zeit als Websockets selbst. Dies könnte
sowohl durch die komplexeren Serverstrukturen als auch durch den Overhead
entstehen, den SocketIO anhängt. Dennoch sind beide Technologien derzeit deutlich
schneller als die Datenübertragung mittels PeerJS basierend auf WebRTC. Hinzu
kommt, dass PeerJS Probleme mit Nachrichten ab 512 Byte hat und im durchgeführten Test abstürzt. Aus diesem Grund sind nur die Messwerte bis einschließlich 512
Byte gelistet.
Abbildung 3.4: Untersuchungen der Round-Trip-Time (RTT) mit verschiedene Nachrichtengrößen für Websockets, SocketIO und WebRTC mit PeerJS
Echtzeitkommunikation im Web
23
Der deutliche Geschwindigkeitsunterschied zwischen Websockets und WebRTC
könnte dadurch erklärt werden, dass WebRTC in der aktuellen Version noch keine
Kommunikation per TCP beherrscht und somit auf UDP zurückgreifen muss. Damit jedoch keine Nachrichten verloren gehen, wird die Zustellung nicht von der
Transport- sondern von der Anwendungsschicht, in diesem Fall WebRTC, sichergestellt. Dies kann zu längeren Übertragungs- und Verarbeitungszeiten führen.
3.6
Zusammenfassung
Zum Zeitpunkt der Untersuchungen war klar zu erkennen, dass WebRTC zwar ein
sehr großes Ziel verfolgt, dieses jedoch noch lange nicht erreicht hat. Die Umsetzung der Audio- und Video-Streams ist bereits sehr gut umgesetzt und funktioniert
sowohl bei Mozilla Firefox wie auch bei Google Chrome in einer sehr guten Qualität
und Geschwindigkeit – Medien-Streams können also bereits auf die neue Technologie setzen. Die unfertigen Standards bei den anderen Datenverbindungen sorgen
jedoch dafür, dass die Verbindungsgeschwindigkeit von WebRTC derzeit noch klar
von Websockets geschlagen wird. Auch führen die offenen Richtlinien dazu, dass
man Applikation häufig umschreiben und neu gestalten muss.
Das Websocket-Protokoll hatte bereits viel Zeit, sich zu etablieren und hat das
auch getan. Es gibt eine Vielzahl von verschiedenen Implementierungen, welche
je nach Anwendungsfall mit immer mehr Funktionalitäten aufwarten. Auch das
Management der Verbindungen und die eigentliche Kommunikation sind viel ausgereifter. Deshalb sind Websockets zum aktuellen Stand der klare Favorit, wenn es
um Echtzeitkommunikation im Web geht.
24
Echtzeitkommunikation im Web
Kapitel 4
Anforderungsanalyse
Um von der Spielmechanik zur Umsetzung zu gelangen, müssen die einzelnen Ideen
und Regeln zusammengefasst und analysiert werden. Dazu dient die Anforderungsanalyse, welche als eine erste Abstraktion der Spielidee die spätere Implementierung
vorbereiten soll. Hierzu werden zunächst die möglichen Benutzerinteraktionen und
deren Folgen ausgewertet und zusammengefasst. Danach werden daraus mögliche
Spielelemente konstruiert, welche eine Grundlage für die späteren Objekte in der
Implementierung bilden. Danach soll auch die benötige Infrastruktur für die Kommunikation und deren Schnittstellen behandelt werden. Zuletzt sollen auch noch
die nicht-funktionalen Anforderungen bezüglich der Kommunikation in Echtzeit
und der Kompatibilität behandelt werden.
4.1
Benutzerinteraktionen
Beim Auswerten des Spielkonzeptes fällt auf, dass der Benutzer sich zunächst in drei
verschiedenen Bereichen bewegen kann. Direkt nach dem Starten der Applikation
gelangt er in das Hauptmenü und hat hier die Interaktionsmöglichkeiten aus Abbildung 4.1 zur Verfügung. Der Benutzer kann hier seinen Benutzernamen wählen,
welcher dann im weiteren Spielverlauf für ihn genutzt wird. Diese Möglichkeit hat
er jederzeit im Hauptmenü. Des Weiteren kann der Benutzer alle aktiven Spiellobbys
sehen, welche vom System geladen werden müssen. Um immer aktuelle Daten zur
Verfügung zu haben, wird die Lobby-Liste immer automatisch aktualisiert, wenn
es eine Änderung bei den Spiellobbys im System gibt. Aus dieser Liste kann der
Benutzer sich dann ein Element aussuchen und dieser Lobby beitreten. Er gelangt
dann als einfacher Benutzer in die Spiellobby. Jedoch kann er auch eine eigene Spiellobby erstellen und gelangt direkt in die neu erstellte Lobby – dieses Mal jedoch als
Lobby-Ersteller.
25
26
Anforderungsanalyse
Abbildung 4.1: Im Hauptmenü können die Benutzer ihren Namen festlegen und in
eine Spiellobby wechseln
Die Spiellobby ist der zweite Bereich der Applikation, in dem alle Benutzer gesammelt werden, bevor das eigentliche Spiel beginnt. Wie bereits im Abschnitt 2.2
beschrieben und in Abbildung 4.2 zu erkennen ist, gibt es zwei Arten von Benutzern,
wobei der Lobby-Ersteller einige Privilegien im Vergleich zum normalen Benutzer
hat. Zunächst können beide Benutzertypen die Spiellobby wieder verlassen und
gelangen dann ins Hauptmenü. Verlässt jedoch der Ersteller seine eigene Lobby,
so wird diese geschlossen und alle Benutzer werden in das Hauptmenü geleitet.
Um einen Überblick über die bereits verbundenen Teilnehmer zu haben, können
alle Benutzer diese in einer Liste sehen. Dabei hat der Ersteller der Lobby hier die
Möglichkeit, Benutzer zu entfernen, wodurch diese wieder im Hauptmenü landen.
Ist der Ersteller mit der Liste der Teilnehmer zufrieden, kann er das Spiel starten
und alle Benutzer dieser Lobby gelangen zum Spiel.
Abbildung 4.2: Die Spiellobby stellt je nach Benutzertyp verschiedene Möglichkeiten
bereit und kann entweder in Richtung Hauptmenü verlassen werden oder ins Spiel
übergehen
Anforderungsanalyse
27
Das Spiel selbst lässt sich wiederum in drei Handlungsstränge unterteilen, welche in
Abbildung 4.3 zu sehen sind. Zunächst können die Benutzer Kugeln verschießen
und somit bestimmte Aktionen auslösen und Extras sammeln. Dann haben sie die
Möglichkeit die gesammelten Extras einzusetzen und somit das Spiel zu beeinflussen. Dies führt letztendlich dazu, dass Benutzer verlieren können und am Ende nur
noch ein Gewinner übrig bleibt.
Abbildung 4.3: Das Spiel besteht aus drei Bereichen: Abschießen der Kugel, Benutzen
von Extras und Spielende
Wie bereits in Kapitel 2.1 beschrieben, besteht der eigentliche Spielablauf aus dem
Abschießen und Abbauen von Kugeln. Dies ist in Abbildung 4.4 zu erkennen. Der
Benutzer kann dafür zunächst eine Kugel in das Spielfeld abschießen, welche dann
in die Richtung des Mauszeigers losfliegt und solange an den Banden abprallt,
bis sie an eine Kugelgruppe oder den oberen Spielfeldrand andockt. Wenn das
geschieht, wird zunächst überprüft, ob das Spielfeld überfüllt ist, die Kugel also
unterhalb der 15. Kugelreihe angedockt ist. Falls dies der Fall sein sollte, hat der
Spieler das aktuelle Spiel verloren. Andernfalls wird die Kugelgruppe, an welche
die neue Kugel angedockt ist, überprüft. Befinden sich hier mindestens noch zwei
andere Kugeln mit der gleichen Farbe, wird diese gesamte Kugelgruppe entfernt.
Ebenfalls werden alle an diese Kugelgruppe angrenzenden Extras entfernt und dem
Inventar angefügt. Werden dabei mehr als fünf Kugeln entfernt, erscheint auf dem
Spielfeld ein neues Extra. Falls jedoch keine Kugelgruppe abgebaut wird, muss eine
Kugel aus dem Kugelvorrat entfernt werden. Ist dieser Vorrat aufgebraucht, wird
außerdem eine neue Reihe von Kugeln angefügt. Hierbei kann es ebenfalls passieren,
dass das Spielfeld überfüllt wird und der Spieler verliert. Bleibt das Spielfeld aber
innerhalb der 15 vorhandenen Zeilen, so wird der Kugelvorrat wieder aufgefüllt.
Falls das Spiel nicht vorbei ist, erhält der Spieler eine neue Kugel und kann mit
dieser weiterspielen.
28
Anforderungsanalyse
Abbildung 4.4: Das Abschießen einer Kugel kann viele verschiedene Aktionen
auslösen
Anforderungsanalyse
29
Hat der Benutzer durch das Abbauen von Kugelgruppen Extras in seinem Inventar
gesammelt, kann er diese Extras benutzen. Dazu stehen ihm die Möglichkeiten
aus Abbildung 4.5 zur Verfügung. Zunächst kann der Benutzer zu jeder Zeit sein
Inventar mit den aktuellen Extras sehen und somit erkennen, welches Extra er
als nächstes nutzen kann. Dieses nächste Extra kann er dann auf sich selbst oder
einen anderen Mitspieler anwenden. Das hat zur Folge, dass das benutzte Extra
aus dem Inventar entfernt wird und sich die Anzeige aktualisiert. Extras mit einer
Wirkdauer werden dem jeweiligen Benutzer dann ebenfalls angezeigt, um den
Benutzer darüber zu informieren. Damit die einzelnen Spieler eine Vorstellung
davon haben, wie der Spielstand bei den anderen Mitspielern aussieht, können sie
neben ihrem eigenen Spielfeld auch die Spielfelder der anderen Teilnehmer sehen.
Abbildung 4.5: Der Spieler kann sein Inventar sehen und Extras daraus benutzen.
Außerdem hat er auch eine Übersicht über seine aktuell aktiven Extras und alle
Spielfelder
Bei der Benutzung von Extras kann es auch dazu kommen, dass das Spielfeld eines
Spielers überfüllt wird. In diesem Fall hat der Spieler, wie auch beim Überfüllen
des Feldes durch eine eigene Aktion, das Spiel verloren. In beiden Fällen wird das
Spielfeld geräumt und der Benutzer kann das Spiel nur noch verlassen. Sobald der
vorletzte Mitspieler sein Spielfeld überfüllt, gewinnt der verbleibende Spieler das
aktuelle Spiel.
Außerdem steht es jedem Mitspieler zu jedem Zeitpunkt frei, das Spiel zu verlassen.
Hier werden alle Spieler, also auch der frühere Lobby-Ersteller, gleichbehandelt:
verlässt ein Spieler das Spiel, so gelangt er in das Hauptmenü und sein Spielfeld
wird geräumt, genauso als hätte er verloren.
Diese Use-Cases stellen die Interaktionen des Benutzers dar und schaffen einen Überblick darüber, welche Eingaben der Benutzer tätigen kann, zu welchen Reaktionen
es dann jeweils kommt und welche Zustände unterschieden werden können.
30
4.2
Anforderungsanalyse
Spielelemente
Die möglichen Interaktionen des Benutzers ergeben neben den Use-Cases auch eine
erste Übersicht über die Elemente und Objekte der Applikation. Diese Elemente
repräsentieren teilweise reale Objekte aus der natürlichen Welt, wie die einzelnen
Benutzer, aber auch Applikationselemente, wie beispielsweise Spiellobbys und
Kugeln. Da diese Objekte die spätere Applikationswelt darstellen, müssen sie aus der
Spielmechanik und den Use-Cases aus Kapitel 4.1 extrahiert und passend verbunden
werden.
Aus diesen Vorlagen entsteht das Domänenmodell in Abbildung 4.6, welches als
Grundlage für die späteren Objekte und Daten in der Applikation dienen soll. Im
Mittelpunkt der Applikation steht der Benutzer, welcher die anderen Elemente
manipulieren kann. Deshalb wird der Benutzer im Domänenmodell ebenfalls im
Zentrum positioniert und hat Verbindungen zu fast allen anderen Objekten. Er wird
repräsentiert durch einen Benutzernamen.
Im Hauptmenü sieht er zunächst die Spiellobby-Liste. Diese Liste besteht aus allen
aktuell aktiven Spiellobbys im System und steht allen Benutzern im Hauptmenü zur
Verfügung. Aus dieser Liste kann jeder Benutzer ein Element auswählen und diesem
beitreten, wodurch die Spiellobby einen weiteren Benutzer aufnimmt. Insgesamt
können sich bis zu neun Spieler in einer Lobby befinden, jeder Benutzer jedoch in
maximal einer gleichzeitig. Außerdem kann jeder Benutzer maximal eine Spiellobby
erstellen, wodurch dieser zu einem Ersteller wird. Dieser kann dann die Spiellobby
in ein Spiel überführen, wodurch alle Benutzer und der Ersteller der Lobby zu
Spielern werden.
Im Spiel erhält jeder Spieler weitere Elemente, welche nur im Spielkontext relevant
sind, weshalb der Spieler beim Verlassen des Spiels wieder zu einem Benutzer
wird. Jeder Spieler hat ein Spielfeld, welches je nach festgelegter Größe aus beliebig
vielen Kugeln bestehen kann. Diese Kugeln haben eine bestimmte Farbe, die für
das Abbauen von Kugelgruppen von Bedeutung ist. Jede Kugel kann von einem
Benutzer verschossen werden, jedoch können Kugeln auch direkt auf dem Spielfeld
auftauchen. Kugeln, die vom Benutzer verschossen werden können, werden im
Kugelvorrat gesammelt. Dabei besteht dieser aus maximal fünf Kugeln gleichzeitig,
wobei mit jedem Schuss ohne entfernte Kugelgruppe jeweils eine Kugel daraus
entfernt wird. Jeder Spieler hat genau einen Vorrat, welcher auch nur diesem Spieler
gehört und beliebig oft aufgefüllt werden kann.
Neben den normalen Kugeln besteht das Spielfeld auch aus beliebig vielen Extras,
welche einen bestimmten Typ haben, der mit einer Aktion verbunden ist. Dabei
kann jedes Extra entweder auf dem Spielfeld liegen, bei einem Benutzer aktiv sein
Anforderungsanalyse
31
oder sich im Inventar eines Benutzers befinden. Jeder Benutzer kann beliebig viele
aktive Extras haben, wobei jedoch jedes Extra auf nur einen Benutzer angewendet
werden kann. Dabei kann das Extra auch einmalig auf das Spielfeld des jeweiligen
Benutzers wirken und dieses manipulieren. Bevor das Extra angewandt werden
kann, befindet es sich im Inventar eines Spielers, welches auch nur von diesem
Spieler benutzt werden kann. Das Inventar selbst kann beliebig viele eingesammelte
Extras sequentiell halten und stellt dem Benutzer immer das älteste davon zur
Verfügung.
Abbildung 4.6: Objekte des Spiels als Domänenmodell mit den Spielbereichen oben,
darunter den Benutzertypen und den Spielelementen
32
Anforderungsanalyse
4.3
Echtzeit
Die Anforderungen an die Applikation beschränken sich natürlich nicht nur funktional. Es existieren auch einige nicht-funktionale Anforderungen, welche ebenfalls
beachtet werden müssen. Eine dieser Anforderungen steckt bereits im Namen der
Arbeit: die Echtzeit. Der Benutzer soll Eingaben tätigen können, die dann von der
Applikation verarbeitet werden und ein bestimmtes Feedback liefern. Diese drei
Bestandteile sollen dabei in Echtzeit ablaufen.
Zunächst muss dazu geklärt werden, was Echtzeit bedeutet. Hierzu gibt es verschiedene Ansichten und Definitionen, die sich teilweise überschneiden: Eine definierte
Aktion im System muss für den Betrachter in einer garantierten Zeit simultan zu
der gleichen Aktion in der realen Welt ablaufen. So muss beispielsweise ein Video
genauso schnell abgespielt werden, wie es auch aufgenommen wurde, um das
Kriterium der Echtzeit zu erfüllen. [9]
Für den hier vorgestellten Fall muss diese Definition jedoch erweitert werden, da
es keinen direkten Vergleich für die Kommunikation im Internet in der realen Welt
gibt. Die Zeitspanne muss also anders festgelegt werden. Hierfür muss man die
Erwartungen des Benutzers heranziehen. Dieser löst eine bestimmte Aktion aus und
erwartet ein sofortiges Feedback vom System. Die Zeitspanne für die Verarbeitung
und Ausgabe der Daten wird hierbei nach der Flimmerverschmelzungsfrequenz (FVF)
bestimmt. Diese besagt, dass ab einer bestimmten Wiederholfrequenz zwei aufeinanderfolgende Reize nicht mehr separat, sondern simultan wahrgenommen werden
– quasi verschmelzen [15]. Eine der bekanntesten Anwendungen ist dabei der Film:
Hier werden einzelne Bilder so schnell ausgewechselt, dass Auge und Gehirn diese
als Bewegung interpretieren.
Dieses Medium soll auch als Grundlage für die Festlegung der Zeitspanne dienen.
Basierend auf den aktuellen Standards bei Film und Fernsehen variiert die Anzahl der Bilder pro Sekunde zwischen 24 (Kino) und 30 (Fernsehen, NTSC1 ). Das
bedeutet, dass zwischen einer Benutzerinteraktion und der Anzeige des daraus resultierenden Ergebnisses maximal 33 Millisekunden vergehen dürfen. Dies schließt
im Netzwerkbetrieb auch die Dauer für die Übertragung einer Nachricht mit ein.
4.4
Kompatibilität
Eine weitere nicht-funktionale Anforderung der Applikation ist die Kompatibilität mit verschiedenen Systemen, auf welchen sie lauffähig sein soll. Es soll eine
Übertragbarkeit sichergestellt werden, die möglichst vielen Benutzern auf unter1 Fernsehsystem
für Farbübertragung nach dem „National Television Systems Committee“, USA
Anforderungsanalyse
33
schiedlichen Endsystemen die Möglichkeit liefert, die Applikation in vollem Umfang
parallel benutzen zu können. Dies soll durch die Nutzung von Technologien und
Frameworks sichergestellt werden, welche möglichst viele Systeme abdecken.
Da die Applikation in zwei Bereiche, Server und Client, aufgeteilt ist, müssen diese
beiden Systeme bezüglich der Kompatibilität separat betrachtet werden. Bei der
Betrachtung dieser Komponenten fällt auf, dass auf einem Server immer beliebig
viele Clients arbeiten können – es gibt also tendenziell mehr Clients als Server. Das
bedeutet auch, dass ein Server ein starres Element ist, welches auf einem bestimmten
System gestartet wird und auf diesem langfristig laufen kann, während die Clients
viel kurzlebiger sind und sich ständig austauschen können. Daraus folgt, dass der
Serverpart nicht auf jedem beliebigen System lauffähig sein muss - es soll ausreichen,
wenn er auf einem passend vorbereiteten System ausgeführt werden kann.
Die Clients hingegen müssen auf möglichst allen Systemen vollständig lauffähig
sein. Um dies zu erreichen, werden diese in eine Website eingebettet und somit von
einem Browser abgespielt. Durch die Nutzung von gängigen Webtechnologien wird
sichergestellt, dass alles standardkonformen Browser die Applikation in vollem Umfang darstellen können. Jedoch gibt es tendenziell auch Bestandteile der Applikation,
die noch nicht als verbreiteter Standard in allen Browsern identisch implementiert
wurden. Um testen zu können, welche Technologien auf möglichst vielen Systemen
lauffähig sind, muss zunächst analysiert werden, welche Browser aktuell wie stark
auf dem Markt vertreten sind. Dazu soll eine Statistik zur Browsernutzung vom
August 2013 herangezogen werden, welche in Abbildung 4.7 dargestellt wird. Die
hier präsentierten Daten basieren auf Seitenaufrufen von verschiedenen Benutzern
unterschiedlicher Webseiten ohne Beachtung der jeweiligen Platformen. Dabei findet auch keine Gewichtung statt. Insgesamt sind die Daten zwar nicht repräsentativ,
sie geben jedoch eine ausreichende Vorstellung von der aktuellen Marktverteilung.
Abbildung 4.7: Statistik zur Browsernutzung vom August 2013, Desktop & mobil
[14]
34
Anforderungsanalyse
Basierend auf diesen Daten müssen die Desktop-Browser Google Chrome ab Version
28, Microsoft Internet Explorer ab Version 8, Mozilla Firefox ab Version 22, Apple Safari
ab Version 6 und Opera ab Version 12.1 von den genutzten Technologien unterstützt
werden. Durch die immer stärker werdende Nutzung von Smartphones und Tablets
ergibt sich im Moment ein Marktanteil von 18% bei Seitenaufrufen [14]. Daher
müssen auch die Browser auf diesen mobilen Systemen berücksichtigt werden, dazu
gehören Apple Safari ab iOS 6, der Android Browser ab Android 2.3 und Google Chrome
ab Version 28.
Kapitel 5
Konzept
Die verschiedenen erhobenen Anforderungen zeigen auf, welche Elemente die Implementierung beinhalten soll, wie sie miteinander verbunden sind und vor allem,
wie der Benutzer mit ihnen interagieren kann. Nun gilt es diese gesammelten Anforderungen in ein Konzept zu fassen und für die Implementierung vorzubereiten.
Zunächst soll dazu eine vereinfachte Übersicht über die gesamte Applikation gegeben werden, bevor es anschließend detailliert in die einzelnen Bereiche geht.
Da die Applikation über ein Netzwerk auf einen Server und mehrere Clients verteilt werden soll, wird als nächstes dargestellt, wie die einzelnen Daten über das
Netzwerk vom Server zu den einzelnen Clients und wieder zurück transportiert
werden, wie diese Schnittstelle erweiterbar bleibt und welche Optimierungen dabei
berücksichtigt werden müssen. Diese abstrahierte Netzwerk-Schnittstelle ist dann
die Zwischenschicht für die eigentliche Spiellogik, welche über die Bereiche Server
und Client verteilt ist. Hierzu soll zunächst die serverseitige Spiellogik mit allen
benötigten Objekten, Zuständen und Abhängigkeiten modelliert werden. Danach
wird dargestellt, wie die einzelnen Clients an diese Elemente angebunden werden.
Als letztes sollen dann noch die verschiedenen Aktionen der Benutzer von der
Eingabe über die Verarbeitung bis zur Ausgabe dargestellt werden.
5.1
Gesamtüberblick
Die hier vorgestellte Applikation verteilt sich über die Bereiche Server, Netzwerk
und Clients. Die dabei benötigten Klassen und ihre bestehenden Abhängigkeiten
sind in Abbildung 5.1 dargestellt. In dieser Darstellung werden diese drei Bereiche
nochmals anders aufgeteilt: Die Clients bestehen hier aus einer Anzeigelogik, einem
Zwischenspeicher und der GUI.
35
36
Konzept
Abbildung 5.1: Vereinfachte Übersicht über die Objekte der Applikation und deren
Abhängigkeiten und Kommunikationswege
Konzept
37
Die GUI startet clientseitig zuerst und ist für die Anzeige von Informationen in den
einzelnen Elementen zuständig. Diese Informationen befinden sich im Zwischenspeicher der Clients, welcher durch die Anzeigelogik verwaltet wird. Hierzu erhält
diese Schicht neue Daten über einen Listener von der Netzwerkschicht und speichert
diese Daten im Zwischenspeicher. Als nächstes benachrichtigt die Logik ebenfalls
über einen Listener das GUI-Objekt über die Art der Veränderung, woraufhin sich
das GUI-Objekt die jeweiligen Daten abholt und darstellt.
Die Netzwerkschicht besteht in diesem vereinfachten Fall aus nur einem Objekt,
welches genau einen Server mit beliebig vielen Clients verbindet. Dabei wird diese
Schicht von außen direkt angesprochen und signalisiert Aktualisierung an den
Server beziehungsweise an die Clients über zuvor registrierte Listener. Hierbei
werden der Typ der Aktion und die benötigten Daten an die verarbeitenden Klassen
übergeben, dort verarbeitet und dem passenden Callback zugewiesen.
Der Server beinhaltet die eigentliche Logik der Applikation und startet unabhängig
von den Clients. Auch hier kommen neue Informationen über die Netzwerkschicht
von den Clients an, werden jedoch nicht nur abgespeichert, sondern manipulieren
die verschiedenen Objekte der Applikation. Hierbei gibt es zunächst die BenutzerObjekte, welche die einzelnen Clients darstellen. Diese können Spiellobby-Objekte
erstellen und diesen beitreten, was ebenfalls durch das Backend-Objekt gesteuert
wird. Der Ersteller kann dann die Lobby in das Spiel überführen, woraufhin die
einzelnen Benutzer-Objekte ein Spielfeld und ein Inventar erhalten und mit diesen
dann am Spiel teilnehmen. All diese Informationen werden immer durch Eingaben
der Benutzer in die Clients erzeugt, über die Netzwerkschicht zum Server geleitet
und hier verarbeitet. Die aktualisierten Objekte und Daten werden dann wieder
über die Netzwerkschicht entweder an alle oder nur an bestimmte, einzelne Clients
geschickt und sorgen dort für eine Aktualisierung der Anzeige.
5.2
Netzwerkkommunikation
Da diese Arbeit von der Entwicklung einer verteilten Applikation handelt, soll zunächst diese Nahtstelle zwischen den einzelnen Clients und dem Server beschrieben
werden. Dabei sollen die verschiedenen Daten, welche auf dem Server vorliegen,
von den einzelnen Clients manipuliert werden können. Dazu können die Benutzer
verschiedene Aktionen ausführen, wodurch die Clients Nachrichten an den Server
verschicken, welche dieser dann passend interpretieren und verarbeiten muss. Die
daraus entstehenden neuen Informationen müssen dann ebenfalls über das Netzwerk zu den einzelnen Clients gelangen und auch dort passend verarbeitet und
dann dargestellt werden.
38
Konzept
Durch die verschiedenen Datentypen ergibt sich eine Vielzahl von Nachrichten,
welche für den jeweiligen Zweck unterschiedliche Aktionen mit verschiedenen
Informationen auslösen sollen. Damit alle diese Anwendungsfälle möglichst einfach
nutzbar werden, soll eine kommunikationsorientierte Middleware entwickelt werden, welche als Bindeglied zwischen den Applikationen auf den einzelnen Clients
und dem Server fungieren soll. Dadurch soll das Verschicken, Empfangen und
Verarbeiten der einzelnen Nachrichten so vereinfacht werden, dass die Netzwerkkommunikation aus Entwicklersicht in den Hintergrund rückt.
Für eine solche Netzwerkschicht soll zunächst die mögliche Infrastruktur entwickelt
werden, welche sich um das Versenden, Empfangen und Verarbeiten der Informationen kümmert. Hierbei wird eine Schnittstelle entwickelt, welche es dem Entwickler
vereinfachen soll, die Daten zwischen dem Server und den Clients auszutauschen.
Dazu wird auch der Ablauf der Kommunikation in dieser Schicht beleuchtet. Danach
soll diese Infrastruktur erweitert werden, um zunächst zwei Arten von Informationen separat behandeln zu können: Ereignisse und Daten. Hierzu wird aufgezeigt,
worin die Unterschiede zwischen diesen Informationen bestehen und welche Verarbeitung sie erfordern. Dabei wird auch beschrieben, wie andere Informationsarten
hinzugefügt werden können und wie die resultierende Netzwerkkomponente aussieht.
5.2.1
Infrastruktur
Für die grundlegende Infrastruktur sollen die beiden Seiten der Applikation, Server
und Client, zunächst stark vereinfacht dargestellt werden, wie in Abbildung 5.2 zu
erkennen ist. Es soll ausreichen, wenn die beiden Seiten nur die Elemente beinhalten,
welche für das Empfangen und Verarbeiten von Nachrichten benötigt werden. Da
diese Schritte auf beiden Seiten gleich ablaufen, sind die zwei dargestellten Klassen zunächst fast identisch. Der Server erhält jedoch bei eingehenden Nachrichten
zusätzlich noch die Verbindung, über welche die Nachricht eingegangen ist. Die
Unterschiede befinden sich in den jeweiligen Callbacks, welche in einer Map gesammelt werden. Hierbei verlinkt eine bestimmte Aktion im Format String auf eine
Methode. Sobald eine Nachricht über die Verbindung eingeht, wird die Methode
handle() aufgerufen und erhält die gewünschte Aktion sowie Schlüssel-Wert-Paare
mit allen vorhandenen Daten. Diese Methode sucht dann den passenden Callback
aus der Map und übergibt der darin festgelegten Callback-Methode die Daten. Diese
Callback-Methode ist dann für die eigentliche Verarbeitung der Daten zuständig.
Für die Kommunikation über das Netzwerk soll eine andere abstrahierte Klasse
sorgen: der Verbindungs-Manager. Dieser hält, wie in Abbildung 5.3 zu erkennen ist,
die einzelnen Verbindungen und kann über diese Nachrichten versenden. Hierbei
Konzept
39
Abbildung 5.2: Server und Client besitzen für die Verarbeitung von Nachrichten die
gleichen Elemente
wird sichergestellt, dass alle Nachrichten zuverlässig übertragen werden. In der
späteren Umsetzung besteht dieser Bereich aus mehreren Elementen, welche auf
Server und Client aufgeteilt werden. Für die Darstellung des Verbindungsablaufs
soll diese Abstraktion jedoch ausreichen.
Abbildung 5.3: Der Verbindungs-Manager ist für den Nachrichtenaustausch zuständig und umfasst hier sowohl die Client- als auch die Serverseite
Theoretisch könnte ein Client nun eine Nachricht über den Verbindungs-Manager an
den Server schicken und dieser könnte auf dem gleichen Weg antworten. Jedoch soll
das Verpacken der Informationen in eine Nachricht weder Aufgabe der Verbindung,
noch von Server und Client sein. Auch das Entpacken der ankommenden Nachricht
gehört nicht in die Aufgabenbereiche der bisherigen Elemente. Für diese beiden
Aufgaben ist ein Bindeglied verantwortlich: der Nachrichten-Manager. Dieser wird
einmal zwischen dem Client und der Verbindung sowie zwischen dem Server und
der Verbindung eingebaut und übernimmt die oben beschriebenen Funktionalitäten,
wie in Abbildung 5.4 zu erkennen ist. Client und Server können über diesen Manager
Informationen versenden, indem sie die Methode send() mit einer Aktion als String
und den jeweiligen Daten als Schlüssel-Wert-Paare aufrufen. Der Server gibt dabei
noch die jeweilige Verbindung an, über welche die Nachricht geschickt werden soll
– der Client hält hingegen nur eine. Der Manager wandelt diese Informationen dann
in eine Nachricht um und leitet diese an den Verbindungs-Manager. Empfängt dieser
Manager eine neue Nachricht, wird diese an den Nachrichten-Manager geleitet,
welcher die Nachricht dann entpackt. Diese entpackten Informationen werden dann
über einen Listener zum Front- beziehungsweise Backend geschickt, welches dann
die passende Callback-Methode startet.
40
Konzept
Abbildung 5.4: Der Nachrichten-Manager ver- und entpackt Informationen und
leitet diese weiter
Wie diese einzelnen Komponenten zusammenspielen, soll an einer einfachen Benutzerinteraktion und dem daraus resultierenden Ergebnis dargestellt werden. Der Benutzer soll dazu seinen Benutzernamen abfragen und diesen dann angezeigt bekommen, wie im Sequenzdiagramm in Abbildung 5.5 dargestellt ist. Hierzu interagiert
der Benutzer zunächst mit dem Client und fordert den Benutzernamen an. Der Client übergibt dann die passende Aktion an den clientseitigen Nachrichten-Manager,
welcher diese Information dann verpackt. Die daraus resultierende Nachricht wird
dann vom Verbindungs-Manager an den serverseitigen Nachrichten-Manager geleitet, welcher daraus wieder die Informationen ausliest und diese an den Server
weiterleitet. Hier wird dann aus der Map die passende Methode zur Verarbeitung
der Aktion gesucht und gestartet.
Abbildung 5.5: Der Benutzername wird über das Netzwerk transportiert
Auf diese Weise können Nachrichten zwischen genau einem Server und einem Client ausgetauscht werden. Es soll aber auch die Möglichkeit bestehen, eine Nachricht
vom Server an alle Clients verschicken zu können - hierzu dienen Broadcasts. Diese
werden ebenfalls von der Netzwerk-Schnittstelle als Funktionalität zur Verfügung
gestellt und mit einer Aktion und den jeweiligen Daten aufgerufen, wodurch simultan einzelne Nachrichten an alle Clients verschickt werden. Die Nachrichten werden
clientseitig genauso behandelt, wie auch die direkten Nachrichten, jedoch ist es auf
diese Weise möglich, alle Clients gleichzeitig mit Informationen zu versorgen.
Konzept
5.2.2
41
Nachrichtentypen
Durch die Interaktion der einzelnen Benutzer mit der Applikation entstehen sehr
unterschiedliche Arten von Informationen, welche vom Benutzer zum Server übertragen, dort verarbeitet und dann wieder verteilt werden müssen. Dabei kann sich
nur ein einzelner Zustand ändern oder aber große Datenmengen anfallen, welche an
alle Benutzer verteilt werden müssen. Hier sollte auch die Middleware so angepasst
werden, dass diese die verschiedenen Arten von Informationen in spezielle Nachrichten umwandelt und diese passend zu der jeweiligen Informationsart verschickt.
Dabei werden zunächst zwei Arten von Nachrichten unterschieden: Ereignisse und
Daten.
Die erste Nachrichtenart enthält Informationen zu Zustandsänderungen, welche mit
nur kleinen Datenmengen und ohne Verarbeitung schnellstmöglich verschickt und
verarbeitet werden sollen. Die resultierenden Nachrichten sind atomar und liefern
immer neue Informationen, ohne dabei von alten Zuständen abhängig zu sein.
Beispiele im aktuellen Anwendungsfall wären das Schließen einer Spiellobby oder
das Anwenden eines Extras. Für diesen Nachrichtentyp muss der bereits festgelegte
Ablauf nicht verändert werden – der Nachrichten-Manager entspricht also in seiner
Funktionsweise bereits dem Ereignis-Manager. Die Informationen werden bereits
vollständig in eine Nachricht verpackt, direkt verschickt und verarbeitet.
Dem gegenüber stehen die Datennachrichten, welche beliebig große Datenmengen
zwischen den einzelnen Clients und dem Server austauschen sollen. Im Gegensatz zu den Ereignissen können diese Datenmengen auf einem vorhergehenden
Datenstand aufbauen und diesen aktualisieren. Eine Ausnahme stellen die initialen
Daten dar, welche jedoch aus Konsistenzgründen ebenfalls als Daten-Nachrichten
übertragen werden sollen. Falls eine Aktualisierung vorgenommen werden soll,
werden nur die Teile der Nachricht neu übertragen, die sich auch verändert haben –
diese Art der Aktualisierung wird als Delta-Update bezeichnet. Dazu müssen immer
zwei Datenstände vorliegen: zum einen der Datenstand mit den neuen Daten und
zum anderen der zuletzt verschickte Datenstand, welcher den Zustand der Daten
beim Ziel repräsentiert. Hat man diese beiden Versionen, kann man den dazwischen
bestehenden Unterschied an Informationen berechnen und diesen so herausziehen,
dass man nur die neuen Informationen zu übertragen braucht. Durch diese Methode kann man vor allem bei kleinen Aktualisierungen in großen Datenmengen
Bandbreite und Übertragungszeit einsparen.
Das dafür benötigte Delta wird von der Klasse Zipper erstellt und wieder entpackt,
wie in Abbildung 5.6 dargestellt ist. Grundlage für diese Sequenz ist das Aktualisieren des Spielfeldes bei einem Benutzer welche an den Server geschickt werden
soll. In diesem Fall werden nur wenige Elemente des Spielfeldes verändert – eine
42
Konzept
teilweise Aktualisierung der Daten ist also dem erneuten Versenden aller Kugeln
vorzuziehen.
Hierzu sendet der Server zunächst das Spielfeld mit der jeweiligen Aktion an den
serverseitigen Nachrichten-Manager, welcher in diesem Fall der Daten-Manager
ist. Dieser verpackt die Informationen wieder in eine Nachricht, wobei jedoch das
Spielfeld noch nicht gesetzt wird, da hier zunächst noch das Delta gebildet werden muss. Hierzu übergibt der Daten-Manager das aktuelle Spielfeld sowie den
letzten Stand des Spielfeldes, welcher ebenfalls im Server verwaltet wird, an die
Zipper-Klasse, welche daraus das Delta erstellt. Da alle Nachrichten zuverlässig
zugestellt werden, hat der Server immer einen Datenstand, welcher dem Datenstand bei den jeweiligen Clients entspricht. Das erstelle Delta wird nun an die
vorbereitete Nachricht angehängt. Sobald dies geschehen ist, wird die Nachricht
über den Verbindungs-Manager an den clientseitigen Daten-Manager geschickt und
von diesem verarbeitet – die Zuteilung zu dem passenden Manager erfolgt dabei
über ein Flag in der Nachricht. Alle bei diesem Manager eingehenden Nachrichten beinhalten ein Delta, das zusammen mit dem letzten Datenstand wieder an
eine Zipper-Klasse übergeben wird - dieses Mal jedoch zum Zusammenbauen des
aktuellen Datenstands. Dieser wird dann an den Daten-Manager zurückgegeben
und von diesem zum Client-Objekt gereicht, dort gespeichert und dem Benutzer
angezeigt. Der Abgleich und die teilweise Aktualisierung der Daten läuft dabei
immer synchron ab, um die Reihenfolge der Aktualisierungen zu wahren.
Abbildung 5.6: Sequenzdiagramm für ein Delta-Update des Spielfeldes
Es ist aber auch vorstellbar, dass alternative Anwendungsfälle andere Nachrichtentypen verlangen, welche ebenfalls auf beiden Seiten der Netzwerkschicht gleich
behandelt und somit von der Middleware selbst abstrahiert angeboten werden sollen. Für eine solche Erweiterung bietet der Verbindungs-Manager auf beiden Seiten
jeweils eine Schnittstelle an, an welche weitere Manager angehängt werden können.
Dadurch soll er ermöglicht werden, neue Funktionalitäten beidseitig anzubinden
und diese dann wiederum per Schnittstelle aus der Middleware heraus anzubieten.
Konzept
43
Aus diesen Vorgaben entsteht die Aufteilung der Netzwerkschicht, wie sie in Abbildung 5.7 dargestellt ist. Hier wird bereits die klare Trennung in Server und Client
beachtet, welche für zwei Verbindungs-Manager mit unterschiedlichen Funktionen
sorgt. So bietet der Server einen Dienst über einen Port an, welchen der Client dann
benutzen kann, um sich mit dem Server zu verbinden. Über die dann gebildete
Datenverbindung können nun beide Seiten miteinander kommunizieren, in dem
die Informationen von den jeweiligen Nachrichten-Managern verpackt und an die
Verbindungs-Manager weitergeleitet werden.
Abbildung 5.7: Die einzelnen Komponenten auf Server- und Client-Seite für den
Verbindungsaufbau und den Nachrichtenaustausch
5.3
Datenverwaltung
Nachdem nun eine Netzwerkinfrastruktur geschaffen ist, welche beliebige Daten
vom Server zum Client und wieder zurück transportieren kann und diese Dienste
so per Schnittstelle anbietet, dass der eigentliche Transport in den Hintergrund
rückt, sollen jetzt die eigentlichen Objekte und Daten der Applikation betrachtet
werden. Dazu werden in diesem Abschnitt zunächst die Spielelemente behandelt,
welche die Grundlage des Spiels bilden und auf dem Server verwaltet werden. Auch
die Anbindung dieser Elemente untereinander und an die Netzwerkschicht wird
hier erläutert, während die Anbindung der Clients erst Bestandteil des nächsten
Abschnitts ist.
Einen Überblick über die Objekte des Servers wird in Abbildung 5.8 vermittelt. Der
Server besteht, wie bereits im Abschnitt 5.1 erläutert, aus der Netzwerkschnittstelle
und der Spiellogik. Die Spiellogik ist auch hier in die bereits vorgestellten Objekte
unterteilt, wobei jedoch das Backend-Objekt die einzelnen Benutzer- und LobbyObjekte nicht mehr direkt, sondern über eine Listen-Klasse hält und verwaltet. Die
44
Konzept
detaillierteren Beschreibungen der einzelnen Klassen und ihre Zusammenhänge
werden nachfolgend behandelt.
Abbildung 5.8: Vereinfachte Darstellung aller serverseitigen Spielobjekte und die
jeweiligen Beziehungen dazwischen
Konzept
45
Die Nahtstelle zwischen dem Netzwerk und dem Backend ist in Abbildung 5.9
dargestellt. Hierbei ist das Netzwerk-Interface die eigentliche Netzwerkanbindung,
welches die Funktionalitäten der serverseitigen Manager aus dem vorherigen Abschnitt abstrahiert zur Verfügung stellt. Dazu gehört das Verschicken der beiden
verschiedenen Nachrichtenarten an entweder einen oder alle Benutzer, wobei auch
das Ziel für einen Broadcast, etwa eine Lobby, angegeben werden kann. An dieses Interface ist die Klasse Backend gebunden, welche die Nachrichten aus dem
Netzwerk über einen Listener signalisiert bekommt und diese an die passenden
Callback-Methoden weiterleitet. Diese Methoden befinden sich entweder direkt in
der Klasse Backend oder gehören zu einem anderen Spielobjekt und werden vom
Backend kombiniert und aufgerufen. Bei Aktualisierungen wird dann die passende
Nachricht verschickt.
Abbildung 5.9: Die Schnittstelle zur Netzwerkschicht und das daran angebundene
Backend
Um die Aktionen aus dem Netzwerk passend weiterleiten zu können, muss das
Backend die beiden wichtigsten Spielobjekte halten, welche wiederum alle anderen
Elemente des Spiels beinhalten: die Liste aller Benutzer und die Liste aller Lobbys.
Diese beiden Listen werden jeweils in einer eigenen Klasse verwaltet und bieten
neben den üblichen Gettern und Settern auch spezielle Methoden zur Datenmanipulation an.
Für die Verwaltung der Benutzer ist die Benutzer-Liste zuständig, welche zusammen mit der Benutzer-Klasse in Abbildung 5.10 zu erkennen ist. Bei diesen sowie
bei allen anderen Klassen und Objekten auf dem Server muss die Abstraktion klar
erkennbar sein, da es sich hier beispielsweise nicht um den Benutzer selbst, sondern
nur seine Repräsentation handelt. Damit es zu keiner Verwechslung kommt, werden alle hier die verwendeten Klassen und Objekte explizit als solche bezeichnet.
Die Benutzer-Liste bietet Methoden zum Anfügen, Entfernen und Auslesen von
Benutzer-Objekten, sowie zur Überprüfung, ob ein bestimmtes Benutzer-Objekt in
der Liste enthalten ist. Die Benutzer werden über einen Benutzernamen identifiziert,
46
Konzept
welcher beim erstmaligen Einfügen zufällig gesetzt und zurückgegeben wird. Dieser
muss in der gesamten Applikation einmalig sein. Für den Fall, dass ein Benutzer
von einer Lobby in ein Spiel gelangt, steht eine Methode zum Erstellen der einzelnen
Spielelemente bereit, welche später detailliert erläutert werden. Zusätzlich kann das
Benutzer-Objekt dann auch eine neue Kugel zum jeweiligen Spielfeld hinzufügen,
den aktuellen Stand seines Spielfeldes liefern, das erste Extra aus dem eigenen
Inventar holen oder sich selbst ein neues aktives Extra hinzufügen.
Abbildung 5.10: Die Benutzer werden als Objekt abstrahiert und in einer BenutzerListe verwaltet
Die Benutzer-Liste wird jedoch nicht nur einmalig vom Backend gehalten und
verwaltet, sondern findet auch Verwendung bei den Spiellobbys und hält in diesen
ebenfalls alle Benutzer-Objekte, welche sich gerade in einer Lobby befinden. Diese
Lobbys wiederum werden von einer Listen-Klasse verwaltet, wie in Abbildung 5.11
zu sehen ist. Die Klasse bietet dabei dem Backend Funktionalitäten an, um die
Liste passend manipulieren zu können. So können neue Lobby-Objekte an die
Liste angefügt oder über den Ersteller beziehungsweise den Lobbynamen wieder
entfernt werden. Außerdem kann das Backend auch gezielt nach einem LobbyObjekt suchen. Für die Ausgabe der gesamten Lobbyliste wird außerdem eine
Methode bereitgestellt, welche aus den Lobby-Objekten die Namen ausliest, damit
diese als Liste von Strings an die Clients geschickt werden können.
Die Lobbys selbst werden durch einen Namen identifiziert, wobei auch hier jeder
Name einmalig in der Applikation ist. Außerdem hält jede Lobby neben der Liste
von Benutzern auch den Ersteller der Lobby, da dieser mehr Privilegien im Lobbybetrieb genießt. Während der Ersteller für die Dauer der Lobby fest ist und nur
einmalig beim Erstellen gesetzt werden kann, können sich die anderen Teilnehmer
frei bewegen. Hierzu werden die Methoden der Listen-Klasse übernommen und
erweitert, damit das Backend direkt auf den Daten arbeiten kann. Die Erweiterung
dient dazu, dass beim Entfernen eines Benutzers geprüft wird, ob dieser der Ersteller der Lobby ist und die Lobby noch nicht ins Spiel übergeführt wurde, da in
Konzept
47
Abbildung 5.11: Lobbys werden in einer Lobby-Liste verwaltet und können zu
einem Spiel erweitert werden
diesem Fall die Lobby entfernt werden muss. Startet der Ersteller das Spiel, wird
dies im Lobby-Objekt durch das Setzen einer Flag gesichert. Die anderen Daten der
Lobby werden nicht verändert, lediglich die Überprüfungen beim Manipulieren der
Lobby werden somit dem aktuellen Zustand angepasst. Außerdem gibt es auch bei
dieser Klasse die Möglichkeit, von allen aktuellen Teilnehmern die Benutzernamen
auszulesen und in eine Liste von Strings zusammenzufügen, damit diese für die
clientseitige Darstellung genutzt werden kann.
Befindet sich der Benutzer in einem Spiel, so erhält das Benutzer-Objekt weitere
Elemente, welche die spielrelevanten Objekte darstellen. Eine Übersicht dieser
Spielobjekte ist in Abbildung 5.12 sichtbar. Dabei werden alle Klassen von dem
Benutzer-Objekt gehalten und verwaltet.
Zunächst hat jeder Benutzer ein eigenes Spielfeld, welches wiederum aus Kugeln
besteht, wobei die Kugeln hier immer nur ein Teil des Spielfeldes sind. Dazu wurde
auch der Kugelvorrat abstrahiert – dieser ist nur noch eine Zahl im Benutzer-Objekt,
welche die Anzahl der verbleibenden Kugeln darstellt, wobei die Farbe der jeweils
nächsten Kugel separat als Zahl abgespeichert wird. Die Kugel-Objekte selbst werden immer zufällig erzeugt, wenn der Spieler die nächste Kugel zum Abschuss
benötigt oder eine neue Reihe eingefügt wird. Schießt der Spieler eine Kugel ab und
bleibt diese an einer Position hängen, wird an eben dieser Position ein Kugel-Objekt
im Spielfeld-Objekt eingefügt. Nach dem Einfügen der Kugel, wird die benachbarte Kugelmenge durchlaufen und entfernt, wenn mindestens drei gleichfarbige
Kugeln zusammenkommen. Dabei werden auch schwebende Kugeln und Extra
abgebaut, wobei letztere wieder zum Benutzer-Objekt gelangen und von diesem
in sein Inventar gelegt werden. Baut der Benutzer außerdem mehr als fünf Kugeln
oder mehr als fünf schwebende Kugeln ab, wird dem Spielfeld ein neues Extra ange-
48
Konzept
Abbildung 5.12: Im Spiel wird das Benutzer-Objekt um ein Spielfeld und ein Inventar
erweitert
fügt. Außerdem bietet die Klasse auch Methoden zum Hinzufügen und Entfernen
ganzer Kugelreihen an, sowie zum vollständigen Bereinigen des Spielfeldes, was
beispielsweise beim Ende des Spiels geschieht.
Andererseits hat der Spieler auch ein Inventar, in dem er Extras sammeln und
diese benutzen kann. Dazu hat das Benutzer-Objekt eine Instanz der InventarKlasse, welche beliebig viele Extra-Objekte halten kann. Extras sind in diesem Fall
Erweiterungen der Kugel-Klasse um einen Typ, welcher die Aktion festlegt, die
beim Benutzen des Extras angewendet wird. Hierzu können neue Extra-Objekte ans
Ende des Inventars angehängt und von vorne wieder ausgelesen werden. Sobald
beim Server ein Extra ankommt, wird dieses vom Benutzer-Objekt angewendet. Bei
Extras, welche das Spielfeld manipulieren, wird die Aktion auf das Spielfeld-Objekt
des Benutzers angewandt. Bei anhaltenden Effekten mit einer Wirkdauer, wird das
Extra an den jeweiligen Spieler geschickt und im Frontend angewendet.
Konzept
5.4
49
Struktur der Clients
Während der Server die gesamten Informationen der Applikation hält, manipuliert
und über das Netzwerk zur Verfügung stellt, befinden sich auf der anderen Seite der
Verbindung die einzelnen Clients, welche die Manipulation der Daten steuern. Diese
basieren auf den in Abschnitt 5.1 beschriebenen Elementen Netzwerk, Anzeigelogik,
Zwischenspeicher und GUI. Wie jedoch in Abbildung 5.13 zu erkennen ist, wurde
der Zwischenspeicher in die einzelnen Spielelemente aufgeteilt, um diese so besser
verwalten zu können. Auch die GUI wurde um eine Klasse erweitert, welche die
Bewegungen der aktiven Kugel steuert.
Abbildung 5.13: Die vereinfachten Klassen des Clients mit den jeweiligen Beziehungen und der Anbindung an die Netzwerkschicht
50
Konzept
Die Clients werden über das Netzwerk mithilfe der bereits vorgestellten und im
Backend genutzten Netzwerkschicht und deren Schnittstelle an den Server angebunden, wie in Abbildung 5.14 dargestellt ist. Der grundlegende Aufbau ähnelt dabei
dem Server: Nachrichten können über die Netzwerk-Schnittstelle versendet und
empfangen werden und gelangen dann in die Frontend-Klasse, welche die passende
Callback-Funktion aufruft. Der essentielle Unterschied besteht jedoch darin, dass
die Clients nicht nur auf Nachrichten reagieren, wie es der Server tut, sondern
selbstständig neue Informationen erzeugen und diese verschicken können. Diese
neuen Informationen werden durch Benutzereingaben erzeugt und vom FrontendObjekt verschickt. Solche Eingaben sind dabei die Aktionen, die der Spieler im Spiel
tätigen kann – dazu gehören das Beitreten, Erstellen und Verlassen einer Lobby, das
Entfernen eines Benutzers aus der eigenen Lobby sowie die Handlungen im Spiel:
einen neuen Ball zum Spielfeld hinzufügen und das erste Extra aus dem Inventar
auf einen Spieler anwenden.
Abbildung 5.14: Das Frontend ist ebenfalls an die Netzwerk-Schnittstelle angebunden, kann aber nicht nur auf Nachrichten reagieren, sondern auch neue Informationen aus Benutzereingaben versenden
Die Clients müssen jedoch auch die Objekte vom Backend passend speichern, aktualisieren und ausgeben können. Dabei werden hier die beiden Oberklassen Benutzer
und Lobby benötigt, welche jedoch nur der Datenhaltung und der Datenaktualisierung vom Server aus dienen, jedoch nicht direkt im Client manipuliert werden
können. Wie in Abbildung 5.15 zu erkennen ist, besitzt der Benutzer clientseitig
viele Objekte vom Server als einfache Variablen innerhalb der Klasse. Neben dem Benutzernamen als String werden hier auch das Spielfeld, die verbleibende Kugelzahl
im aktuellen Kugelvorrat, das Inventar und die aktiven Extras verwaltet. Bis auf das
letzte Element besitzen alle anderen nur Methoden zum Setzen und Auslesen der
Daten.
Konzept
51
Abbildung 5.15: Die Klasse des Benutzers hält alle Informationen zum aktuellen
Benutzer sowie die aktiven Extras
Extras hingegen liegen als eigenes Objekt vor und werden in einer Liste gesammelt
und verwaltet, da die aktiven Extras nur clientseitig angewendet werden. Hierzu werden neue Extras, welche über das Netzwerk im Client ankommen, an das
Benutzer-Objekt weitergeleitet und hier in die Liste von Extras angefügt. Für die Aktualisierung der verbleibenden Zeiten sorgt ein Timer, welcher im Sekundentakt die
dafür benötigte Methode im Benutzer-Objekt anstößt. Hier werden die abgelaufenen
Extras ebenfalls entfernt.
Neben den Informationen zum aktuellen Benutzer müssen auch alle relevanten
Informationen für die aktuelle Lobby gehalten werden, in der sich der Benutzer befindet. Hierzu gehört zum einen die Angabe, ob der Benutzer der Ersteller der Lobby
ist, da ihm hierdurch mehr Funktionen eingeblendet werden. Zum anderen zählen
hierzu aber auch die Namen und Spielfelder der anderen Benutzer in der Lobby. Die
Verwaltung dieser Daten übernimmt die Klasse Lobby aus Abbildung 5.16. Da dem
aktuellen Benutzer jedoch nur der Benutzername und die Spielfelder, nicht aber
die aktiven Extras oder das Inventar, von den anderen Spielern angezeigt werden
sollen, speichert das Lobby-Objekt auch nur diese Informationen. Dabei reichen hier
ebenfalls Getter und Setter aus.
Abbildung 5.16: Die clientseitige Lobby-Klasse hält Informationen zu der aktuellen
Lobby und deren Teilnehmern
Für die Darstellung der einzelnen Objekte sowie für die Anbindung der Benutzerinteraktionen wird noch eine weitere Schicht oberhalb der Datenschicht im Client
gesetzt. Da die eigentliche Darstellung im Browser durch HTML zustande kommt,
52
Konzept
wird eine Klasse benötigt, welche die einzelnen Elemente passend aktualisiert.
Hierzu dient die GUI-Manager-Klasse aus Abbildung 5.17, welche vor allem zwei
Bereiche abdecken muss.
Abbildung 5.17: Die Aktualisierung der Anzeige sowie das Abfangen von Benutzereingaben übernimmt der GUI-Manager
Zum einen fängt diese Klasse Benutzerinteraktionen ab und leitet diese passend
weiter. Dafür stehen die beiden Event-Listener für Maus- und Tastatureingaben
zur Verfügung, welche entweder den Zahlenwert der gedrückten Taste oder aber
die Position des Mausklicks auswerten. Diese Informationen werden dann, je nach
Bedeutung der einzelnen Eingaben, an die passende Methode im Frontend-Objekt
geleitet und von dort an den Server verschickt. Eine Ausnahme stellt dabei das
Verschießen und Bewegen einer Kugel bis zum Zeitpunkt des Auftreffens auf andere
Kugeln beziehungsweise den oberen Spielfeldrand dar. Diese Bewegung wird vollständig auf Clientseite berechnet und angezeigt, weil eine serverseitige Berechnung
und die ständige Übertragung der aktualisierten Position über das Netzwerk zu
aufwändig wären. Deshalb wird die Kugel clientseitig bewegt und erst die Position
des Auftreffens wird an den Server übertragen.
Neben dem Verarbeiten von Benutzereingaben sorgt diese Klasse aber auch für die
Visualisierung der aktuellen Daten. Zum einen können dazu die einzelnen Bereiche
der Applikation eingeblendet oder auch versteckt werden, welche dann die für
diesen Bereich passenden Daten anzeigen. Sobald neue Daten über das Netzwerk
eingehen, werden diese in den passenden Objekten abgelegt und der GUI-Manager
wird über die Aktualisierung informiert. Je nach übergebenem Typ werden dann die
passenden Information gespeichert und dem Benutzer in den jeweiligen Elementen
angezeigt.
5.5
Ablauf der Kommunikation
Bevor die entstandenen Klassen implementiert werden, muss noch geklärt werden,
wie die einzelnen Objekte miteinander interagieren, um die verschiedenen Benutzerinteraktionen, wie sie in Abschnitt 4.1 aufgelistet wurden, umsetzen zu können.
Hierzu sollen die wichtigsten Use-Cases durchlaufen und die dabei genommenen
Konzept
53
Wege vom Benutzer zum Server und wieder zurück aufgezeigt werden. Hierbei
wird die Netzwerkschicht ebenfalls abstrahiert dargestellt, da der Ablauf innerhalb
dieser immer dem bereits demonstrierten Schema aus Abschnitt 5.2 entspricht.
Zunächst soll der Benutzer die Applikation starten und zum Hauptmenü gelangen,
in dem er dann seinen Benutzernamen und eine Liste der aktuellen Spiellobbys sehen kann. Hierzu wird die Applikation beim Client gestartet, wie in Abbildung 5.18
dargestellt ist. Dabei werden von dem GUI-Manager aus alle weiteren Klassen erzeugt und das Hauptmenü angezeigt. Sobald die Netzwerk-Schnittstelle erzeugt
wurde, verbindet diese sich automatisch mit dem Server.
Abbildung 5.18: Der Client instanziiert vom GUI-Manager aus alle weiteren Klassen
und verbindet sich zum Server
Serverseitig geht daraufhin ein Ereignis für einen neuen Benutzer ein, wie in Abbildung 5.19 zu sehen ist. Zunächst fügt das Backend-Objekt ein neues Benutzer-Objekt
in der Benutzer-Liste ein. Dabei wird ein zufälliger, einmaliger Name erzeugt und
über die Netzwerk-Schnittstelle als Ereignis mit dem Benutzernamen als Datensatz an das Frontend gesendet. Danach wird noch eine weitere Aktion gestartet,
welche dem Benutzer alle aktuellen Lobbys liefert. Hierzu holt sich das BackendObjekt von der Lobby-Liste alle aktuelle Lobbynamen und sendet diese über die
Netzwerk-Schnittstelle als Daten-Nachricht an den Client.
Beim Client müssen nun beide Informationen vom Server verarbeitet werden. Zunächst wird dazu der Benutzername gesetzt, wie es in Abbildung 5.20 sichtbar
ist. Dazu übergibt die Netzwerk-Schnittstelle die Aktion mit dem Benutzernamen
als Datensatz an das Frontend-Objekt. Dieses speichert den Namen ab und informiert den GUI-Manager über die Aktualisierung. Dieser holt sich nun über das
Frontend-Objekt den gespeicherten Benutzernamen aus dem Benutzer-Objekt und
zeigt diesen an. Dieser Ablauf wird immer beim Erhalt eines neuen Benutzernamen
durchlaufen, also auch, wenn der Benutzer selbst einen Namen festlegt. Hierbei
sendet der Client diesen Benutzernamen an den Server, welcher dann überprüft, ob
54
Konzept
Abbildung 5.19: Der Server fügt den neuen Benutzer in die Liste und schickt ihm
seinen Benutzernamen und die Lobby-Liste
der Name zulässig ist. Falls das der Fall ist, wird der neue Name zurückgeschickt,
andernfalls der alte. Auch bei anderen Aktionen, in denen sich ein Element im
Benutzer-Objekt verändert, wird dieses Muster durchlaufen – hierzu gehört das
Aktualisieren der verbleibenden Kugeln und des Inventars.
Abbildung 5.20: Sobald der Client einen neuen Benutzernamen erhält, wird dieser
gespeichert und angezeigt
Auch die Liste der aktuellen Lobbys wird auf einem ähnlichen Weg gespeichert.
Die Liste selbst wird wieder über die Netzwerkschnittstelle an das Frontend-Objekt
gereicht, welches sie dann abspeichert. Danach wird dem GUI-Manager auch hier
eine Aktualisierung signalisiert, woraufhin dieser sich die Liste abholt und anzeigt.
Die Aktualisierung geschieht jedoch nicht nur beim ersten Verbinden, sondern auch
danach bei jeder Änderung der Lobby-Liste – etwa beim Erstellen oder Schließen
von Lobbys. Dabei werden die Informationen unabhängig vom aktuellen Zustand
der Applikation aktualisiert, wodurch die Clients immer aktuelle Daten zur Verfügung haben, auch wenn diese dem Benutzer nicht angezeigt werden müssen. Dies
stellt sicher, dass bei einem Zustandswechsel alle relevanten Informationen direkt
Konzept
55
zur Verfügung stehen und der Server die einzelnen Clients bei Broadcasts nicht
unterscheiden muss.
Aus dieser Liste kann der Benutzer nun einen Eintrag auswählen und diesem
Beitreten. Hierbei wird passend zu dieser Aktion eine Nachricht an den Server
geschickt, wie in Abbildung 5.21 dargestellt ist. Dazu liest der GUI-Manager den
Namen der ausgewählten Lobby direkt aus dem Element aus und sendet diesen
an das Frontend-Objekt. Dieser Name wird zusammen mit der passenden Aktion
über die Netzwerk-Schnittstelle als Ereignis an den Server geschickt. Ebenfalls nach
diesem Prinzip verläuft auch das Erstellen, Verlassen und Starten einer Lobby, sowie
das Entfernen eines Benutzers als Ersteller einer Lobby ab. In allen Fällen wird ein
Ereignis an den Server geschickt, welches dieser je nach Aktion und Daten passend
verarbeiten muss.
Abbildung 5.21: Bei einfachen Benutzerinteraktionen werden die Daten direkt an
den Server geschickt
Das Betreten einer Lobby wird in Abbildung 5.22 behandelt. Hierbei wird das
Backend-Objekt über die Netzwerkschnittstelle mit der Verbindung des Benutzers,
welcher die Nachricht geschickt hat, der Aktion und dem Lobbynamen als Datensatz
aufgerufen. Danach holt sich diese Klasse aus der Liste der Benutzer das Objekt
mit der gesetzten Verbindung. Mittels dieses Benutzer-Objekts kann nun auch das
passende Lobby-Objekt geladen werden, in dem sich der Benutzer aktuell befindet.
Danach wird die Instanz des Benutzers hinzugefügt und eine Nachricht über den
erfolgreichen Beitritt in die Lobby über die Netzwerk-Schnittstelle an den Client
zurückgeschickt. Außerdem wird auch die aktualisierte Liste der Benutzernamen in
dieser Lobby an alle Teilnehmer verschickt.
Abbildung 5.23 zeigt auf, was beim Beitreten einer Lobby clientseitig geschieht.
Das Ereignis kommt als Nachricht über die Netzwerk-Schnittstelle mit dem Namen
der beigetreten Lobby an. Das Frontend-Objekt erstellt daraufhin eine neue Lobby
mit diesem Namen und gibt dabei gleich mit an, ob der aktuelle Benutzer der
Ersteller der Lobby ist. Da der Benutzer in diesem Fall einer Lobby beigetreten
ist, kann er nicht der Ersteller sein. Sobald das Lobby-Objekt erstellt ist, wird eine
Aktualisierung an den GUI-Manager signalisiert, woraufhin dieser zur Spiel-Ansicht
56
Konzept
Abbildung 5.22: Beim Eintreten in eine Lobby wird die Teilnehmer-Liste aktualisiert
und verschickt
wechselt. Sollte der Benutzer eine Lobby erstellt haben, so erhält der Client eine
ähnliche Nachricht nur mit einer anderen Aktion, woraufhin bei der Erstellung des
Lobby-Objekts der aktuelle Benutzer als Ersteller markiert wird.
Abbildung 5.23: Nach dem Erstellen des Lobby-Objekts gelangt der Spieler in die
Spiel-Ansicht
Sobald der Benutzer einer Lobby beitritt, erhält er auch die aktuelle Liste aller
Benutzer in dieser Lobby, welche dann im Client gespeichert wird. Dabei kommt die
Benutzer-Liste, wie in Abbildung 5.24 zu sehen ist, über die Netzwerk-Schnittstelle
zum Frontend-Objekt, welches die Liste im Lobby-Objekt abspeichert und danach
dem GUI-Manager eine Aktualisierung mitteilt. Dieser holt sich die Benutzer-Liste
Konzept
57
und zeigt sie an. Diese Sequenz wird bei allen Veränderungen der Teilnehmer
innerhalb der Lobby durchlaufen.
Abbildung 5.24: Die Benutzerliste wird im Lobby-Objekt gespeichert und vom
GUI-Manager angezeigt
Wenn der Ersteller der Lobby mit den Teilnehmern zufrieden ist, kann er die Lobby
zum Spiel überführen. Hierzu kann er über einen Klick ein Ereignis an den Server
schicken, welches nach dem gleichen Muster wie auch das Beitreten einer Lobby
(siehe Abbildung 5.21) abläuft. Die serverseitige Verarbeitung ist in Abbildung 5.25
dargestellt.
In diesem Fall liefert die Netzwerk-Schnittstelle wieder das Ereignis an das BackendObjekt, welches sich das Benutzer-Objekt aus der Benutzer-Liste holt, welches das
Ereignis abgeschickt hat. Der Benutzername wird dann genutzt um das LobbyObjekt zu holen, in dem sich der aktuelle Benutzer befindet. Falls der Benutzer
der Ersteller der Lobby ist, wird dieses Lobby-Objekt gestartet. Hierzu werden alle
Benutzer-Objekt in der Benutzer-Liste des Lobby-Objekts durchlaufen, wobei bei
jedem Eintrag das Spiel gestartet wird. Hierbei erhält die Instanz des Benutzers ein
neues Spielfeld, welches initial mit acht Kugelreihen mit jeweils 17 zufälligen Kugeln
gefüllt wird. Außerdem wird dem Benutzer noch eine neue Inventar-Instanz angefügt, sowie die Kugelfarbe für die erste bewegliche Kugel zufällig festgelegt. Nach
diesem Setup wird ein Broadcast an alle Spieler in der Lobby über den bevorstehenden Spielstart verschickt. Danach holt sich das Backend-Objekt eine vereinfachte
Darstellung aller Spielfelder, durchläuft diese Liste und verschickt jedes Spielfeld
mit dem Namen des Benutzers an alle Benutzer im Spiel. Danach wird jedem Spieler
noch die Farbe seiner ersten beweglichen Kugel mitgeteilt.
58
Konzept
Abbildung 5.25: Beim Starten des Spiels erhält jeder Spieler ein neues Spielfeld,
Inventar und die Farbe der ersten beweglichen Kugel. Die Spielfelder und die
Kugelfarbe werden danach verschickt
Konzept
59
Die einzelnen Spielfelder werden clientseitig nach dem Muster aus Abbildung 5.26
behandelt. Nachdem die Netzwerk-Schnittstelle die Informationen an das BackendObjekt gegeben hat, speichert dieses das Spielfeld mit dem Benutzernamen im
Lobby-Objekt. Danach benachrichtigt das Backend-Objekt den GUI-Manager über
die Aktualisierung und übergibt dabei den Benutzernamen des jeweiligen Nutzers,
bei dem sich das Spielfeld verändert hat. Der GUI-Manager holt sich die Informationen aus dem Lobby-Objekt und aktualisiert das jeweilige Spielfeld. Diese
Aktualisierung läuft auch in allen anderen Fällen, in denen sich ein Spielfeld verändert, so ab – also auch beim Hinzufügen einer Kugel und beim Benutzen eines
Extras.
Abbildung 5.26: Das Spielfeld wird im Lobby-Objekt gespeichert und die Anzeige
über die Spielerposition aktualisiert
Die erste bewegliche Kugel wird ebenfalls über die Netzwerk-Schnittstelle und das
Frontend-Objekt an den GUI-Manager geleitet, welcher eine neue Kugel mit der
erhaltenen Farbnummer erzeugt. Diese Kugel bleibt solange an ihrem Startpunkt,
bis der Benutzer auf das Spielfeld klickt und den Abschuss auslöst, welcher in
Abbildung 5.27 dargestellt ist.
Zuerst wird die Entfernung vom Ursprung der Kugeln zu der geklickten Stelle
berechnet und auf eine Geschwindigkeit normiert, sodass sich alle Kugeln mit der
gleichen Geschwindigkeit bewegen. Danach wird die Instanz der beweglichen Kugel in diese Richtung gestartet und erhält außerdem noch das Frontend-Objekt als
Referenz, um über dieses das eigene Spielfeld auslesen zu können. Bei jedem Aufruf
der Bewegungs-Methode wird zunächst die Kugelposition mit der Bewegungsrichtung aktualisiert. Danach holt sich das Kugel-Objekt über das Frontend-Objekt
den aktuellen Spielernamen aus dem Benutzer-Objekt, um dann damit das eigene
vereinfachte Spielfeld laden zu können. Auf diesem Spielfeld wird nun überprüft,
ob die bewegliche Kugel auf eine andere Kugel auf dem Spielfeld getroffen ist. Da
sich das Spielfeld auch während des Fluges beispielsweise durch die Benutzung
60
Konzept
Abbildung 5.27: Die bewegliche Kugel wird nur clientseitig bewegt – der Server
erhält erst die finale Position
von Extras verändern kann, muss bei jedem Durchlauf das aktuelle Spielfeld zum
Abgleich genutzt werden. Danach wird die neue Position im GUI-Manager aktualisiert, sodass der Spieler sie auch sehen kann. Ist die Kugel auf eine Kugelgruppe
oder den oberen Spielfeldrand getroffen, so wird die finale Position der Kugel vom
Frontend-Objekt als Ereignis an die Netzwerk-Schnittstelle geschickt.
Das Setzen der neuen Kugel läuft nach dem in Abbildung 5.28 dargestellten Muster
ab. Zunächst erhält das Backend-Objekt das Ereignis und holt sich das passende
Benutzer-Objekt zu der Verbindung. Danach wird die finale Position der Kugel an
das Objekt übergeben, welches wiederum diese Daten zusammen mit der Nummer
der Kugelfarbe an die Instanz des Spielfeldes weiterleitet. Hier wird zunächst eine
neue Kugel erzeugt und an dieser Position in das Spielfeld eingefügt.
Konzept
61
Abbildung 5.28: Das serverseitige Hinzufügen einer Kugel kann verschiedene Konsequenzen haben
62
Konzept
In diesem Ablauf kann es passieren, dass die anzufügende Kugel außerhalb des
Spielfelds liegt und der Benutzer somit verloren hätte. In diesem Fall würde der
aktuelle Ablauf zunächst im Feld- und dann im Benutzer-Objekt sofort beendet
werden, woraufhin das Backend-Objekt eine negative Rückmeldung bekommt und
somit erkennt, dass der Benutzer das Spiel verloren hat. Dieses Szenario soll jedoch
separat beschrieben werden – in dem aktuellen Fall liefert das Anfügen der Kugel
eine positive Antwort. Nachdem die Kugel angefügt wurde, wird als nächstes die
Größe der Kugelgruppe an der neuen Kugel ermittelt, bei der alle Kugeln die gleiche
Farbe haben wie die neue. Falls diese Gruppe aus weniger als drei Elementen besteht,
wird der Kugelvorrat um eins reduziert. Dies kann dazu führen, dass dieser danach
null ist. In diesem Fall wird eine neue Reihe angefügt, wobei auch hier wieder
überprüft wird, ob eine Kugel aus dem Spielfeld herausläuft – sollte dies der Fall
sein, verliert der Spieler. Andernfalls wird der Kugelvorrat wieder aufgefüllt. Sollte
hingegen die Kugelgruppe mehr als drei gleichfarbige Kugeln enthalten, werden
diese entfernt. Hierbei wird die Anzahl der entfernten Kugeln gespeichert, da bei
mehr als fünf entfernten Kugeln eine zufällige Kugel auf dem Spielfeld zu einem
zufälligen Extra aufgewertet wird. Dies ist auch der Fall, wenn beim Entfernen
von schwebenden Kugeln mehr als fünf Kugeln abgebaut werden. Beim Abbauen
können außerdem auch Extras entfernt werden, welche in einer Liste gesammelt
zum Benutzer-Objekt zurückgegeben werden – dieses fügt die Extras nach dem
Entfernen zu seinem Inventar-Objekt hinzu. Da der Benutzer noch nicht verloren
hat, erhält das Backend-Objekt eine positive Antwort und versendet als nächstes die
aktualisierten Daten. Zunächst holt es sich den Namen der Lobby in dem sich der
Spieler befindet und versendet dann an alle Teilnehmer das Spielfeld des Benutzers
zusammen mit seinem Namen. Im Client erfolgt die Aktualisierung dabei wie bereits
in Abbildung 5.26 dargestellt. Da sich außerdem auch das Inventar des Spielers
verändert haben kann, erhält dieser hierfür eine Aktualisierung, welche im Client
nach dem Muster aus Abbildung 5.20 verarbeitet wird.
Das Benutzen eines Extras aus dem Inventar wird clientseitig ebenfalls direkt als
Ereignis an den Server geschickt, wie es auch bei dem Beitreten einer Spiellobby der
Fall ist. Der Server verarbeitet die Nachricht passend zum Typ des Extras, welches
der Benutzer zuerst in seinem Inventar hat. Verändert das Extra das Spielfeld eines
Benutzers, wird dieses Spielfeld passend manipuliert und alle Benutzer erhalten die
neue Version des Spielfeldes. Handelt es sich hingegen um ein aktives Extra, wird
dieses an den jeweiligen Client geschickt, welchen der Benutzer beim Versenden
des Extras ausgewählt hat. In beiden Fällen erhält der initiierende Benutzter eine
aktualisierte Version seines Inventars.
Wurde ein aktives Extra an einen Benutzer verschickt, so verläuft die clientseitige
Verarbeitung wie in Abbildung 5.29 dargestellt. Dem Benutzer-Objekt wird zunächst
Konzept
63
ein neues Extra mit einer Wirkdauer hinzugefügt. Falls noch kein Timer-Objekt
läuft, wird eins gestartet, welches die verbleibenden Zeiten der einzelnen Extras im
Sekundentakt wieder verringert. Dieses Verringern und das daraus resultierende
Aktualisieren der Anzeige finden solange statt, wie es noch aktive Extras gibt.
Abbildung 5.29: Erhält der Benutzer aktive Extras, werden diese im Client gehalten
und aktualisiert
Durch das Abschießen von Kugeln und das Benutzen von Extras kommt es dazu,
dass die Spielfelder der einzelnen Spieler überfüllt werden. In diesem Fall werden
die bei Abbildung 5.28 beschriebenen alternativen Routen abgelaufen. Hierbei wird
das Spielfeld im Benutzer-Objekt geräumt und alle Mitspieler erhalten ein Ereignis,
welches sie über das Ausscheiden eines Mitspielers informiert. Clientseitig wird
dazu das Spielfeld bei den anderen Teilnehmern ebenfalls geräumt und Extras,
welche zu diesem Spieler geschickt werden, kommen ungenutzt zurück. Der ausgeschiedene Benutzer kann dann zwar nicht mehr am Spiel teilnehmen, sieht aber
noch die Spielfelder der anderen Mitspieler, bis er das Spiel verlässt. Sobald es
nur noch einen verbleibenden Spieler gibt, erhält dieser die Mitteilung über seinen
Sieg ebenfalls durch ein Ereignis. Nachdem das Spiel vorbei ist, können alle Spieler
dieses verlassen, ins Hauptmenü zurückkehren und dort wieder eine neue Lobby
erstellen.
64
Konzept
Kapitel 6
Implementierung
Die bisher gesammelten Anforderungen und das daraus resultierende Konzept
bildet nun die Grundlage für die Implementierung. Dazu wird zunächst eine passende Technologie ausgewählt, mit der sich das hier präsentierte Design am besten
umsetzen lässt. Danach soll auf Basis dieser Technologie damit begonnen werden
die einzelnen Bereiche der Applikation umzusetzen, wobei dazu zunächst die Netzwerkschicht, dann der Server- und zuletzt der Clientpart beschrieben werden.
6.1
Auswahl der Technologien
Da die Applikation clientseitig in einem Browser laufen soll, bietet sich die Nutzung
von HTML an, da diese Sprache von allen Webbrowsern interpretiert werden kann.
Um die Elemente der Anzeige steuerbar zu machen, wird außerdem auf JavaScript
und jQuery1 gesetzt, wobei letzteres einige Vereinfachungen bietet und Fallbacks für
ältere Browser bereitstellt. Dadurch ist es möglich die gesamte Applikation in einer
Seite ablaufen zu lassen und passend auf die einzelnen Aktionen des Benutzers oder
Aktualisierungen aus dem Netzwerk reagieren zu können.
Für die Kommunikation mit dem Server soll primär auf das Websocket-Protokoll
gesetzt werden, da dieses zum aktuellen Stand die am meisten verbreitete und
bei Datenübertragungen auch schnellste Technologie im Web-Kontext ist, welche
außerdem auch die Kommunikation in Echtzeit unterstützt. Für die serverseitige
Umsetzung soll hierbei node.js eingesetzt werden. Dieses Framework basiert auf
JavaScript und ist eine ideale Grundlage zur Entwicklung von Serverstrukturen.
Hierzu reagiert es auf Ereignisse und kann passend dazu die jeweiligen Aktionen
ausführen. Außerdem ist das Framework vollständig skalierbar und passt sich
1 http://jquery.com/
65
66
Implementierung
automatisch an die aktuelle Netzwerklast an.
Zwar unterstützt node.js selbst nicht das Websocket-Protokoll, jedoch können Erweiterungen diese Funktionalität hinzufügen. Eine einfache Implementierung des
Websocket-Protokolls bietet die bereits in Abschnitt 3.5 erwähnte Implementierung
„WebSocket Node“ von Brian McKelvey. Mit dieser wäre bereits ein großer Teil der
modernen Browser, auf Desktop-Computern sowie auch im mobilen Bereich, in
der Lage mit dem Server kommunizieren zu können. Damit aber auch alle anderen
Browser die Applikation nutzen können, wird die Erweiterung Socket.IO genutzt.
Dieses Framework implementiert verschiedene Fallbacks für nahezu alle Browser
und bietet diese über eine einzige Schnittstelle an, welche unabhängig von der
Technologie angesprochen werden kann. Außerdem bietet es auch eine erweiterte
Verwaltung der einzelnen Verbindung an: man kann Benutzer direkt benachrichtigen, einen globalen Broadcast an alle Benutzer oder einen Broadcast an eine zuvor
festgelegte Gruppe von Benutzern verschicken.
Als Technologien auf Seite der Clients werden somit HTML und CSS zur Visualisierung und JavaScript zusammen mit jQuery und dem clientseitigen Skript von
Socket.IO zur Datenhaltung und -manipulation genutzt. Der Server basiert auf
node.js mit dem Framework Socket.IO.
6.2
Netzwerkschicht
Die Netzwerkschicht sorgt für die Übertragung der Daten zwischen den beiden
Applikationshälften und ist deshalb das Element, welches zuerst beschrieben wird.
Zunächst geht es dabei um den Aufbau einer Verbindung und welche Schritte dabei
zum Versenden einer Nachricht nötig sind. Danach werden spezielle Formen des
Nachrichtenversands erläutert, beispielsweise das Verschick einer Nachricht an alle
Benutzer einer zuvor festgelegten Benutzergruppe. Nachdem die verschiedenen
Versandtechniken geklärt sind, werden auch die verschiedenen Nachrichtenarten
beleuchtet. Hierbei spielt vor allem die teilweise Aktualisierung eines Objektes, das
Delta-Update, eine wichtige Rolle. Am Ende soll dann noch beschrieben werden,
wie diese verschiedenen Funktionalitäten nach außen propagiert werden, also wie
die Schnittstelle der Netzwerkschicht aussieht und aufgebaut ist.
Für das Erstellen einer Verbindung wird zunächst ein Server benötigt, welcher die
verschiedenen Dienste über das Netzwerk anbietet. Der Code für eine einfache
Version eines solchen Servers ist im Listing 6.1 zu sehen. Dabei werden zunächst
alle benötigten Pakete importiert, welche für das Aufsetzen der Serverfunktionalität
wichtig sind. Diese Pakete sind in node.js primär JavaScript-Dateien, welche wiederum andere Dateien importieren können. Hierbei kann eine Datei für eine oder
Implementierung
67
auch mehrere Klassen stehen. Für die spätere Benutzung dieses Pakets wird es als
Variable abgelegt und steht dann unter diesem Variablennamen zur Verfügung.
1
2
3
4
5
6
7
var http = require(’http’);
var PORT
= 80;
var server = http.createServer(function(request, response) {
response.writeHead(200);
response.write(’Hello World’);
response.end();
}).listen(PORT);
Listing 6.1: Ein einfacher HTTP-Echo-Server
In diesem Fall wird nur das Paket zum Erstellen eines HTTP-Servers importiert, auf
dessen Grundlage dann ein einfacher Echo-Server aufgesetzt wurde. Dieser liefert
zu jeder eingehenden Anfrage eine statische Nachricht. Hierzu wird die CallbackFunktion, welche innerhalb von createServer() festgelegt wurde, aufgerufen und
erhält die Objekte für die Anfrage und die Antwort. Dieser Server kann nun so
erweitert werden, dass er die URL der Anfrage auswertet, die angeforderte Zieldatei
sucht und diese als Antwort zurücksendet. Hierdurch entsteht ein einfacher HTTPServer, welcher auf Benutzeranfragen reagiert und Daten zurückliefern kann.
Auf diesen HTTP-Server soll nun das Websocket-Protokoll aufgesetzt werden. Da
dieses auf dem gleichen Handshake basiert, wird es auf den bereits bestehenden
HTTP-Server aufgesetzt. Wie in Listing 6.2 aufgeführt ist, muss auch hier als erstes
das jeweils benötigte Paket importiert werden, welches in diesem Fall Socket.IO ist.
Danach wird das Websocket-Protokoll auf den HTTP-Server gelegt, wodurch bereits
Websocket-Verbindungen aufgebaut werden können. Um die dabei entstehenden
Nachrichten abfangen und verarbeiten zu können, werden zuletzt noch die passenden Callbacks registriert. In diesem Fall reagiert der Server zunächst auf eingehende
Verbindungen und bei diesen dann auf Nachrichten und den Verbindungsabbruch.
1
2
3
4
5
6
7
var io = require(’socket.io’);
var socketio = io.listen(server);
var mainsocket = socketio.sockets.on(’connection’, function (socket) {
console.log(’connected’);
socket.on(’message’, function(message) {console.log(message);});
socket.on(’disconnect’, function() {console.log(’disconnect’);});
});
Listing 6.2: Der Websocket-Server baut auf dem bestehenden HTTP-Server auf
Um diese Websocket-Verbindung nutzen zu können, muss der Client erweitert
werden. Dafür soll eine leere HTML-Seite dienen, welche die clientseitige Implementierung von Socket.IO importiert. Außerdem bindet sie auch den Code aus
Listing 6.3 ein und startet diesen nach dem Ladevorgang. Dadurch wird eine
68
Implementierung
Websocket-Verbindung zum lokalen Server aufgebaut. Danach werden auf diesen Socket verschiedene Callbacks registriert, welche je nach Ereignis aufgerufen
werden. In diesem Fall wartet der Client auf den Aufbau der Verbindung und sendet dann über diese eine Nachricht an den Server. Dieser kann die Nachricht dann
verarbeiten und sie in der Konsole ausgeben.
1
2
3
4
5
6
7
8
socket = io.connect(’http://localhost/’);
socket.on(’connect’, function() {
socket.emit(’message’, ’hello socket world’);
});
socket.on(’message’, function() (message) {
console.log(message);
});
Listing 6.3: Der Aufbau der Socket.IO-Verbindung auf Clientseite
Über diese Verbindung können nun verschiedene Informationen übertragen werden.
Dabei beschränken sich die Daten nicht nur auf einfache Zeichenketten – es können
auch JSON-Objekte verschickt werden, wie Listing 6.4 aufzeigt. Beim Ziel kommen
die Daten ebenfalls in diesem Format an und können dann übern den jeweiligen
Namen ausgelesen werden.
1
2
3
4
5
6
7
8
9
socket.emit(’message’, {msg: ’hello socket world’,
browser: navigator.appName, mood: {name: ’happy’,
category: ’positive’}});
socket.on(’message’, function(message) {
console.log(’new message from ’ + message.browser);
console.log(’saying: ’ + message.msg);
console.log(’being: ’ + message.mood.name);
});
Listing 6.4: Versenden, Empfangen und Auslesen von JSON-Objekten mittels
Socket.IO
Hat man mehrere Benutzer, so ist es auch sinnvoll, Nachrichten gleichzeitig an
alle diese Nutzer zu verschicken. Hierzu dient der Broadcast, welcher ebenfalls
von Socket.IO zur Verfügung gestellt wird und im Listing 6.5 dargestellt ist. Dieser
Code befindet sich serverseitig innerhalb des connection-Callbacks und sendet
zunächst einen Broadcast vom neuen Socket aus an alle anderen Sockets im System.
Dies bedeutet, dass die neue Verbindung diese Nachricht nicht erhält. Die zweite
Nachricht hingegen wird an alle verbundenen Sockets geschickt, also auch an den
neuen Benutzer.
Implementierung
1
2
69
socket.broadcast.emit(’message’, ’new user connected’);
mainsocket.emit(’message’, ’we have ’ + curUserCount + ’ users online’);
Listing 6.5: Globales Verteilen von Informationen mittels Broadcasts in Socket.IO
Manchmal will man jedoch nur eine bestimmte Gruppe von Benutzern über etwas
informieren, beispielsweise sollen nur die Benutzer einer Lobby eine Liste der darin
befindlichen Teilnehmer erhalten. Hierzu bieten sich Räume an, welche Socket.IO
ebenfalls bereitstellt und welche im Listing 6.6 gezeigt werden. Die Verbindungen
abwechselnd in zwei unterschiedliche Räume hinzugefügt. Diese Räume können
dann, unter Angabe des Namens, ebenfalls mit Broadcasts benachrichtigt werden.
1
2
3
4
5
6
7
if(currentUserCount % 2 == 0) {
socket.join(’even’);
mainsocket.to(’even’).emit(’message’, ’secret message for even room’);
} else {
socket.join(’odd’);
mainsocket.to(’odd’).emit(’message’, ’secret message for odd room’);
}
Listing 6.6: Benutzer können in Räume gefasst werden, welche dann lokale
Broadcasts unterstützen
Um verschiedene Nachrichtentypen unterscheiden zu können, ist es außerdem
möglich, Nachrichten nicht nur als message zu verschicken, sondern eigene Typen
anzugeben und diese beim Empfangen gesondert zu verarbeiten. Wie Listing 6.7
zeigt, können weitere Nachrichtentypen direkt beim Versenden angegeben und für
das Empfangen registriert werden, wodurch besser auf verschiedene Informationsarten reagiert werden kann. Dies wird in der aktuellen Applikation genutzt, um
Ereignis- und Daten-Nachrichten zu unterscheiden.
1
2
3
4
5
socket.emit(’usercount’, currentUserCount);
socket.on(’usercount’, function(message) {
console.log(’### NEW USERCOUNT: ’ + message + ’ ###’);
});
Listing 6.7: Der Typ der Nachricht kann variiert werden, um verschiedene
Informationsarten zu unterscheiden
Nachdem nun Daten-Nachrichten separat behandelt werden können, müssen die
hierfür benötigten Delta-Updates realisiert werden. Für diese Applikation spielen
bei der Übertragung nur Arrays eine Rolle, da die verschiedenen Informationen,
welche als Daten-Nachricht zwischen Server und Client ausgetauscht werden müssen, immer als Array vorliegen. Hierfür muss immer der letzte übertragene Stand
gehalten werden, damit dieser mit den neuen Informationen abgeglichen werden
70
Implementierung
kann. Dieser Abgleich und das Erstellen des Deltas wird im Listing 6.8 gezeigt.
Hierzu werden die beiden Datenstände an die Funktion übergeben, welche sie dann
abgleicht und die Aktualisierungen zurückgibt. Als erstes wird dazu ein leeres
Array erstellt, in dem die einzelnen Aktualisierungen gesammelt werden. Dann
werden alle Elemente der beiden Arrays durchlaufen. Um dies zu erreichen, wird
die Länge des größeren Arrays zum iterieren verwendet. In der Schleife werden
dann die jeweiligen Elemente beider Arrays an einer Stelle verglichen. Falls dabei
das neue Array diese Stelle nicht hat, muss sie auch aus dem alten gelöscht werden.
Sollte jedoch das neue Array eine Stelle mehr haben als das alte, wird das Element
aus dem neuen Array übernommen. Dies geschieht ebenfalls, wenn beide Arrays
zwar Elemente an dieser Stelle haben, diese jedoch unterschiedlich sind. Sind die
beiden Elemente hingegen Arrays, wird der Vergleich rekursiv durchgeführt und
bei Veränderungen an das Delta angehängt. Der Aufbau des Deltas ähnelt dabei
einer beim IETF2 gelisteten Vorlage für Delta Updates bei JSON-Dokumenten [2].
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function getDelta(o, n) {
var delta = [];
var i = (o.length > n.length) ? o.length : n.length;
while(i--) {
if(n[i] == null) {
delta.push({"op": "del", "pos": i});
} else if(o[i] == null) {
delta.push({"op": "mod", "pos": i, "val": n[i]});
} else if(Array.isArray(o[i]) && Array.isArray(n[i])) {
var cDelta = getDelta(o[i], n[i]);
if(cDelta.length > 0) {
delta.push({"op": "array", "pos": i, "val": cDelta});
}
} else if(o[i] !== n[i]) {
delta.push({"op": "mod", "pos": i, "val": n[i]});
}
}
return delta;
}
Listing 6.8: Die Unterschiede zwischen den Arrays werden mit der jeweiligen
Position, dem Typ und dem Wert in einem Delta gesammelt
Diese Deltas sparen vor allem bei kleinen Aktualisierungen in großen Datenmengen
sehr viel Bandbreite, was beispielsweise bei dem Setzen einer neuen Kugel auf dem
Spielfeld von Vorteil ist. Liegt jedoch kein alter Datenstand vor, müssen alle Daten
neu übertragen werden, was bei dieser Art von Delta jedoch auch sehr viel Overhead
bedeutet. Deshalb sollen Daten nur dann auf diese Weise übertragen werden, wenn
2 "http://www.ietf.org/"
Implementierung
71
es bereits einen alten Datenstand gibt. Andernfalls wird die neue Datenmenge als
Ganzes ohne Delta übertragen.
Kommt ein Delta-Update an, muss dieses den alten Datenstand aktualisieren, wie
es der Code aus Listing 6.9 tut. Dabei werden für jeden Eintrag aus dem Delta die
einzelnen Informationen für die Aktualisierung ausgelesen. Je nach Typ wird dann
entweder der Wert überschrieben, das Element gelöscht oder rekursiv mit einem
Array gefüllt. Am Ende entsteht dadurch das aktuelle Array.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function patch(o, delta) {
for(var i = 0; i < delta.length; i++) {
var operation = delta[i].op;
var position = delta[i].pos;
var value = delta[i].val;
if(operation == "mod") {
o[position] = value;
} else
if(operation == "del") {
o.splice(position,1);
} else
if(operation == "array") {
o[position] = patch(o[position], value);
}
}
return o;
}
Listing 6.9: Das alte Array wird mit jedem Eintrag im Delta aktualisiert
Diese verschiedenen Funktionalitäten müssen nun noch an die Manager von Client
und Server angebunden werden. Hierzu erhält die Netzwerkschicht beim Instanziieren eine Referenz auf die handle()-Methoden der jeweiligen Manager-Klasse, welche
für die Verwaltung von Nachrichten zuständig ist. Dadurch werden eingehende
Nachrichten passend zu ihrem Typ direkt zu dem jeweiligen Callback weitergeleitet
und von diesem verarbeitet. Für das Senden von Informationen bietet die Schicht
die bereits in den Abschnitten 5.3 und 5.4 aufgezeigten Methoden an.
6.3
Server
Der Server wurde nach dem Konzept aus Abschnitt 5.3 umgesetzt, weshalb die
meisten Details hierzu bereits vorliegen. Aus diesem Grund wird in diesem Abschnitt nur die Anbindung der Callbacks an die Netzwerkschicht und die generelle
72
Implementierung
Nutzung von Objekten in JavaScript beschrieben. Die detaillierte Umsetzung der
einzelnen Klassen wird nicht behandelt.
Für die Anbindung an die Netzwerkschicht werden die beiden Handler-Methoden
des Backend-Objekts beim Instanziieren an diese übergeben. Über diese Handler
können nun eingehende Nachrichten zum passenden Callback weitergeleitet werden. Um das Hinzufügen von eigenen Callbacks zu vereinfachen, werden diese
nicht explizit in einer Map mit ihrer Aktion verlinkt, sondern in einen JavaScript
Namespace geschrieben. Dies ist ein Objekt, welches verschiedene Variablen und
Methoden sammelt. Diese können zum einen klassisch über den Methodennamen
aufgerufen werden:
1
myNamespace.method(data);
Jedoch ist es auch möglich, auf die einzelnen Bestandteile per Zeichenkette zuzugreifen:
1
myNamespace[method](data);
Dabei kann die Zeichenkette frei gewählt werden, wodurch es möglich wird, eine
Nachricht über einen String aufzurufen. Dies ist vor allem im vorliegenden Fall
besonders gut geeignet, da auf diese Weise neue Callbacks schnell angebunden
werden können. Hierzu wird der Namespace um eine Methode mit dem Namen
der Aktion erweitert und kann dann bereits über eine Nachricht angesprochen
werden. Diese Verarbeitung von Nachrichten ist server- und clientseitig identisch,
mit dem Unterschied, dass der Server noch die Verbindung, also den Socket der
Nachrichtenquelle an das Callback übergibt. Eine vollständige Implementierung
eines solchen Handlers und des dazugehörigen Namespaces wird im Listing 6.10
dargestellt. Hierbei werden die Methoden im Namespace über den Handler mit den
jeweiligen Daten aufgerufen.
1
2
3
4
5
6
7
8
9
10
11
function handle(action, data) {
myFunctions[action](data);
}
var myFunctions = {
log: function(data) {console.log(data);},
wraplog: function(data) {this.log(’>~~~> ’ + data + ’ <~~~<’);}
}
handle(’log’, ’message 1’);
handle(’wraplog’, ’beautiful message 2’);
Listing 6.10: Mit Namespaces können Funktionen über einen String aufgerufen
werden
Implementierung
73
Wie bereits an den Namespaces zu sehen ist, gibt es in JavaScript keine klare Unterscheidung zwischen verschiedenen Objektarten. Auch die Typen der Extras basieren
auf Variable aus Namespaces, da JavaScript keine eigene Implementierung für Enumerations liefert. Auch gibt es keine Klassen, wie man sie beispielsweise aus Java
kennt. Hier setzt JavaScript auf Funktionen, welche durch Variablen und Methoden
erweitert werden können und dann über new() zu instanziieren sind.
Ein Beispiel für eine solche Klasse zeigt Listing 6.11. Hierbei wird zunächst die Klasse
User mit dem Attribut username erstellt, welches beim Instanziieren gesetzt wird.
Weitere Attribute können dabei entweder direkt in dieser Funktion gesetzt werden,
oder aber auch später in den anderen Methoden angefügt werden. Diese anderen
Methoden werden über den Prototypen des neuen Objektes gesetzt, da ansonsten
jede neue Instanz auch alle Methoden neu instanziieren würde, wodurch es zu
unnötigen Speicherverbrauch kommt. Dieses Objekt kann entweder direkt in dem
Code eingebaut werden, in dem es auch genutzt wird, oder aber auch ausgelagert
werden. Dabei sollten die einzelnen Objekte als Modul exportiert werden.
1
2
3
4
5
6
7
8
9
10
11
12
13
function User(username) {
this.username = username;
}
User.prototype.setUsername = function(username) {
this.username = username;
}
User.prototype.setUsername = function() {
return this.username;
}
module.exports.User = User;
Listing 6.11: Klassen basieren in JavaScript auf Funktionen, welche dazu erweitert
werden können
Nach dem Einbinden ist es dann möglich, ein neues Objekt zu erzeugen, welches
sich dann wie jede andere Variable verhält. Auf Basis dieser Technik wurden auch
alle anderen Klassen des Servers umgesetzt. Aus diesem Grund werden diese nicht
explizit beschrieben.
74
6.4
Implementierung
Client
Auch die Klassen im Client basieren auf den Informationen aus Abschnitt 5.4, welche nach dem Muster aus Listing 6.11 umgesetzt wurden. Auch die Anbindung
der einzelnen Callbacks an den Server verläuft so, wie bereits im Listing 6.10 gezeigt wurde. Die Implementierung dieser Elemente soll deshalb nicht nochmals
beschrieben werden. Vielmehr soll hier die Ausgabe der Informationen an den
Benutzer behandelt werden. Dabei wird zuerst gezeigt, wie Benutzereingaben abgefangen und verarbeitet werden. Danach werden die Aktualisierungen der einzelnen
Anzeigeelemente behandelt. Dabei wird zunächst gezeigt, wie einzelne Elemente
einmalig ausgetauscht werden und dann, wie man dies so erweitern kann, dass
auch Animationen durchgeführt werden können.
Zum Abfangen von Benutzereingaben soll neben JavaScript auch die Bibliothek
jQuery genutzt werden, welche viele nützliche Vereinfachungen und Erweiterungen für JavaScript bietet. Nachdem diese Bibliothek importiert ist, kann man über
den Namespace $ auf die einzelnen Objekte der Website zugreifen und diese manipulieren, wie im Listing 6.12 gezeigt wird. In diesem Beispiel wird jeweils ein
Klick-Handler mit einem Callback an alle input-Objekte sowie an das Element mit
der ID clickfield angebunden. Außerdem wird auch ein Tastatur-Handler für die
gesamte Seite registriert. In den einzelnen Callbacks können dann die Eingaben
ausgewertet werden. So kann beispielsweise die Position des geklickten Elements
innerhalb von anderen Elementen geliefert werden, während beim Spielfeld die
Koordinaten der geklickten Stelle ausgelesen werden können. Bei den Tastatureingaben kann ebenfalls abgefangen werden, welche Tasten der Benutzer gedrückt
hat.
1
2
3
4
5
$(function () {
$("input").click(buttonClick);
$("#clickfield").click(fieldClick);
$(document).keypress(handleKeypress);
});
Listing 6.12: jQuery vereinfacht das Anbinden von Handlern zur Laufzeit
Sobald die Aktionen verarbeitet wurden, gibt es in den meisten Fällen eine Ausgabe,
welche wieder an den Benutzer gelangen soll. Hierzu müssen die Objekte der
Anzeige mit den neuen Informationen aktualisiert werden. Hierzu bietet jQuery
ebenfalls einige Erweiterungen an, welche im Listing 6.13 aufgeführt sind. Dabei
kann ein Element neue Daten angefügt bekommen oder gleich den gesamten Inhalt
neu setzen. Außerdem können auch die style-Attribute ausgelesen und angepasst
werden, um so Elemente neu zu positionieren oder ein- und auszublenden.
Implementierung
1
2
3
4
75
$("#output1").append("a");
$("#output2").html("Neuer Inhalt");
$("#rect").css("display", "block");
$("#rect").css({top: 100, left: 50});
Listing 6.13: Die Manipulation von Elementen ist mittels jQuery ebenfalls schnell
umzusetzen
Verbindet man diese Funktionalitäten mit einem Interval, können damit Animation
umgesetzt werden, wie Listing 6.14 zeigt. Hierbei wird ein neues Interval mit einem
Callback und einer Zeitangabe in Millisekunden registriert, welches dann das Callback solange aufruft, bis dieses Interval wieder gelöscht wird. Speichert man sich
die Koordinaten und die Bewegungsrichtungen ab, kann man dadurch ein Element
in einem anderen hin- und herfliegen lassen. Dies ist auch die Grundlage für die
Animation der Kugeln auf dem Spielfeld.
1
2
3
4
5
6
7
8
9
10
11
12
13
var moveInterval = setInterval(move,10);
function move() {
x += xDir;
y += yDir;
$("#rect").css({top: y, left: x});
if(x < 0) xDir = Math.abs(xDir);
if(x > 600) xDir = -1*Math.abs(xDir);
if(y < 0) yDir = Math.abs(yDir);
if(y > 280) yDir = -1*Math.abs(yDir);
}
clearInterval(moveInterval);
Listing 6.14: Ein Interval ruft die angegebene Funktion alle 10 Millisekunden auf,
wodurch eine Animation entsteht
Basierend auf diesen Funktionalitäten können die verschiedenen Anwendungsfälle
im GUI-Manager umgesetzt werden. Sobald neue Informationen über das Netzwerk
angekommen und abgespeichert sind, wird der GUI-Manager über die Aktualisierung benachrichtigt. Dieser holt sich dann die jeweils benötigten Daten von den
Daten-Objekten über das Frontend-Objekt und aktualisiert die Anzeige.
76
Implementierung
Kapitel 7
Schlusswort
Die in dieser Arbeit beschriebenen und bei der Implementierung genutzten Technologien sind für die hier behandelte Applikation sehr passend, wodurch eine
Umsetzung aller funktionalen und nicht-funktionalen Anforderungen gelang. Deshalb können die Technologien der einzelnen Anwendungsbereiche klar empfohlen
werden, auch wenn es Alternativen gibt.
Node.js bietet eine stabile und vor allem skalierbare Serverarchitektur, welche zudem durch verschiedene Erweiterungen an den jeweiligen Anwendungsfall angepasst werden kann. So gestaltet sich auch die Anbindung von Websockets durch
Socket.IO als völlig problemlos, was dadurch zu einer sehr mächtigen Serverinfrastruktur führt. Auch die Anbindung der Clients an den Server ist dank Socket.IO
schnell erledigt und durch eine abstrahierte Netzwerkschicht lassen sich viele verschiedene Ereignisse und Daten direkt zwischen den Applikationselementen austauschen. Die Darstellung ist dank html5 und jQuery sowohl einfach als auch mächtig
– für jede Aufgabe kann eine passende Funktionalität geschaffen werden. Alle hier
vorgestellten Technologien werden außerdem auch weiterhin durch eine große
Community und viele engagierte Entwickler gepflegt, wodurch man für die nähere
Zukunft mit vielen Neuerungen rechnen kann.
Es wurde aber auch gezeigt, dass bereits die nächsten Technologien in den Startlöchern stehen. Allen voran ist WebRTC zu erwähnen, welches die Kommunikation
im Web nochmals stark verändern kann – durch die Nutzung von Peer-to-PeerVerbindungen werden die Server stark entlastet, was bei den stetig wachsenden
Datenmengen für ein entkoppeltes Wachstum der Dienste sorgen kann. Insgesamt
befindet sich die Entwicklung der verteilten Systeme und des Cloud Computing
noch in den Anfängen, hat jedoch bereits zu diesem Zeitpunkt eine enorme Nachfrage. Die Echtzeitkommunikation im Web-Kontext bleibt daher auf jeden Fall ein
Hauptentwicklungsgebiet im Internet.
77
78
Schlusswort
Literaturverzeichnis
[1] ArcadeHistory. puzzle bobble [coin-op] arcade video game, taito corp. (1994).
[letzter Zugriff: 10. August 2013].
[2] Ed. P. Bryan and Inc. ForgeRock US. A json media type for describing partial
modifications to json documents. [letzter Zugriff: 21. September 2013].
[3] Michelle Bu and Eric Zhang. Peerjs - peer-to-peer data in the web browser.
[letzter Zugriff: 21. September 2013].
[4] caniuse. Can i use web sockets. [letzter Zugriff: 20. August 2013].
[5] I. Fette, Google Inc., A. Melnikov, and Isode Ltd. Rfc 6455 - the websocket
protocol. [letzter Zugriff: 17. August 2013].
[6] R. Fielding, UC Irvine, J. Gettys, Compaq/W3C, J. Moguland Compaq, H. Frystyk, W3C/MIT, L. Masinter, Xerox andP. Leach, Microsoft, and T. Berners-Lee.
Rfc 2616 - hypertext transfer protocol – http/1.1. [letzter Zugriff: 20. August
2013].
[7] Jesse James Garrett. Ajax: A new approach to web applications. [letzter Zugriff:
16. August 2013].
[8] D. Gourley, B. Totty, M. Sayer, A. Aggarwal, and S. Reddy. HTTP: The Definitive
Guide. O’Reilly Media, 2009.
[9] ITWissen. Echtzeit :: realtime :: Rt :: Itwissen.info. [letzter Zugriff: 11. September
2013].
[10] A.B. Johnston and D.C. Burnett. Webrtc: APIs and Rtcweb Protocols of the Html5
Real-Time Web. CreateSpace, 2012.
[11] Brian McKelvey. Worlize/websocket-node. [letzter Zugriff: 21. September
2013].
[12] Peter Mell and Timothy Grance. The nist definition of cloud computing. [letzter
Zugriff: 22. September 2013].
79
80
Literaturverzeichnis
[13] Guillermo Rauch. Socket.io: the cross-browser websocket for realtime apps.
[letzter Zugriff: 21. September 2013].
[14] StatCounter. Statcounter global stats - browser, os, search engine including
mobile market share. [letzter Zugriff: 21. September 2013].
[15] UniversalLexikon. Flimmerverschmelzungsfrequenz. [letzter Zugriff: 21. September 2013].
Herunterladen