V.NET04-06

Werbung
Stand 04-2006
Institut für Systemarchitektur
Lehrstuhl Rechnernetze
Praktikum Rechnernetze
Komplex II – Programmierung verteilter Anwendungen
Praktikumsversuch .NET
- Webservices und .NET-Remoting im Vergleich –
Praktikumsdokumentation
-1-
Stand 04-2006
1
Einleitung ...................................................................................................................... - 3 -
2
Grundlagen .NET ......................................................................................................... - 3 2.1
Common Language Runtime (CLR) .................................................................. - 4 -
2.1.1
Common Intermediate Language ................................................................... - 5 -
2.1.2
Common Type System ................................................................................... - 6 -
2.1.3
Assemblies ..................................................................................................... - 6 -
2.1.4
Laufzeit-Host und Anwendungsdomänen ...................................................... - 7 -
2.1.5
Ausführung von verwaltetem Code................................................................ - 8 -
2.2
Klassenbibliothek ................................................................................................. - 8 -
2.2.1
3
2.3
Auslieferungsformen von .NET .......................................................................... - 9 -
2.4
Windows Communication Foundation............................................................. - 10 -
Webservices & .NET-Remoting ................................................................................ - 11 3.1
5
Webservices......................................................................................................... - 11 -
3.1.1
Standards ...................................................................................................... - 11 -
3.1.2
Umgebung .................................................................................................... - 11 -
3.1.3
Internet Information Server .......................................................................... - 11 -
3.1.4
virtuelle Verzeichnisse ................................................................................. - 12 -
3.1.5
Vorgehen bei der Erstellung eines Webservices .......................................... - 12 -
3.1.6
Beispiel Webservice ..................................................................................... - 13 -
3.2
4
Organisation der Klassenbibliothek ............................................................... - 8 -
.NET Remoting ................................................................................................... - 14 -
3.2.1
Remoting-Architektur .................................................................................. - 14 -
3.2.2
Eigenschaften entfernter Objekte ................................................................. - 15 -
3.2.3
Nutzung eines entfernten Objektes .............................................................. - 15 -
3.2.4
Beispiel .NET-Remoting .............................................................................. - 16 -
Der Versuch ................................................................................................................ - 19 4.1
Versuchsbeschreibung ....................................................................................... - 19 -
4.2
Sicherung der Versuchsergebnisse ................................................................... - 20 -
Literatur ...................................................................................................................... - 21 -
-2-
Stand 04-2006
1 Einleitung
"42 - Das ist die Antwort auf die ultimative Frage in Per Anhalter durch die Galaxis von
Douglas Adams ... .Net - Das ist die Antwort auf die ultimative Frage, die sich Microsoft in
den letzten Jahren gestellt hat." Mit dieser Aussage beginnt Ralf Westphal in [1] seine
Darstellung der .NET-Technologie. Die Frage, die sich Microsoft (MS) seit Mitte der 90iger
Jahre gestellt hat ist allerdings deutlich leichter formulierbar, als Douglas Frage "nach dem
Leben, dem Universum und dem ganzen Rest". Sie lautet in etwa folgendermaßen: "Wie kann
eine homogene, leistungs- und anpassungsfähige Middlewareplattform mit hohem
Abstraktionsgrad und intuitiver Handhabung zur Erstellung von (verteilten) Anwendungen
und Komponentensystemen geschaffen werden, die zudem konkurrenzfähig zu aktuellen
Entwicklungen auf diesem Gebiet ist?". Wie von Seiten Microsofts auf die Frage grundlegend
geantwortet wurde, wird in der Folge zunächst dargestellt. Dabei wird insbesondere auf zwei
für verteilte Anwendungen relevante Teilbereiche der .NET-Technologie, Webservices und
.NET Remoting, eingegangen. Bei der Bearbeitung des Versuches werden die dazu
gemachten Ausführungen angewandt.
2 Grundlagen .NET
Das Wesen von .NET ist überwiegend geprägt durch einen Kompromiss zwischen
Interoperabilität mit früheren Entwicklungen von Microsoft, insbesondere COM+ und
zwischen offenen Standards. Maßgeblicher Standard bildet dabei die Common Language
Infrastructure (CLI), die auf sprach- und plattformunabhängige Anwendungsentwicklung
abzielt. Mit dieser Öffnung hin zu anderen Technologien und firmenfremden Standards
vollzog Microsoft einen deutlichen Strategiewechsel, der das eigentlich Innovative dieser
Plattform auszeichnet.
Die technologische Basis der .NET-Technologie bildet zum einen eine mächtige
Laufzeitumgebung, die so genannte Common Language Runtime (CLR) und zum anderen
eine umfangreiche Klassenbibliothek. Diese Struktur ist in Abbildung 1 dargestellt.
.NET-Framework
Programmiersprachen
Klassenbibliothek
Werkzeuge
Laufzeitumgebung
Betriebssystem
Abbildung 1: Struktur der .NET-Plattform
-3-
Stand 04-2006
2.1
Common Language Runtime (CLR)
Kurz gesagt stellt die CLR die Umgebung für die Ausführung von Programmen in Form einer
virtuellen Maschine dar, die als Abstraktion von einer konkreten Maschine fungiert. Die CLR
lässt sich prinzipiell in ihrer Funktionalität und dem Ablauf der Programmausführung mit der
Java Virtual Machine (JVM) vergleichen, unterscheidet sich allerdings in zwei Punkten
maßgeblich:

striktere Definition des Verhaltens der abstrakten Maschine
Während in der Spezifikation der JVM über zahlreiche Details wie z.B. der Form der
Codeausführung, bei der es sich im Fall der JVM prinzipiell auch um eine Interpretation
handeln kann, keine Angaben gemacht werden, schreibt Microsoft eine Umwandlung in
nativen Code vor.

Sprachunabhängigkeit
Die JVM wurde ausschließlich für die Ausführung von Bytecode geschaffen, der aus
Java-Code erzeugt wurde. Auch wenn Ansätze existieren, diese Abbildung aus anderen
Sprachen auf Java Bytecode heraus zu ermöglichen, sind diese überwiegend als unsauber
zu bewerten. Erzielt wird die Sprachunabhängigkeit bei .NET durch das Common Type
System.
Wie der zweite Punkt andeutet, ist ein entscheidendes Design-Kriterium von .NET die
Unabhängigkeit dieser Schicht von einer Programmiersprache. So existieren neben den so
genannten .NET-Sprachen wie C# oder Visual C++ Unterstützung für eine sehr breite Palette
weiterer Sprachen, wie Smalltalk, Perl, Python oder Haskell.
Die Sprachunabhängigkeit besagt nicht, dass beliebige Sprachen innerhalb der eigentlichen
Laufzeitumgebung unterstützt werden, sondern dass für die jeweilige Sprache eine Abbildung
auf die Common Intermediate Language (CIL) vorliegt, sprich ein Programm dieser Sprache
mittels eines Compilers in die CIL übersetzt werden kann. Dieser Zwischencode wiederum
wird entweder zur Laufzeit (Just in Time) von der CLR in nativen Code übersetzt und
ausgeführt oder aber im Vorfeld durch ein Programm (z.B: Ngen.exe) in nativen Code
umgewandelt, der damit allerdings seine Plattformunabhängigkeit verliert.
Als Ausführungsumgebung verwaltet die CLR auszuführenden Code, stellt Anwendungen
Basisdienste bereit und erfüllt damit hauptsächlich folgende Aufgaben:

Speicherverwaltung bzw. Heap-Verwaltung samt Freigabe nicht mehr benötigter
Speicherbereiche (Garbage Collection – siehe dazu [5])

Verwaltung
des
Zugriffs
auf
die
Klassenbibliothek
Betriebssystemfunktionalitäten (inklusive Threadverwaltung)

Bereitstellung einer sicheren Ablaufumgebung (auch für nicht vertrauenswürdigen
Code)

Unterstützung der Interoperabilität zwischen allen verfügbaren Sprachen

Durchführung der Just-In-Time-Compilierung von Common Intermediate LanguageCode (s.u.)

Laden von Programmen samt Auffinden aller Programmkomponenten
und
auf
Die Struktur der CLR mit ihren wichtigsten Teilbereichen während der Abarbeitung einer
Programmkomponente (Assembly) ist in Abbildung 2 dargestellt.
-4-
Stand 04-2006
CLR
Anwendungsdomäne
Programmlader
Stack
Assembly
Klasse A
Heap
Klasse B
JIT-Compiler
Garbage-Collector
Metadaten
Laufzeit-Host
Abbildung 2: Überblick über die CLR (in Anlehnung an [2], S. 126)
2.1.1 Common Intermediate Language
Die CIL (früher Microsoft Intermediate Language – MSIL) orientiert sich in ihrem Aussehen
stark an einem abstrakten nativen Befehlssatz eines Prozessors. Ihr liegt eine virtuelle StackMaschine mit entsprechend zahlreichen Stack-Operationen zugrunde. Wie bereits erwähnt
handelt es sich bei einem Programm in CIL allerdings nicht um lauffähigen Code, sondern
dieser wird grundsätzlich in den Code der real zugrundeliegenden Maschine übersetzt. Die
Übersetzung erfolgt nicht für eine komplette Anwendung, sondern nur für die Teile bzw.
einzelne Methoden der Anwendung, die tatsächlich benötigt werden, was zu einer
Effizienzsteigerung und kürzeren Start-Up-Verzögerung führt.
Der Vorteil einer Zwischensprache, wie es die CIL ist liegt neben
Plattformunabhängigkeit und Abstraktion von einer tatsächlichen Maschine in
Möglichkeit, Code vor der Ausführung auf Zuverlässigkeit und Sicherheit, insbesondere
Typsicherheit zu überprüfen. Zudem wird somit eine spezifische Codeoptimierung für
.NET-Technologie ermöglicht.
der
der
auf
die
Es besteht ein sehr starker Zusammenhang zwischen der CIL und dem zugrunde liegenden
Typsystem, was sich in einer engen Verknüpfung des Befehlssatzes mit dem CTS
widerspiegelt. So wird beispielsweise das so genannte Boxing und Unboxing, also die
Umwandlung eines gewöhnlichen Werttypen (int, short, ...) in ein Objekt und umgekehrt von
der Zwischensprache unmittelbar unterstützt.
Programme in CIL-Code werden auch als verwalteter (managed) Code bezeichnet.
Abgrenzend dazu wird Code, der außerhalb der CLR ausgeführt wird als unmanaged
bezeichnet.
Abbildung 3 verdeutlicht den Weg von Quellcode unterschiedlicher Programmiersprachen
zum ausführbaren Maschinencode.
-5-
Stand 04-2006
C#-Code
C#
C++
VB
…
C#-
C++-
VB
…-
Compiler
Compiler
Compiler
Compiler
if (a > b) max = a; else max = b;
CIL-Code
CIL-Code
(+ Metadaten)
IL_0004: ldloc.0
IL_0005: ldloc.1
IL_0006: ble.s IL_000c
IL_0008: ldloc.0
IL_0009: stloc.2
IL_000a: br.s
IL_000e
IL_000c: ldloc.1
IL_000d: stloc.2
Maschinencode
Lader
mov ebx,-4[ebp]
mov edx,-8[ebp]
cmp ebx,edx
jle 17
mov ebx,-4[ebp]
mov -12[ebp],ebx
...
Verifizierer
JIT-Compiler
Maschinencode
Abbildung 3: Generierung von CIL- und Maschinencode (aus [3], S. 5)
2.1.2 Common Type System
Massgebliches Problem bei der Herstellung von Interoperabilität zwischen
Programmiersprachen bilden unterschiedliche Typsysteme, also die Festlegung, welche
einfachen (ganze Zahlen, Wahrheitswerte etc.) bzw. welche komplexen Typen (Strukturen,
Klassen etc.) existieren und wie diese repräsentiert werden. Somit liegt es nahe, einen
kleinsten gemeinsamen Nenner bezüglich der zu unterstützenden Typen für die
Programmiersprachen zu definieren. Genau diesen Weg ist .NET gegangen. Dabei wird
innerhalb eines Regelwerkes, der Common Language Specification (CLS) genau festgelegt,
wie die Abbildung der Sprachkonstrukte einer unterstützen Sprache auf die CIL erfolgt.
Damit ist Interoperabilität sowohl zur Compilezeit, als auch zur Entwicklungszeit
gewährleistet, es können also beispielsweise Klassen, die in einer Programmiersprache A
geschrieben wurden von Klassen, die in einer Programmiersprache B geschrieben wurden
abgeleitet oder Objekte von Klassen in anderen Programmiersprachen instanziiert werden.
Grundlegend wird zwischen Werttypen und Referentypen unterschieden. Werttypen werden
beim Programmablauf auf den Stack gelegt, während für Referenztypen ein spezieller
Speicherbereich, der Heap verwendet wird.
2.1.3 Assemblies
Großer Wert wurde bei der Handhabung von Programmierbausteinen auf
komponentenorientierte Softwareentwicklung gelegt. Diese kleinsten Programmbestandteile
werden als Assemblies (*.exe oder *.dll) bezeichnet und haben die Eigenschaft,
selbstbeschreibend zu sein. Eine Assembly besteht potentiell aus mehreren Dateien, die CIL-6-
Stand 04-2006
Code beinhalten. Zudem können Ressourcendateien wie Grafiken und dergleichen einer
Assembly hinzugefügt werden.
Die Beschreibung der eigenen Schnittstellen, Versionsangaben und benötigten Ressourcen
erfolgen in Metadaten, die als Manifest bezeichnet werden und dem eigentlichen
Programmcode beiliegen. Bei der Auslieferung und Installation von Software in Form von
Assemblies müssen demnach nicht mehr aufwendige Registrierungen der Schnittstellen in der
Windows-Registry erfolgen. Damit werden die Inbetriebnahme von Software und die
Nutzung von Komponenten deutlich erleichtert.
Assemblies sind wiederum unterteilt in Module. Eine Assembly besteht aus mindestens einem
Modul. Der übersetzte CIL-Code wird innerhalb dieser Module abgelegt und durch eigene
Metadaten beschrieben. Eine Aufteilung einer Anwendung in einzelne Module ist in heutigen
Entwicklungsprozessen durchaus wünschenswert, zumal Module in unterschiedlichen
Sprachen geschrieben sein können. Ein weiterer Vorteil dieses Konzepts bildet die Tatsache,
dass eine Anwendung modulweise in den Speicher geladen wird. Speicher wird also erst dann
von einem Modul belegt, wenn dieses auch tatsächlich benötigt wird. Allerdings ist ein Modul
mangels Manifest nicht eigenständig ladbar, sondern nur falls eine Assembly Code aus
diesem Modul aufruft. Aus einer C#-Quellcodedatei kann durch den Aufruf „csc
/target:module Dateiname.cs“ ein Modul erzeugt werden.
Abbildung 4 zeigt eine dll-Datei, bestehend aus mehreren Modulen und dem Manifest. Die
Assembly-Datei A.dll umfasst damit die drei Module A, B und C.
A.dll
B.netmodule
C.netmodule
Modul A
Modul B
Modul C
Manifest
Abbildung 4: Assembly bestehend aus mehreren Modulen
Durch eine nicht zentrale Speicherung aller notwendigen Informationen, insbesondere
Versionsinformationen zu einer Komponente ist ein Ausweg aus der so genannten "DLLHölle", die den Konflikt zwischen unterschiedlichen Versionen einer Komponente auf einem
Rechner beschreibt, geschaffen worden.
2.1.4 Laufzeit-Host und Anwendungsdomänen
Die CLR ist zunächst keine eigenständige Applikation, sondern wird für gewöhnlich
innerhalb einer anderen Anwendung ausgeführt, die als Laufzeit-Host bezeichnet wird. Diese
sorgt für die Initialisierung und das Laden der CLR. Das reguläre .NET-Framework bringt bei
seiner Auslieferung die drei Laufzeit-Hosts ASP.NET, Microsoft Internet Explorer und
ausführbare Shelldateien mit und unterstützt durch zahlreiche APIs die Erstellung eigener
Laufzeit-Hosts.
-7-
Stand 04-2006
Wie in Abbildung 2 dargestellt, läuft eine Assembly innerhalb einer so genannten
Anwendungsdomäne ab. Der umgebende Prozess wird damit in weitere logische Einheiten
zerteilt. Durch sie wird zudem der Heap, auf dem alle Verweistypen (Klassentypen, …)
gespeichert werden, gekapselt. Die mit diesem Konzept erzielte Separation einzelner
Assemblies während der Ausführung macht Anwendungsdomänen mit Prozessen des
Betriebssystems vergleichbar. Die Einteilung in Anwendungsdomänen hat zwei grundlegende
Vorteile:
1. Anwendungsdomänen bilden effizientere Einheiten, als grobgranulare Prozesse.
2. Durch Anwendungsdomänen wird ein prozessähnliches Konstrukt geschaffen, das
vom zugrunde liegenden Prozessmodell und damit vom verwendeten
Betriebssystem abstrahiert, was dem Anspruch von .NET, plattformunabhängig zu
sein, gerecht wird.
2.1.5 Ausführung von verwaltetem Code
Bei der Ausführung von verwaltetem Code tritt zunächst der Programm-Loader in
Erscheinung. Er sucht nach der benötigten Assembly und lädt sie in den Speicher. Dieser
Vorgang ist äußerst komplex und soll hier nur kurz angeschnitten werden.
Zunächst prüft der Loader, ob die Assembly bereits geladen wurde. Ist dies nicht der Fall
durchsucht er den so genannten Global Assembly Cache (GAC), ein Verzeichnis, in dem sich
standardmäßig Assemblies befinden, die global, durch mehrere Programme genutzt werden
können. Ist die Suche auch hier erfolglos werden spezielle Konfigurationsdateien der
aufrufenden Anwendung durchsucht, die Auskunft über den Ort geben können. Bei erneuter
Erfolglosigkeit wird die Suche in dem so genannte Stammverzeichnis fortgesetzt, bei dem es
sich entweder um das Verzeichnis handelt, in dem die Anwendung liegt oder aber um einen
entfernten Pfad zu einem anderen Rechner. Dieser Schritt unterstreicht die
Netzwerkzentrierung der .NET-Technologie. Eine gewöhnliche Anwendung kann also
vollkommen transparent Assemblies auf entfernten Rechnern nutzen.
Nachdem die Assembly gefunden und geladen wurde, werden ihre in CIL vorliegenden
Methoden nach und nach bei Bedarf in nativen Code übersetzt und ausgeführt.
2.2
Klassenbibliothek
Neben der CLR ist eine umfangreiche, objektorientierte Klassenbibliothek das zweite
Standbein der .NET-Technologie. Sie umfasst mehrere tausend Klassen und Datentypen und
stellt eine Vielzahl vorgefertigter Frameworks für Netzwerkkommunikation,
Datenbankzugriffe (ADO.NET) etc. zur Verfügung. Weiter unten wird beispielsweise genauer
auf das .NET-Remoting-Framework eingegangen.
2.2.1 Organisation der Klassenbibliothek
Die Klassenbibliothek ist hierarchisch in baumartigen Strukturen organisiert. Die jeweiligen
Knoten und Blätter werden als Namensräume (namespace) bezeichnet. Ausgangspunkt bilden
die beiden Wurzelelemente System und Mircosoft. Die vom Namensraum System abgeleiteten
Namensräume bieten Funktionalitäten an, die unabhängig von der Ausführungsumgebung,
insbesondere unabhängig von einer Microsoft-Plattform sind und so auch in anderen
zugrunde liegenden Systemen verwendet werden können. Bei den vom Namensraum
Microsoft abgeleiteten Namensräumen handelt es sich analog um an eine Microsoft-8-
Stand 04-2006
Umgebung gebundene Klassen. Ein kleiner Ausschnitt der Namensraumhierarchie ist in
Abbildung 5 dargestellt. Für den Praktikumsversuch wird u.a. der Namensraum
System.Web.Services relevant sein.
System
…
System.Collections
Microsoft
…
System.Data
…
System.Web
…
Microsoft.CSharp
Microsoft.Win32
…
…
System.Web.Services
Abbildung 5: Ausschnitt aus der Namensraumhierarchie von .NET
Ähnlich zu Java ist die Wurzel jeder Ableitungsfolge die Klasse System.Object, die
grundlegende Funktionalitäten anbietet, wie die Methoden Equals() oder ToString(). Auch
reguläre Werttypen sind mit dem Typ System.Object kompatibel, können also objectVariablen zugewiesen werden.
2.3
Auslieferungsformen von .NET
Microsoft bietet eine breite Palette von Formen an, wie und mit welchen Werkzeugen etc.
.NET ausgeliefert wird. Geht es nur darum, .NET-Programme ausführen zu können, so kann
eine isolierte Laufzeitumgebung gemeinsam mit der Klassenbibliothek kostenlos von den
Internetseiten von Microsoft herunter geladen werden. Daneben existiert das ebenso
kostenlose Software Development Kit (SDK), das zusätzlich zur Laufzeitumgebung und
Klassenbibliothek zahlreiche Werkzeuge, wie Compiler, Disassembler, Debugger und
ähnliches mitbringt. Als kostenpflichtige Formen werden darüber hinaus eine umfangreiche
Entwicklungsumgebung (Visual Studio .NET) angeboten. In neusten Server-Plattformen aus
der Microsoft Windows Reihe ist .NET bereits integraler Bestandteil.
Für Kleinstgeräte wie Mobiltelefone, auf denen zugeschnittene Windows-Versionen laufen,
stellt Microsoft eine an die eingeschränkten Ressourcen der Geräte angepasste .NETLaufzeitumgebung, namentlich das .NET Compact Framework zur Verfügung.
Aufgrund der Offenheit des .NET-Frameworks existieren auch erste Ansätze von anderen
Anbietern und freien Entwicklergemeinschaften, eine eigene Version der Entwicklungs- und
Laufzeitumgebung zu erstellen. Bekannt ist dabei das Open-Source-Projekt Mono, das auf
zahlreichen Betriebssystemen (u.a. Linux, BSD-Derivate, …) lauffähig ist.
-9-
Stand 04-2006
2.4
Windows Communication Foundation
.NET ist keine abgeschlossene Technologie, sondern unterliegt einer starken Dynamik, da
Microsoft große Entwicklungsanstrengungen aufbringt, um es weiterzuentwickeln und in
unterschiedlichste Bereiche zu integrieren. Eine momentane Entwicklungsrichtung, die als
Windows Communication Foundation (WCF) oder auch als Indigo bezeichnet wird, zielt
dabei auf die Entwicklung einer einheitlichen, standardisierten Kommunikationsinfrastruktur
für verteilte Anwendungen ab, die den Grundgedanken von Webservices weiterführt und auf
andere Kommunikationsmodelle umsetzt. Dieses System basiert dabei auf dem .NETFramework und führt einige Erweiterungen ein. Geplant ist eine Veröffentlichung mit
Microsoft Windows Vista, aber ebenso die Unterstützung früherer Microsoft Windows
Systeme.
Für weitere Informationen zu dieser Thematik siehe [6].
- 10 -
Stand 04-2006
3 Webservices & .NET-Remoting
Nachdem fundamentale Begriffe und Konzepte der .NET-Technologie eingeführt wurden,
werden nun die theoretischen und praktischen Vorkenntnisse für den Themenbereich des
Praktikums behandelt und diese dann jeweils an einem kleinen Beispiel beleuchtet. Es handelt
sich um zwei Möglichkeiten auf Basis der .NET-Technologie verteilte Anwendungen zu
realisieren: Webservices und .NET-Remoting. Sie sind in ihrem Anwendungsgebiet und ihrer
Zieletzung deutlich verschieden. Neben diesen beide Kommunikationsformen haben zwei
weitere Technologien große Relevanz bei der Realisierung verteilter Anwendungen auf
Grundlage von Windows. Dies sind das proprietäre DCOM, das eng mit COM verbunden ist
und MS Message Queues, die zur Realisierung nachrichtenorientierter Anwendungen
eingesetzt werden.
3.1
Webservices
Die folgenden Ausführungen geben eine kurze Einleitung in die Thematik der Webservices
innerhalb einer .NET-Umgebung. Auf die einzelnen Protokolle und Mechanismen wird an
dieser Stelle nicht näher eingegangen. Sie erfahren neben einer allgemeinen Betrachtung des
Industriestandards der "Webservices" Behandlung im Studienmaterial zu den
Praktikumsversuchen.
3.1.1 Standards
Das Feld der Webservices, die auf Grundlage von .NET erstellt und angeboten werden
können, stützt sich auf die vier Standards XML, SOAP, WSDL und UDDI.
Die .NET-Webservice-Schnittstelle versteht drei verschiedene Übertragungsprotokolle:
1. HTTP-GET (Parameterübergabe erfolgt über die URL)
2. HTTP-POST (Parameterübergabe erfolgt in einem separaten Datenstrom)
3. SOAP (Parameterübergabe erfolgt innerhalb dieses XML-Formats, das selbst
wiederum per HTTP-POST zugestellt wird)
Alle Kommunikationsvarianten stützen sich somit auf das HTTP-Protokoll, das in jedem
gängigen System Unterstützung findet.
3.1.2 Umgebung
Das Betriebssystem Microsoft Windows bringt in zahlreichen Versionen (Windows NT
Server, Windows 2000 Server, Microsoft Windows Server 2003, Windows 2000 Professional,
Windows XP Professional u.a.) alle Voraussetzungen mit, um Webservices anbieten zu
können. Wichtig sind dazu zum einen das .NET-Framework 2.0 (in der Form des Software
Development Kits), das kostenlos von den Seiten von Microsoft bezogen werden kann und
zum anderen ein lauffähiger Internet Information Server (IIS).
3.1.3 Internet Information Server
Beim IIS handelt es sich um eine Sammlung von Serverfunktionalitäten, die in das
Betriebssystem Microsoft Windows integriert sind. Insbesondere bringt er alle
- 11 -
Stand 04-2006
Voraussetzungen für einen modernen Webserver mit und unterstützt so eine Vielzahl an
unterschiedlichen Webtechnologien, wie etwa ASP.NET.
Sollte der IIS auf einem der genannten Systeme nicht installiert sein, so kann dies über den
Assistenten für Windows-Komponenten nachgeholt werden. Zur Installation findet er sich
beispielsweise unter Windows XP Professional als Eintrag "Internet Informationsdienste"
unter
Start->Einstellungen->Systemsteuerung->Software->Windows-Komponenten
hinzufügen/entfernen.
Nach der Installation kann der IIS unter Windows XP Professional innerhalb der
Systemsteuerung unter Verwaltung -> Internet-Informationsdienste konfiguriert und
angepasst werden.
3.1.4 Virtuelle Verzeichnisse
Die eigentliche Webservice-Applikation wird in einem virtuellen Verzeichnis abgelegt, das
vom IIS verwaltet wird. Dazu muss zunächst ein solches Verzeichnis erstellt und mit einem
physikalischen Verzeichnis verknüpft werden. Zum Erstellen eines virtuellen Verzeichnisses
muss innerhalb der Konfigurationsumgebung des IIS die angezeigte Baumstruktur geöffnet
werden, so dass der Menüpunkt "Standardwebsite" sichtbar wird. Nach einem Rechtsklick auf
diesen Eintrag kann über "Neu" und der Befolgung der Dialogschritte ein virtuelles
Verzeichnis mit einem lokalen Ordner verbunden werden.
3.1.5 Vorgehen bei der Erstellung eines Webservices
Als erster Schritt muss eine Klasse implementiert werden, die Methoden als Webservices im
Intra- oder Internet anbieten soll. Diese unterscheidet sich nur durch drei Zusätze von einer
regulären Klasse.
1. Sie besitzt eine Direktive, die die Klasse zu einer Web-Service-Klasse macht:
<%@ WebService Language="C#" class="ClassName"%>
In dieser Direktiven wird zudem die verwendete Sprache, hier die .NET-Sprache C#
angegeben.
2. Sie ist von der Klasse System.Web.Services.WebService abgeleitet (was z.B. durch das
Attribut [WebService] erreicht wird).
3. Jede Methode, die als Webservice aufgerufen werden kann, ist mit dem Attribut
[WebMethod] versehen.
Die so erstellte Klasse wird in einer Datei mit der Endung .asmx abgespeichert, in einem
virtuellen Verzeichnis abgelegt und kann daraufhin bereits mit einem Browser angesprochen
werden. Um die gesamten Vorgänge der Schnittstellen-Publikation, der Kompilierung usw.
kümmert sich die .NET-Umgebung vollkommen eigenständig. Die Schnittstelle wird in
WSDL beschrieben. Aus dieser Beschreibung kann sich ein Client mit Hilfe des Programms
„wsdl“ eine Proxy-Klasse generieren. Der Client selbst spricht dann den Proxy an, der sich
um die Kommunikationsdetails mit dem Webservice, insbesondere um die Umwandlung
zwischen Formaten der übergebenen Werte kümmert.
- 12 -
Stand 04-2006
3.1.6 Beispiel Webservice
Die Erstellung eines Webservices wird in der Folge anhand eines kleinen Beispiels
demonstriert. Bei dem Beispiel handelt es sich um einen Webservice, der zwei int-Werte
miteinander multipliziert und das Ergebnis der Operation zurück liefert.
Der gesamt Quellcode für diese Anwendung sieht folgendermaßen aus:
// Multiplier.asmx
<%@ WebService Language="C#" class="Multiplier" %>
using System;
using System.Web.Services;
[WebService(Namespace="http://localhost/beispiel/")]
class Multiplier {
[WebMethod]
public int multiplyValues(int x, int y) {
return x * y;
}
}
Diese Datei ist unter dem Namen Multiplier.asmx abzuspeichern und in einem virtuellen
Verzeichnis abzulegen. Im Beispiel wurde die Datei im physikalischen Verzeichnis
„C:\beispiel“ gespeichert, das auf das virtuelle Verzeichnis „beispiel“ abgebildet ist. Auf dem
Praktikumsrechner kann das Verzeichnis C:/Inetpub/wwwroot/Versuch.Net/Benutzername
verwendet werden. Bei lokal laufendem IIS ist der Webservice durch Eingabe der URL
http://localhost/beispiel/Multiplier.asmx (bzw. auf dem Praktikumsrechner (141.76.40.92):
http://localhost:8000/Versuch.Net/Benutzername/Multiplier.asmx) von einem Web-Browser
aus erreichbar. Die IIS- bzw. .NET-Infrastruktur sorgt für die automatische Generierung einer
HTML-Seite, über die sich der Webservice ansprechen lässt. Durch klicken auf den Link
„multiplyValues“ auf dieser Seite kann die Methode getestet werden. Zudem befinden sich
dort einige Informationen zum Aufrufmechanismus. Für alle Parameter der Methode wird ein
Formularfeld bereitgestellt.
Die Rückgabe des Tests wird in Form einer XML-Kodierung präsentiert, die im Falle der
Eingabe von „2“ und „3“ als Parameter folgende Form hat:
<?xml version="1.0" encoding="utf-8" ?>
<int xmlns="http://localhost/beispiel/">6</int>
Wie bereits erwähnt kümmert sich die zugrunde liegende Infrastruktur eigenständig um die
Generierung einer WSDL-Beschreibung aus einer Webservice-Klasse. Diese kann durch
Eingabe von „?WSDL“ hinter der URL des Webservices, im Beispiel also
http://localhost/beispiel/Multiplier.asmx?WSDL betrachtet werden.
Aus dieser Beschreibung wird von der Eingabeaufforderung aus durch den Aufruf
wsdl /out:Multiplier.cs http://localhost/beispiel/Multiplier.asmx?WSDL
eine Proxy-Klasse erzeugt, die in der Datei Multiplier.cs abgelegt wird. Diese steht somit
beispielsweise einem ASP.NET- oder auch einem eigenständigen Client zur Verfügung und
ermöglicht eine Nutzung des Webservices, als handele es sich um eine lokale Klasse.
- 13 -
Stand 04-2006
3.2
.NET Remoting
Webservices stellen eine standardisierte und einfache Möglichkeit dar, auf entfernte
Funktionalitäten zuzugreifen. Neben diesem Konzept zur Bereitstellung von Diensten über
das Internet bietet .NET eine Lösung für Anwendungen aus verteilten bzw. entfernten Objekte
an: .NET-Remoting.
Diese Technologie gilt als eine leichtgewichtige und effiziente Form zur Publikation und
Nutzung von Remote-Objekten.
3.2.1 Remoting-Architektur
Die Grundlage der Kommunikation per .NET-Remoting bilden so genannte Kanäle
(Channels) über die Daten zwischen Client und Server in einem bestimmten Protokollformat,
in das sie mit Hilfe von Formatierern (Formattern) gebracht werden, übertragen werden. Als
wichtigste Kanäle existiert zum einen der HTTP-Kanal, über den Daten im SOAP-Format
geschickt werden und zum anderen der TCP-Kanal, der Daten in einem Binärformat
übermittelt. Daneben gibt es zahlreiche weitere, insbesondere proprietäre Kanäle.
Die so genannte Channel-Architektur samt Kommunikationsvorgang ist in Abbildung 6
dargestellt.
Clientprozess
Serverprozess
int x = 12;
Result = Obj.callRemote(x);
...
entferntes Objekt
1
5
Proxy
Formatter
Dispatcher
2
Formatter
4
3
Transportkanal (z.B. binäre Kodierung oder SOAP-Format)
Abbildung 6: Kommunikationsvorgang per .NET-Remoting
Der Ablauf ist dabei folgendermaßen:
1. Ein Client-Prozess ruft eine Methode eines entfernten Objektes auf, der tatsächlich an
ein lokales Proxy-Objekt weitergeleitet wird.
2. Mithilfe eines Formatters wird der Aufruf in eine übertragbare Form gebracht.
- 14 -
Stand 04-2006
3. Innerhalb des Remoting-Subsystems erfolgt die Übermittlung der Daten auf einem
zuvor angeforderten Kanal.
4. Auf der Seite des entfernten Objekts wird der Aufruf entgegen genommen und zurück
in das ursprüngliche Format transformiert.
5. Über einen Dispatcher, der zeitgleiche Zugriffe reguliert wird der Aufruf schließlich
an das entfernt nutzbare Objekt weitergereicht.
Der Weg von Rückgabedaten ist zum Aufruf analog.
3.2.2 Eigenschaften entfernter Objekte
Um durch das .NET-Remoting-System nutzbar zu sein, müssen entfernte Objekte direkt oder
indirekt von der Klasse MarshalByRefObjekt erben. Sie müssen eigenständig für die
Reservierung eines Kanals an einen bestimmten Port sorgen und ihre remote nutzbare
Funktionalität explizit registrieren. Wie dieses im Detail aussieht, wird im Beispiel weiter
unten behandelt.
Entfernte Objekte werden je nach Form ihrer Aktivierung und Verwaltung in zwei Kategorien
eingeteilt. Bei Server Activated Objects (SAO) übernimmt die Server-Seite transparent für
den Client die Instanziierung etc., während bei Client Activated Objects (CAO) ein entferntes
Objekt explizit von und für einen Client instanziiert wird und maximal für die Dauer der
Sitzung erhalten bleibt.
Die Kategorie der SAO wird weiter eingeteilt in:

SingleCall-Objekt
Lebt nur für die Dauer eines Methodenaufrufs und ist somit für die Haltung eines
internen Zustands nicht verwendbar.

Singleton-Objekt
Das Objekt wird exakt einmal instanziiert und bearbeitet Methodenaufrufe in
parallelen Threads. Es eignet sich damit zur Speicherung eines internen Zustands,
kann also Instanzvariablen besitzen.
3.2.3 Nutzung eines entfernten Objektes
Der Client, der ein entferntes Objekt nutzen will muss über zwei Informationen verfügen:
1. Die Adresse des entfernten Objektes, die entweder fest in den Sourcecode einkompiliert
oder aber in einer Konfigurationsdatei angegeben wird.
2. Die Beschreibung der benötigten Methode (Name, Parameter, Rückgabewert). Diese wird
ihm in der Regel durch ein Interface der verwendeten Programmiersprache bekannt gegeben.
Nachdem der Client einen Kommunikationskanal angefordert hat, kann er eine Referenz auf
ein entferntes Objekt beantragen. Dazu kann die statische Methode GetObject() der Klasse
Activator verwendet werden. Durch den damit angestoßenen Ablauf wird auf Client-Seite ein
Proxy-Objekt erstellt, dem Client zugeordnet und an diesen Proxy eine Referenz auf das
entfernte Objekt gebunden. Wird nun eine Methode des entfernten Objekts aufgerufen,
verläuft die gesamte Kommunikation über das Proxy-Objekt, das die Serialisierung
ausgehender und Deserialisierung eingehender Daten realisiert.
- 15 -
Stand 04-2006
3.2.4 Beispiel .NET-Remoting
Ebenso wie im Falle des Webservices soll nun eine Beispielanwendung entwickelt werden,
die auf Basis von .NET-Remoting die Multiplikation zweier int-Werte innerhalb eines
entfernten Objektes ermöglicht.
Dazu sind drei verschiedene Bestandteile zu erstellen:

ein Server-Programm, das der Einfachheit halber direkt das entfernt nutzbare Objekt
(SingleCall) beinhaltet

ein Client-Programm, das eine Methode des Objekts nutzt

eine Beschreibung der Schnittstelle der Methoden des entfernten Objektes, die in
Form eines Interfaces dem Client zur Verfügung gestellt wird.
Als Channel zwischen Client und Server wird ein TCP-Kanal verwendet.
Demnach hat der Server folgenden Aufbau:
//Multiplier.cs
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace Multiplier{
public class Multiplier : MarshalByRefObject, IMultiplier.IMultiplier
{
public Multiplier()
{
}
public int multiply(int x, int y){
return x*y;
}
public static void Main(){
TcpServerChannel channel = new TcpServerChannel(2342);
ChannelServices.RegisterChannel(channel,false);
WellKnownServiceTypeEntry remoteObject = new WellKnownServiceTypeEntry(
typeof(Multiplier),
"Multiplier",
WellKnownObjectMode.SingleCall
);
RemotingConfiguration.RegisterWellKnownServiceType(remoteObject);
Console.WriteLine("Server is running...");
Console.ReadLine();
}
}
}
- 16 -
Stand 04-2006
Zunächst registriert der Server den gewünschten Kanal und daraufhin das entfernte Objekt
vom Typ Multiplier als SingleCall-Objekt. Auffallend an diesem Code ist die fehlende
Verbindung zwischen dem deklarierten Kanal und dem entfernten Objekt. Diese Beziehung
wird intern vom .NET-Remoting-Framework hergestellt.
Das vom Server implementierte Interface beinhaltet die Beschreibung der MethodenSchnittstelle und steht Client und Server zur Verfügung. Es hat folgenden Aufbau:
//IMultiplier.cs
using System;
namespace IMultiplier
{
public interface IMultiplier
{
int multiply(int x, int y);
}
}
Für den Client ergibt sich folgender Code:
//MultiplierClient.cs
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace MultiplierClient{
class MultiplierClient{
[STAThread]
static void Main(string[] args)
{
TcpClientChannel channel = new TcpClientChannel();
ChannelServices.RegisterChannel(channel,false);
IMultiplier.IMultiplier multiplier =
(IMultiplier.IMultiplier)Activator.GetObject
(
typeof(IMultiplier.IMultiplier),
"tcp://localhost:2342/Multiplier"
);
int result = multiplier.multiply(2, 3);
Console.Write(result);
}
}
}
- 17 -
Stand 04-2006
Analog zum Server erfolgt zunächst die Registrierung des Channels und daraufhin die
Anforderung einer Referenz auf das entfernten Objekt. Die einfachste Möglichkeit dazu ist
die Nutzung der Methode Activator.GetObject(). Das zurück gegebene Proxy-Objekt kann
nach einem Cast auf den gewünschten Typ (hier im Format Namespace.Interface) wie ein
lokales Objekt verwendet werden.
Damit der Client die aufzurufende Schnittstelle kennt, wird ihm diese bei der Kompilierung
mitgeteilt:
csc MultiplierClient.cs IMultiplier.cs //Übersetzung Client
csc Multiplier.cs IMultiplier.cs //Übersetzung Server
Die beiden erzeugten *.exe-Dateien können daraufhin gestartet werden.
Neben der unmittelbaren Festlegung der Verbindungs- (Adresse/Port des entfernten Objekts
etc.) oder der Channelparameter im Quellcode können diese Daten Client bzw. Server ebenso
per XML-Konfigurationsdatei bekannt gegeben werden, was den Vorteil der einfachen
Konfigurierbarkeit mit sich bringt.
Im Falle des obigen Clients sieht eine solche Konfigurationsdatei wie folgt aus:
<configuration>
<appSettings>
<add key="reference" value="http://localhost:2342/entferntesObjekt" />
</appSettings>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http"/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Diese wird standardmäßig nach dem Schema Assemblyname.Assemblytyp.config im
Verzeichnis der Assembly gespeichert, im Beispiel also unter MultiplierClient.exe.config.
Daraufhin kann sie über das nachfolgende Codesegment geladen und auf ihrer Basis eine
Referenz auf ein entferntes Objekt angefordert werden.
String configfilename =
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
RemotingConfiguration.Configure(configfilename,false);
IMultiplier.IMultiplier multiplier =
(IMultiplier.IMultiplier)Activator.GetObject
(
typeof(IMultiplier.IMultiplier),
System.Configuration.ConfigurationManager.AppSettings["reference"]
);
- 18 -
Stand 04-2006
4 Der Versuch
Bei der Anwendung handelt es sich um ein verteiltes Flugbuchungssystem, das
Funktionalitäten sowohl auf Basis von Webservices, als auch auf Basis von .NET-Remoting
zur Verfügung stellt. Mit Hilfe eines Client-Programms ist es möglich, die bereitgestellten
entfernten Methoden zu nutzen und so Flüge zu buchen, Buchungen anzuzeigen und neue
Flugverbindungen einzutragen.
Voraussetzung für die Bearbeitung des Versuchs ist ein installiertes, aktuelles .NETFramework (der Versuch wurde auf Basis der Framework-Version 2.0 erstellt). Dieses kann
von den Internet-Seiten von Microsoft bezogen werden. Daneben muss auf dem verwendeten
Rechner der IIS laufen.
Eine Programmierumgebung wie etwa Visual Studio ist nicht erforderlich. Alle Dateien
können und sollten mit einem Texteditor bearbeitet werden. Das System ist in C#
geschrieben.
4.1
Versuchsbeschreibung
Nach dem Download der Sourcen von den Seiten des Lehrstuhls und dem Entpacken des
Archivs liegen die Programmkomponenten in den Unterordnern „client“, „server“ und „data“
vor. Im Ordner „data“ befinden sich die beiden Dateien DataBase.xml und
DataDescription.xsd, die zum einen die Datenbasis und zum anderen die Beschreibung des
Datenmodells in Form von XML-Dateien bilden. Der Datenzugriff von Seiten des Servers
wird durch die Klasse DataManager in der Datei DataManager.cs durchgeführt.
Der Versuch kann auf dem Praktikumsrechner (141.76.40.92) des Lehrstuhls Rechnernetze
bearbeitet werden. Dazu wurde unter C:/Inetpub/wwwroot/Versuch.Net/ für jeden Benutzer
ein Verzeichnis mit der Bezeichnung des Benutzernamens angelegt. Wird das Archiv
unmittelbar in dieses Benutzerverzeichnis entpackt, so kann der Webservice über die URL
http://localhost:8000/Versuch.Net/Benutzername/server/FlightManager.asmx angesprochen
werden. Damit der Webservice bzw. der Remoting-Server auf die Datenbasis zugreifen kann,
müssen die Rechte für die Datei DataBase.xml entsprechend angepasst werden. Der
Einfachheit halber kann der Benutzergruppe „Authenticated Users“ voller Zugriff gewährt
werden.
Folgende Dateien sind für die Bearbeitung des Versuchs relevant:
Datei
Aufgabe
Server/FlightManager.asmx
Stellt Funktionalitäten als Webservices zur Verfügung und
greift dazu auf die Datenbasis zu.
Ermöglicht das Hinzufügen einer Flugverbindung zum
Datenbestand mittels .NET-Remoting.
Server/FlightAdd.cs
Client/FlightClient.cs
Bildet die Hauptklasse des Clients, in der fundamentale
Initialisierungen vorgenommen werden.
Client/FlightClient.exe.config Beinhaltet XML-Konfigurationsdaten für die
Kommunikation per .NET-Remoting
Client/IFlightAdd.cs
Die Interface-Beschreibung des per .NET-Remoting
Server/IFlightAdd.cs
nutzbaren entfernten Objekts
- 19 -
Stand 04-2006
Die folgenden Aufgaben sind durchzuführen:
I.
II.
III.
IV.
V.
VI.
VII.
Die Datei Server/FlightManager.asmx ist so zu erweitern, dass die Methoden
getAirports(), getFlights(), getFlightData(), storeBooking(), getBookings(),
getBookingData() und deleteBookingData() als Webservice-Methoden
ansprechbar sind. Zudem ist der Pfad zu den Dateien DataBase.xml und
DataDescription.xsd bei der Instanziierung der Klasse DataManager anzugeben.
Aus der WSDL-Beschreibung des Webservices ist eine Proxy-Klasse für den
Client zu erstellen. Diese muss unter dem Namen FlightManager.cs im ClientVerzeichnis abgelegt werden.
In der Datei Server/FlightAdd.cs sind in der Main-Methode die notwendigen
Ergänzungen für die Initialisierung eines TCP-Channels (Port 4223) und der
Nutzbarmachung der Klasse FlightAdd als entferntes Objekt unter dem Namen
„FlightAdd“ in Form des SingleCall-Objektes (SOA) mit der Methode addFlight()
durchzuführen. Auch hier ist der Pfad der Dateien DataBase.xml und
DataDescription.xsd bei der Instanziierung der Klasse DataManager anzugeben.
Die beiden Dateien IFlightAdd.cs sind mit der Angabe des Interfaces für das
entfernte Objekt bzw. dessen Methoden zu vervollständigen (für Client und
Server).
Die Konfigurationsdatei Client/FlightClient.exe.config ist so zu erweitern, dass auf
ihrer Basis das entfernte Objekt per Remoting genutzt werden kann.
Im Konstruktor der Klasse FlightClient sind entsprechende Ergänzungen zum
Laden der Konfigurationsdatei FlightClient.exe.config zu machen. Dabei wird die
Referenz auf das entfernte Objekt der Variablen „adder“ zugewiesen.
Vergleichen Sie kurz die beiden Technologien .NET-Webservices und .NETRemoting in Bezug auf Standardisierung und bevorzugter Einsatzzweck und
führen Sie die maßgeblichen Vor- und Nachteile auf. Die Antworten sind in der
Datei „Aufgabe7.txt“ abzulegen.
Nach erfolgreicher Abarbeitung der Punkte 1-6 kann das Client- und Serverprogramm jeweils
von der Kommandozeile aus übersetzt und gestartet werden. Die Übersetzung geschieht
beispielsweise durch die folgenden beiden Aufrufe:
csc FlightClient.cs AddFlights.cs BookFlight.cs ShowFlights.cs
IFlightAdd.cs FlightManager.cs //Übersetzung Client
csc FlightAdd.cs DataManager.cs IFlightAdd.cs //Übersetzung Server
4.2
Sicherung der Versuchsergebnisse
Nachdem die richtige Funktionsweise der Versuchsergebnisse ausreichend getestet wurde,
sind folgende Dinge zu tun:
1. Anlegen des Ordners „Lösungen.NET“ im Unterverzeichnis „Lösungen“ des eigenen
Verzeichnisses auf dem Praktikumsrechner
2. Speicherung der veränderten und erstellten Dateien (FlightManager.asmx,
FlightManager.cs, FlightClient.cs, FlightClient.exe.config, FlightAdd.cs, IFlightAdd.cs,
Aufgabe7.txt) in diesem Verzeichnis
3. Mail mit dem Betreff „Versuch .NET“ an [email protected] unter Angabe des
Namens, Vornamens, der Matrikelnummer und des Logins verschicken
4. Kopie der Versuchsergebnisse bis zur Ausgabe der Scheine sichern
- 20 -
Stand 04-2006
5 Literatur
[1]
Einführung in die Grundlagen von .NET:
Ralf Westphal: .NET
ISBN 3827411858
[2]
kompakt;
Spektrum,
Akademischer
Verlag;
2002;
Einführung in die Grundlagen von .NET:
Hanspeter Mössenböck: Softwareentwicklung mit C#; dpunkt.verlag; 2003;
ISBN 3898641260
[3]
Probekapitel zur Common Language Runtime:
David Chappell: .NET verstehen . Einführung und Analyse; Addison-Wesley; 2002;
ISBN 3827320232
http://download.microsoft.com/download/9/2/3/923d72fb-0076-49b6-96c4aac1c255a60e/2023_S87_126OK.pdf
[4]
Portal zu .NET von Microsoft.com; Bezugsquelle für das .NET-Framework
http://msdn.microsoft.com/netframework/
[5]
Überblick über die Garbage Collection der CLR:
http://msdn.microsoft.com/library/deu/default.asp?url=/library/DEU/cpguide/html/c
pconAutomaticMemoryManagement.asp
[6]
Portal zu WCF von Microsoft.com mit umfangreichen Informationen
http://msdn.microsoft.com/webservices/indigo/
- 21 -
Herunterladen
Explore flashcards