Ein Beispiel für die Implementierung einer Echtzeit-Textkonversation mit der SignalR-Bibliothek
Autor: Denis Balant, Enej Hudobreznik
Für die Datenübertragung im Internet wird nach wie vor überwiegend das HTTP-Protokoll (Hypertext Transfer Protocol) verwendet. sein moderneres und sichereres verschlüsseltes Derivat HTTPS (HTTP Secure). Sie basieren auf einer Client-Server-Architektur, bei der der Client (englischer Client) eine Anfrage (englischer HTTP-Request) an den Server (englischer Server) sendet und der Server darauf mit einer Antwort (englischer HTTP-Response) antwortet.
Für einfache Request-Response-Übertragungen (z. B. Übertragung einer HTML-Datei auf Anfrage des Clients) eignen sich die Protokolle HTTP und HTTPS. Mithilfe dieser beiden Protokolle kann der Benutzer aktualisierte Daten nur erhalten, wenn er eine neue Anfrage sendet. Sie eignen sich daher nicht am besten für die Echtzeit-Datenübertragung (z. B. Online-Chat, Online-Videospiele, Standortverfolgung usw.). Ein Großteil der Funktionalität moderner Webanwendungen basiert darauf. Agilität kann auf viele verschiedene und bessere Arten umgesetzt werden, jede mit ihren eigenen Vor- und Nachteilen.
Die einfachste Implementierungsmethode ist das regelmäßige Polling, bei dem der Client in jedem Polling-Intervall wiederholt Anfragen nach neuen Daten an den Server sendet, unabhängig davon, ob sich der Inhalt überhaupt geändert hat. Trotz seiner Einfachheit eignet sich dieser Ansatz nicht für größere Dienste, bei denen der Zugriff auf den Server viele Clients erfordert, da eine große Menge unnötiger und wiederholter Anfragen ihn überlasten und somit die Leistung des Dienstes verlangsamen kann.
Die Verbesserung wird durch die Einführung einer Langzeitabfrage erreicht, bei der der Client auf die gleiche Weise Anfragen an den Server sendet, der Server jedoch erst dann darauf antwortet, wenn er das Vorhandensein neuer Daten erkennt. Der Nachteil eines solchen Ansatzes besteht darin, dass dem Client ein bestimmtes Zeitintervall definiert werden muss, nach dem er die Inaktivität des Servers als Fehler erkennt. Dies erhöht die Komplexität des Client- und Server-Setups zusätzlich.
Der moderne HTML5-Standard bietet auch eine API namens Server-Sent Events. Es handelt sich um ein Protokoll, bei dem der Client nicht regelmäßig Anfragen an den Server senden muss, sondern der Server die erforderlichen Daten erneut sendet, wenn Änderungen vorgenommen werden, wodurch die Kommunikation in Echtzeit erleichtert wird.
Der HTML5-Standard bietet außerdem das WebSockets-Protokoll, das eine echte bidirektionale Echtzeitkommunikation ermöglicht. Zu Beginn der Einrichtung wird ein Handshake durchgeführt, bei dem sich Client und Server auf die von ihnen verwendeten Standards einigen. Nach einem erfolgreichen Handshake wird mit einer kleinen Verzögerung eine dauerhafte Verbindung zwischen den beiden Teilnehmern geöffnet. Dieses Protokoll ist besonders nützlich, wenn es um eine Punkt-zu-Punkt-Verbindung (eine direkte Verbindung zwischen einem Client und einem Server) geht.
Neben der klassischen Client-Server-Architektur gibt es auch andere Lösungen, bei denen die Verbindung zwischen Clients nicht über Server erfolgt, sondern direkt zwischen Clients, die sowohl die Rolle des Clients als auch des Servers übernehmen (diese Peer-to-Peer-Netzwerke). Ein beliebtes Protokoll für die Echtzeitkommunikation in solchen Netzwerken ist WebRTC.
In der .NET-Umgebung können wir für die Echtzeitkommunikation die Open-Source-Bibliothek SignalR verwenden, die Teil des ASP.NET Core-Webframeworks ist, sodass wir sie problemlos als zusätzliche Middleware-Ebene zu bestehenden Projekten hinzufügen können Bearbeitung eingehender Anfragen. Die Bibliothek simuliert Methodenaufrufe auf dem Client von der Serverseite und Methodenaufrufe auf dem Server von der Clientseite (Remote Procedure Call – RPC), übernimmt jedoch keine Gewähr für die Angemessenheit der Nr. Parameter und ihre Typen. Vom Server aus können wir Methoden auf allen Clients, auf einer bestimmten Gruppe von Clients oder nur auf einem Client aufrufen.
Sein Hauptvorteil ist die einfache Implementierung dank der Softwareentwicklungspakete (SDK) für viele verschiedene Plattformen, die das Innenleben des Dienstes vor den Programmierern verbergen, und die Möglichkeit eines einfachen Hostings mit dem Azure SignalR-Dienst. Ein zusätzlicher Vorteil besteht darin, dass der Dienst selbst den am besten geeigneten Verbindungstyp (oben beschrieben) zwischen dem Client und dem Server auswählt.
Die Nutzung der Bibliothek basiert auf speziellen Klassen namens Hubs, die eine Abstraktion der Verbindung zwischen Clients und Server darstellen. Für sie definieren wir Methoden, die Clients auf dem Server aufrufen können.
Das folgende Beispiel zeigt ein Beispiel für eine einfache Textkonversationsimplementierung. Zuerst definieren wir den Basisknoten für die Konversation auf dem Server. Die Methoden dieser Klasse stellen Methoden dar, die von Clients auf dem Server aufgerufen werden können. Beim Aufruf der SendMessage-Methode sendet der Client die Benutzer-ID (userId) und den Inhalt der Nachricht (message), während der Server die ReceiveMessage-Methode auf allen Clients mit den angegebenen Parametern aufruft.
Das folgende Beispiel zeigt eine JavaScript-Webclient-Implementierung unter Verwendung der offiziellen Bibliothek (@microsoft/signalr). Clients stellen eine Verbindung zur Knoten-URL her (/chat im Beispiel unten). Eine Verbindung wird durch das Verbindungsobjekt dargestellt. Die on-Methode stellt einen Listener für den Methodenaufruf dar, dessen Name als erster Parameter und als zweiter die Funktion angegeben wird, die auf dem Ereignis ausgeführt wird. Wir implementieren Methoden auf dem Server über die Aufrufmethode des Verbindungsobjekts, die als erstes Argument den Namen der Methode gefolgt von ihren Parametern erfordert.
Standardmäßig werden Daten zwischen Server und Clients im JSON-Format übertragen, die Bibliothek unterstützt jedoch auch das effizientere MessagePack-Format.
Obwohl die Bibliothek die Entwicklung erheblich erleichtert, hat sie auch Nachteile. Einer der Hauptgründe ist die Skalierbarkeit des Systems für Selbsthosting und die Gewährleistung eines unterbrechungsfreien Betriebs, da seine Implementierung nur sehr schwer auf mehrere separate Server verteilt werden kann. Dies erweist sich als besonders schwierig, wenn wir die Latenz für Remote-Benutzer reduzieren und mehrere verschiedene Standorte hosten möchten, da das Lösungsdesign auf nur einen beschränkt ist. Außerdem gewährleistet die Bibliothek keine zuverlässige Zustellung und Bestellung von Nachrichten, was das Benutzererlebnis beeinträchtigen kann.