blog.atwork.at

news and know-how about microsoft, technology, cloud and more.

Etwas HTTP und viel ASP.NET SignalR

Als Anwender sind wir es mittlerweile schon gewohnt, dass wir Aktualisierungen in Webseiten ohne Page-Reload erhalten. Große Webseitensysteme wie Facebook, Twitter und Co zeigen uns dies täglich. Alle diese Systeme verwenden denselben Mechanismus: Der Datentransfer findet asynchron zwischen Server und Client in Echtzeit statt.

ASP.NET SignalR eBook

Wie man das mit modernsten Technologien bewerkstelligen kann, zeigt das freie eBook ASP.NET SignalR. Hier möchte ich paar wichtige Themen aus diesem coolen Buch von Jose M. Aguilar über die Kommunikation per HTTP kurz zusammenfassen und einen raschen Überblick liefern.

signalr-done

HTTP – synchrone Kommunikation

Das Hypertext Transfer Protocol (HTTP 1.0) wird seit 1990 im World-Wide Web verwendet, es ist bekanntlich ein stateless protocol und besitzt - für heutige Verhältnisse - viele Einschränkungen, schließlich findet die Kommunikation zwischen Client und Server synchron per Request/Response statt. Der Client muss nach seinem Request warten, bis der Server seine Response sendet.

AJAX – asynchron im Hintergrund

Per AJAX (Asynchronous JavaScript And XML) können Daten asynchron vom Client zum Server gesendet werden, ohne dass die Webseite verlassen wird. Ein AJAX-Request kann Daten in verschiedensten Formen liefern, als HTML, JSON, XML. Hierbei geht die Aktion jedoch immer vom Client aus, der entscheidet, wann ein Request zum Server erfolgt.

Polling – der Client fragt nach

Mit Polling führt der Client periodische Connections zum Server durch, um nachzusehen, ob es relevante Aktualisierungen am Server gibt. Polling ist relativ einfach und funktioniert in jedem Browser. Die “Kosten” dafür sind jedoch eine hohe Nutzung der Bandbreite und Prozesse auf beiden Seiten, je höher die Aktualisierungsfrequenz ist, desto mehr Daten müssen transportiert und verarbeitet werden. Mit intelligentem Polling – zum Beispiel bei einem E-Mail Client, der nur dann auf neue Nachrichten prüft, wenn der Benutzer eine Aktion im Browser durchführt – kann dieser Traffic jedoch auch wieder stark reduziert werden.

Push – der Server sendet

Wenn der Server Daten für den Client bereitstellt, wird ein Push-Mechanismus benötigt, wie es beispielsweise das IRC oder SMTP-Protokoll verwenden. Für Echtzeitkommunikation benötigt der Server eine Point-to-Point Verbindung zum Client. Dies ist jedoch technisch nicht möglich, verschiedene Transportschichten dazwischen wie Firewalls, Router oder Proxy können diese Verbindung unterbinden. Daher wurde oft auf einen Polling-Mechanismus ausgewichen. Eine Umgehung davon sind aktive Komponenten, die in Webseiten eingebettet sind und die Kommunikation über WebSockets herstellen, wie zum Beispiel Java oder Silverlight. Diese Technologien werden allerdings nach und nach durch HTML5-Technologien abgelöst.

Websockets – immer offen

Das W3C hat ein Development API vorgeschlagen (derzeit im Entwurfsmodus), das eine persistente Verbindung zwischen Client und Server herstellt, die offen bleibt. Somit ist eine Zwei-Wege Kommunikation zwischen den beiden Partnern zu jeder Zeit möglich. Websockets werden derzeit von den meisten aktuellen Browsern wie IE10, Chrome, Firefox, Opera und Safari unterstützt, in anderen (älteren) Browsern sind Websockets einfach nicht verfügbar, eine Liste findet sich hier: http://caniuse.com/websockets.

Auch die Webserver müssen Websockets beherrschen, bei Microsoft werden diese erst in den neuesten Produkten und Technologien nativ unterstützt: IE10, ASP.NET 4.5, WCF, IIS8. Websockets werden über Javascript und mit dem ws:// Protokoll verwendet. Websockets sind die Technologie der Zukunft um Push-Dienste in Echtzeit zu verwenden.

Weitere Technologien

Es gibt noch weitere Push-Methoden, etwa Server-Sent Events, wo der Client eine Verbindung öffnet und sich dann auf eine Event-Quelle “subscribiert”, dies ist jedoch uni-direktional und nur ein Datentransfer vom Server zum Client. Diese Technologie ist ebenfalls noch im W3C-Entwurfsmodus. Bei Long-Polling bleibt die Verbindung solange geöffnet, bis der Server Daten sendet, oder ein Timeout eintritt. In beiden Fällen wird danach sofort wieder eine neue Connection aufgebaut. Forever Frames werden mit <iframe src=”/forever”> umgesetzt, der Server hält die Verbindung permanent offen und sendet Aktualisierungen in Form von Script-Aufrufen, die am Client laufen.

Endlich: SignalR

ASP.NET SignalR is a new library for ASP.NET developers that makes it incredibly simple to add real-time web functionality to your applications.”, so lautet die Definition auf signalr.net. SignalR ist ein Framework aus .NET Bibliotheken kombiniert mit JavaScript Bibliotheken wie jQuery, entstanden aus einem privaten Projekt von David Fowler und Damian Edwards, die beide im ASP.NET-Team bei Microsoft arbeiten. Sie hielten übrigens eine sehr gute Session auf der Build Conference 2012, siehe http://channel9.msdn.com/Events/Build/2012/3-034 und weitere Webcasts (siehe unten).

asp-net-signalr

SignalR befreit den Webentwickler, sich mit solchen grundlegenden Technologien wie oben kurz beschrieben zu befassen, sondern vermittelt den Eindruck, dass man mit einer permanent geöffneten Verbindung arbeitet. Das Framework kümmert sich um das “Wie”. Je nachdem, welche Technologien vorhanden sind, verwendet SignalR Long Polling, Server-Sent Events oder Websockets. Das ist praktisch, denn es gibt mehrere Fallback-Mechanismen. Welche Technologie verwendet wird, machen sich Server und Client beim Verbindungsaufbau aus: “negotiation” (auch wenn man das Internetprotokoll auch selbst aussuchen kann, aber das macht wohl nur in wenigen Szenarien Sinn).

Weiters kümmert sich das Framework automatisch um den Verbindungsstatus und öffnet bei Abbruch eine Connection erneut. SignalR ermöglicht ein einheitliches Programmiermodell – eine Art virtuelle Verbindung über immer dieselbe API -, das unabhängig von der darunter verwendeten Technologie und Verbindung ist und so sehr bequem zu verwenden.

Es ist aber noch mehr in SignalR drin: Das Framework enthält einen Messaging Bus, der Nachrichten zwischen Server und Clients sicher übermittelt. So kann der Server den Verbindungsstatus überwachen und besitzt auch einen Mechanismus, um Nachrichten an alle verbundenen Clients zu senden, unabhängig von Transfermechanismus, Bandbreitengeschwindigkeit, Latenz, Fehlern etc. Kurz: Mit SignalR können asynchrone, Multi-User Applikationen einfach gebaut werden.

In SignalR existiert eine Zwei-Wege Abstraktionsschicht. Über den Internetprotokollen und dem Transport-Layer existiert ein Layer “Persistent Connections” (nahe an der tatsächlichen Verbindung, ähnlich der Programmierung mit Sockets, auch wenn es sich um eine virtuelle Verbindung handelt) und darüber der Layer der “Hubs” (das Programmiermodell, das ganz unabhängig von der darunterliegenden Verbindung verwendet wird).

SignalR ist cool! Im eBook schlägt der Autor Jose M. Aguilar in die gleiche Kerbe: “An application that combines internet, asynchrony, and multiple users cooperating and interacting at the same time always deserves a “wow!”.”

Installation

Diese erfolgt am einfachsten in Visual Studio über NuGet:

Install-Package Microsoft.AspNet.SignalR

Auch ein SignalR-Beispiel kann so installiert werden. Das Beispiel ist jedoch nur in VS 2012 lauffähig.

Install-Package Microsoft.AspNet.SignalR.Sample

In NuGet können alle verfügbaren Pakete leicht ausgelesen werden:

Get-Package microsoft.aspnet.signalr –ListAvailable –pre

Id Version Description/Release Notes
-- ------- -------------------------
Microsoft.AspNet.SignalR 1.0.0 Incredibly simple real-time web for .NET....
Microsoft.AspNet.SignalR.Cl... 1.0.0 .NET client for ASP.NET SignalR.
Microsoft.AspNet.SignalR.Core 1.0.0 Core server components for ASP.NET SignalR.
Microsoft.AspNet.SignalR.JS 1.0.0 JavaScript client for ASP.NET SignalR.
Microsoft.AspNet.SignalR.Owin 1.0.0 OWIN componenets for ASP.NET SignalR.
Microsoft.AspNet.SignalR.Redis 1.0.0-rc3 Redis messaging backplane for scaling out of ASP.NET SignalR applications in a web-farm.
Microsoft.AspNet.SignalR.Se... 1.0.0-rc3 Service Bus messaging backplane for scaling out of ASP.NET SignalR applications in a web-farm.
Microsoft.AspNet.SignalR.Sy... 1.0.0 Components for using ASP.NET SignalR in applications hosted on System.Web.
Microsoft.AspNet.SignalR.Utils 1.0.0 Command line utilities for ASP.NET SignalR, including performance counter installation and Hub JavaScri...
Microsoft.AspNet.SignalR.Sa... 1.0.0 A simple fake stock ticker sample for ASP.NET SignalR.

Für Console-Applikationen wird beispielsweise das generische Microsoft.AspNet.SignalR.Client Paket verwendet. Der Download von SignalR kann auch direkt von der http://signalr.net/ Website erfolgen, NuGet vereinfacht den Import in ein Projekt allerdings ungemein.

Step by Step Demo

Wie SignalR verwendet wird, beschreibt das eBook ausführlich. Wer es lieber live sehen möchte findet hier einen sehr kompakten Webcast von Damian Edwards, David Fowler und Brady Gaster auf Channel9, wo Damian von Beginn an (ab 3:53 bis 11:20) ein kleines Demo mit einer dragable Box in mehreren Browserfenstern aufbaut:

http://channel9.msdn.com/Shows/Web+Camps+TV/Damian-Edwards-and-David-Fowler-Demonstrate-SignalR

signalr-webcast

Die Webseite wird in mehreren verschiedenen Browserfenstern geöffnet. Sie zeigt eine kleine blaue Box, die mit der Maus verschoben werden kann. Jedes Verschieben positioniert die Box automatisch in allen geöffnete Browserfenstern an dieselbe Position.

Es wird ein ASP.NET Webproject erstellt. Nach dem Setzen der Referenzen zu SignalR wird wird eine serverseitige Class erstellt, die von SignalR.Hubs erbt. Clientseitig verwendet SignalR einen Proxy – wenn es ein Browser ist mit Javascript-Code. So können Server und Client Daten austauschen.

Der Beispiel-Code für das Demo sieht etwa so aus: Die Klasse am Server:

using Microsoft.AspNet.SignalR;

namespace MoveShapeDemo
{
// Am Client so ansprechen...:
//[HubName("moveShape")]
public class MoveShapeHub : Hub
  {
  public void MoveShape(int x, int y)
   {
   // Clients ist ein dynamic und kommt aus der Hub-Class.
   // Wir rufen eine Methode shapeMoved am Client per RPC auf.
   // Übergeben wird die ConnectionID, damit wir wissen, wer der Client ist sowie weitere Parameter für Koordinaten.
   Clients.shapeMoved(Context.ConnectionId, x, y);
   }
  }
}

Nun folgt der Client-Teil in MoveShape.js:

/// <reference path="jquery-1.7.2.js" />
/// <reference path="jquery.signalR-0.5.2.js" />
/// <reference path="jquery-ui-1.8.20.js" />
// Document Load Handler
$(function () {
// Referenz auf den Hub: (aus MoveShape.cs: public class MoveShape : Hub)
// sozusagen die Client-Side version. Wir holen ein DOM Element per ID:
var hub = $.connection.MoveShapeHub,
  $shape = $("#shape");
// Nun die Methoden für den Server zum Broadcast:
// Mit Standard-jQuery Extend werden neue Members zum Hub-Objekt erstellt (Clients.shapeMoved)
$.extend(hub, {
  shapeMoved: function (cid, x, y) {
   // aber bitte nur Moves verwenden, die nicht von mir selbst sind:
   if ($.connection.hub.id !== cid) {
    // wir positionieren den shape per jQuery auf eine neue Position:
    $shape.css({ left: x, top: y });
   }
  }
});
// nun die Connection starten:
$.connection.hub.start().done(function () {
  // wir schalten draggable für den shape ein:
  $shape.draggable({
   drag: function () {
    // wenn drag passiert, informieren wir den Server mit der Position aus dem DOM:
    // public void MoveTheShape(int x, int y)
    hub.MoveShape(this.offsetLeft, this.offsetTop || 0);
   }
  });
});
});

Jetzt kommt noch die HTML-Seite MoveShape.html. Hier werden die erforderlichen JavaScript-Bibliotheken eingebunden, plus eine Referenz für SignalR um einen Proxy zu generieren: <script src="/signalr/hubs"></script>

 

<!DOCTYPE html>
<html>
<head>
    <title>MoveShape Web</title>
    <style>
        html, body {
            padding: 0; margin: 0;
            width: 100%; height: 100%;
            font-family: 'Segoe UI', Arial;
        }
        #shape {
            width: 100px;
            height: 100px;
            background-color: #0094ff;
            cursor: move;
        }
        #clientCount {
            position: absolute;
            top: 0;
            right: 10px;
            font-size: 72px;
            color: #cdcdcd;
        }
    </style>
</head>
<body>
    <div id="shape"></div>
    <div id="clientCount"></div>
   
    <script type="text/javascript" src="../Scripts/jquery-1.7.2.js"></script>
    <script type="text/javascript" src="../Scripts/jquery-ui-1.8.20.js"></script>
    <script type="text/javascript" src="../Scripts/jquery.signalR-0.5.2.js"></script>
    <script type="text/javascript" src="/signalr/hubs"></script>
    <script type="text/javascript" src="MoveShape.js"></script>
</body>
</html>

In Application_Start muss die Route für die Hubs eingetragen werden:

// Register the default hubs route: ~/signalr/hubs
RouteTable.Routes.MapHubs();

Demo Download

Das ganze funktionierende Demo kann von https://github.com/DamianEdwards/SignalR-MoveShapeDemo downgeloadet werden (jedoch noch mit einer älteren Version von SignalR – fürs erste Durchsehen ist das allerdings ausreichend). Das Demo wird sogar noch mit einer Desktop-Variante abgerundet, die dieselbe Funktionalität besitzt und die Box in den Browserfenstern mitbewegt.

Das Ergebnis in drei verschiedenen Browsern: Das Verschieben der blauen Box in einem beliebigen Browser ändert sofort die Position in allen anderen Browsern. Ein cooles Demo, das zeigt, dass der Nachrichtenfluss zwischen Server und Clients einwandfrei funktioniert.

MoveShapeDemo

Auf der github-Seite https://github.com/DamianEdwards von Damian sind eine Reihe weiterer Beispiele zu SignalR zu finden.

Viel Spaß beim Evaluieren und selbst Testen von SignalR – entdeckt den Coolness-Faktor dieses ASP.NET Frameworks.

Update 20.04.2013: siehe auch cooles Tutorial High-Frequency Realtime with SignalR auf der ASP.NET Website!

Loading