Instant peer-to-peer media streaming for use in multi-party conferencing - Masterarbeit – Institut für Softwaretechnik und Datenkommunikation Hochschule Mannheim Paul-Wittsack-Straße 10 68163 Mannheim Autor: Matr.-Nr.: Studiengang: Lyubomir Lyubenov 0710894 Informationstechnik Betreuer: Prof. Dr. Eckhart Körner, Hochschule Mannheim Dipl. -Ing. Lothar Grimm, T-Systems ES GmbH, Darmstadt Ehrenwörtliche Erklärung Ich versichere, dass ich die vorliegende Arbeit selbständig und ohne Benutzung anderer als der angegebenen Hilfsmittel angefertigt habe. Alle Stellen, die wörtlich oder sinngemäß aus veröffentlichten und nicht veröffentlichten Schriften entnommen wurden, sind als solche kenntlich gemacht. Die Arbeit hat in dieser oder ähnlicher Form keiner anderen Prüfungsbehörde vorgelegen. Mannheim, den 24.02.2009 Lyubomir Lyubenov Abstrakt Application Layer Multicast (ALM) ist eine Technik, die in den letzten Jahren immer mehr an Bedeutung gewonnen hat. Sie basiert auf Peer-To-Peer (P2P) Kommunikation. Eine von vielen auf ALM basierenden Anwendungen ist das Video Streaming, auch in der Form als Live-Übertragung. Diese Arbeit beschäftigt sich zunächst mit ALM und den zugrunde liegenden P2PNetzen. Die existierenden Ansätze für P2P-Netze werden eingeführt und nach ihren Architekturen klassifiziert. Des Weiteren wird eine freie Implementierung eines P2PProtokolls namens Pastry vorgestellt, speziell mit ihrer Erweiterung SplitStream. SplitStream bietet eine API, die den Aufbau eines ALM für Live Video Streaming ermöglicht. Für die Medienübertragung kann dabei das Real-time Transport Protocol (RTP) eingesetzt werden. Weiterer Schwerpunkt dieser Arbeit ist das multi-party Conferencing mit dem SIPProtokoll, welches die Signalisierung für die Audio- und Videokommunikation unter mehreren Endnutzern zur Verfügung stellt. Der praktische Teil dieser Arbeit befasst sich mit der Implementierung eines Plugins für den SIP User Agent (UA) SIP Communicator, welcher unter der GNU GPL Lizenz steht. Der SIP Communicator wird in Java entwickelt und setzt auf das dynamische Java Modulsystem OSGi auf, welches eine Service-Orientierte Programmierung ermöglicht. Die Aufgabe des für den SIP Communicator entwickelten Plugins ist der Aufbau eines ALM auf Basis von SplitStream, welches ein Live Video Streaming unter den Nutzern in einer multi-party Conference ermöglicht. Dafür wurde auch die SIPSignalisierung des UAs erweitert, um SIP-basierte Konferenzgespräche zu unterstützen. Beispielszenarien für die Nutzung von P2P Live Video Streaming in Konferenzen sind das Einspielen eines Urlaubvideos oder die Live-Übertragung eines Popkonzerts in eine bestehende SIP Session. Derartige Szenarien werden in den aktuellen Conferencing Tools noch gar nicht unterstützt. Abschließend werden die Ergebnisse analysiert und die weiterführenden Aufgaben definiert. Abstract In the past few years Application Layer Multicast (ALM) gained more and more importance. Peer-to-peer (p2p) networks catalyse the importance of this new technology. There are different applications based on ALM. One of them is live video streaming. In this master thesis, we describe at first the p2p technology. Then we classify the different p2p networks depending on their architecture. We work with a free p2p protocol called Pastry and its extension SplitStream. SplitSteam provides a programming API for the establishment of an ALM. We also consider the Real-time Transport Protocol (RTP) protocol which defines a standardized packet format for delivering audio and video over the Internet. Next, we describe the Session Initiation Protocol (SIP) protocol that is widely used for setting up and tearing down multimedia communication sessions such as voice and video conferences over the Internet. The practical part of this thesis deals with the implementation of a plug-in for a SIP User Agent (UA). We use the SIP Communicator (SC) that is an audio/video Internet phone and instant messenger. It supports some of the most popular instant messaging and telephony protocols such as SIP, Jabber and ICQ. The SIP Communicator is Open Source Software, and is freely available under the terms of the GNU Lesser General Public License. SC works with the Java-based service platform OSGi. Thus, we also provide a small introduction to service-oriented programming. The new SC plug-in can be deployed to construct an ALM based on SplitStream. The UAs will then be able to distribute videos in a live mode among the participants in a conference. Our example scenario is the multicasting of a holiday video or a rock concert within a SIP session. Thereby, we want to demonstrate a future use of p2p video streaming. This use case is a major highlight of the thesis as there is no implementation yet of this feature in any current instant messenger. In the conclusion we discuss the obtained results and possible future works. Inhaltsverzeichnis 1. Einführung.............................................................................................................................. 6 1.1 Motivation ........................................................................................................................ 6 1.2 Zielsetzung ....................................................................................................................... 6 1.3 Überblick der Kapitel ....................................................................................................... 7 2. Technologischer Hintergrund................................................................................................. 8 2.1 SIP (Session Initiation Protocol) ...................................................................................... 8 2.2 SDP (Session Description Protocol) .............................................................................. 11 2.3 RTP (Realtime Transport Protocol) ............................................................................... 12 2.4 Peer-to-Peer-Netze ......................................................................................................... 13 2.4.1 Einführung............................................................................................................... 13 2.4.2 Klassifizierung ........................................................................................................ 14 2.5 Peer-To-Peer-Netze für Live Video Streaming.............................................................. 17 2.5.1 Einführung............................................................................................................... 17 2.5.2 Baumbasierte Systeme ............................................................................................ 18 2.5.3 Vermaschte Systeme ............................................................................................... 22 2.6 OSGi - Dynamic Module System for Java..................................................................... 24 2.6.1 Einführung............................................................................................................... 24 2.6.2 Die OSGi Service Plattform.................................................................................... 24 2.6.3 Implementierungen.................................................................................................. 25 2.6.4 Das OSGi Framework ............................................................................................. 25 2.6.5 Grundelemente ........................................................................................................ 26 3. Konzept ................................................................................................................................ 28 3.1 SIP-basierte Konferenz mit Live Video Streaming........................................................ 28 3.2 Das P2P Konzept............................................................................................................ 32 3.2.1 Hashfunktionen ....................................................................................................... 32 3.2.2 Pastry....................................................................................................................... 34 3.2.3 Scribe....................................................................................................................... 37 3.2.4 SplitStream .............................................................................................................. 39 3.3 Das OSGi-Schichtenmodell ........................................................................................... 41 3.3.1 Module-Schicht ....................................................................................................... 42 3.3.2 Die Lifecycle-Management-Schicht ....................................................................... 43 3.3.3 Die Service-Schicht................................................................................................. 44 3.3.4 Die Security-Schicht ............................................................................................... 44 3.3.5 Die Framework Services ......................................................................................... 45 4. Realisierung.......................................................................................................................... 46 4.1 Erstellen eines SplitStream Clients ................................................................................ 46 4.2 Entwicklung eines OSGi Pugins .................................................................................... 57 4.3 Erstellen von eigenen Modulen für den SIP Communicator.......................................... 62 4.4 SplitStream Erweiterungen für den SIP Communicator ................................................ 66 4.4.1 P2P-Live-Video-Streaming-Proxy .......................................................................... 66 4.4.2 Anbindung an den SIP Communicator.................................................................... 68 5. Zusammenfassung................................................................................................................ 74 5.1 Ergebnisse ...................................................................................................................... 74 5.1 Weiterführende Aufgaben .............................................................................................. 74 Anhang A: Use Case für Integration von SplitStream mit SIP ................................................ 76 Anhang B: Abbildungsverzeichnis........................................................................................... 81 Anhang C: Listingverzeichnis.................................................................................................. 82 Anhang D: Literaturverzeichnis ............................................................................................... 83 1. Einführung 1. Einführung 1.1 Motivation Im Zeitalter von Web 2.0 werden immer neue Onlinedienste angeboten. Das VideoSharing wird immer beliebter, nicht nur bei Jugendlichen, sondern auch bei Erwachsenen. In den meisten Fällen ruft ein Nutzer eine entsprechende Webseite auf und schaut sich ein eingebettetes Video an. Er ist dabei mehr oder minder passiv, es fehlt das Gemeinschaftserlebnis. Man wünscht sich daher eine Möglichkeit, Videos und Musik mit anderen zu teilen und zwar in Echtzeit. Die Tauschbörsen bieten keine echte Alternative, da man dort abwarten muss, bis die Media-Datei heruntergeladen ist. Aktuell existieren ausgereifte Peer-to-Peer (P2P) Netze. Die zugrunde liegende Technologie wird immer intelligenter, angefangen bei hybriden P2P Netzen, bei denen ein Server vorhanden ist, um bestimmte Aufgaben zu erfüllen, bis hin zu reinen P2P-Architekturen, wo die Clients alle Funktionen erbringen. Andererseits stehen Internet-Protokolle wie SIP zur Verfügung, um die Audio- und Videokommunikation zwischen Onlinenutzern zu vermitteln. Es sollte von daher möglich sein, die unterschiedlichen Technologien in einem einzigen Produkt zu vereinen, so dass das gemeinsame Anschauen eines Videos während einer Audio- bzw. Videokonferenz stattfinden kann. Damit werden Leute von geografisch entfernten Regionen näher aneinander gebracht, um ihre Videos, aber auch Gefühle besser auszutauschen. 1.2 Zielsetzung Ziel dieser Masterarbeit ist es, einen existierenden SIP User Agent für die Unterstützung von Media Streaming zu erweitern. Als Beispielszenarien dienen das Einspielen eines Urlaubvideos oder die Live-Übertragung eines Popkonzerts in eine bestehende SIP Session. Dabei kann es sich um eine SIP Session mit zwei oder mehr Teilnehmern handeln. Zur effizienten Medienverteilung in einer großen Gruppe wird ein Application Layer Multicast (Peer-to-Peer Video Streaming) eingesetzt. Ausgangspunkt für die Implementierung ist der als Open Source von der Universität Straßbourg entwickelte Instant Messenger „SIP Communicator“ (SC), und speziell seine Erweiterung zur Unterstützung des SIP Protokolls. Der SIP Communicator ist in Java entwickelt und setzt auf das dynamische Java Modulsystem OSGi auf. Der IM Client ist in zweierlei Hinsicht zu erweitern: • Erweiterung der SIP Signalisierung für Konferenzen mit beliebiger Anzahl Teilnehmer. -6- 1. Einführung • Integration des VLC Media Players mit Peer-to-Peer Proxy. Ein Peer-to-Peer Live Video Streaming System wurde bereits in einer vorherigen Masterarbeit entwickelt. [thr] 1.3 Überblick der Kapitel Dieses Dokument soll dem Leser als Erstes einen Überblick über die Peer-To-Peer Technologie und Overlay-Netze für Live Video Streaming geben. Zusätzlich werden die Protokolle für die Übertragung von Medienströmen im Internet eingeführt. Das zweite Kapitel beginnt mit der Beschreibung der Media-Protokolle SIP, SDP und RTP, die die Basis für eine Audio- bzw. Videokommunikation bilden. Anschließend werden die P2P-Netze klassifiziert. Schließlich wird die service-orientierte Architektur OSGi mit ihren Grundbausteinen vorgestellt. Sie spielt bei der nachfolgenden Implementierung eine wichtige Rolle. Im Kapitel 3 liegt der Fokus auf der Entwicklung einer geeigneten SIP Signalisierung für Live Video Streaming in Konferenzen. Dazu werden auch die Pastry-Technologie und die aus ihr stammenden Weiterentwicklungen Scribe und SplitStream näher beschrieben. Schließlich wird das OSGi – Schichtenmodell weiter ausgebaut. Kapitel 4 widmet sich der Java-Implementierung des Konzeptes aus Kapitel 3. Es wird gezeigt, wie man einen SplitStream Client und ein OSGi Bundle erstellen kann. Zusätzlich wird die Schnittstelle des SIP Communicators für die Entwicklung eigener Module betrachtet. Mit Hilfe dieser Kenntnisse werden am Ende des Kapitels die am SIP Communicator vorgenommenen Erweiterungen beschrieben. Zum Abschluss werden in Kapitel 5 die erreichten Ergebnisse zusammengefasst und der Ausblick auf weitere Entwicklungen gegeben. -7- 2. Technologischer Hintergrund 2. Technologischer Hintergrund In diesem Kapitel werden Technologien und Protokolle beschrieben, die P2P Video Streaming ermöglichen. An erster Stelle werden SIP und SDP vorgestellt, die zum Aufbau, zur Steuerung und zum Abbau einer Kommunikationssitzung zwischen zwei oder mehr Teilnehmern eingesetzt werden. Kapitel 2.3 führt das Real-Time Transport Protocol (RTP) ein, welches zur kontinuierlichen Übertragung von audiovisuellen Daten (Streams) über IP-basierte Netzwerke benutzt wird. Anschließend werden die Techniken der P2P-Netze analysiert und nach ihrer Struktur klassifiziert. Als logische Fortsetzung werden die unterschiedlichen Topologien für P2P Live Video Streaming Netze betrachtet. Auf Basis ihrer Vor- und Nachteile werden die aktuellen Trends auf diesem Gebiet herausgearbeitet. Abschließend wird die OSGi Service Plattform mit ihren Grundelementen vorgestellt. 2.1 SIP (Session Initiation Protocol) Das Session Initiation Protocol (SIP) ist ein Netzwerkprotokoll zum Aufbau einer Kommunikationssitzung zwischen zwei und mehr Teilnehmern. Das Protokoll wird in den RFC 3261 [r1] (früher RFC 2543 [r2]) spezifiziert. Im Gegensatz zu H.323, das von der ITU-T stammt, wurde SIP mit Blick auf das Internet von der IETF entwickelt und orientiert sich an der Architektur gängiger Internet – Anwendungen. Dabei wurde von Beginn an auf leichte Implementierbarkeit, Skalierbarkeit, Erweiterbarkeit und Flexibilität geachtet. SIP kann benutzt werden, um beliebige Sessions mit einem oder mehreren Teilnehmern zu verwalten. Dabei ist es nicht auf Internet – Telefonie beschränkt, sondern Sessions können beliebige Multimediaströme, Konferenzen, Computerspiele usw. sein. [Wiki] Mit Hilfe des SIP-Protokolls werden die Initialisierungen der Verbindung vorgenommen sowie Daten und Parameter der Verbindung ausgetauscht. SIP ähnelt sehr dem HTTP Protokoll, denn es ist ASCII – formatiert und unterteilt die Art der Nachrichtenübermittelung in Requests (aktive Anfragen) und Responses (passive Antworten auf Anfragen). Bei Request – Anfragen steht die Art der Anforderung in der ersten Zeile. Es gibt folgende Request Typen: • INVITE — Lädt einen anderen Client zu einem Gespräch ein • ACK — Bestätigt den Verbindungsaufbau und leitet somit den Gesprächsbeginn ein • BYE — Beendet die aktuelle Sitzung • OPTIONS — Dient zur Ermittlung von Benutzer- und Serverdaten, wird jedoch nicht für den Aufbau benötigt • CANCEL — Wird vorwiegend von Servern oder Proxies zum Signalisieren eines Abbruches verwendet -8- 2. Technologischer Hintergrund • REGISTER — Damit meldet sich der SIP-Teilnehmer am SIP-Server oder SIP Proxy an. Häufig wird hier eine verschlüsselte Benutzer – Authentifizierung nach dem ”MD5 – Hash“- Verfahren vorgenommen • INFO — Dient zum Transport beliebiger Informationen in der Payload der SIP – Nachricht. Die durch das SDP eingestellten Parameter werden dadurch nicht beeinflusst. Häufig werden XML- Dateien zur Statuskontrolle oder Steuerung übertragen • MESSAGE — Dient der Realisierung eines Instant Messengers, der Chat – Nachrichten sofort überträgt und anzeigt • PRACK — Dient der vorläufigen Bestätigung einer wichtigen Response Nachricht der 100er Reihe • SUBSCRIBE — Signalisiert dem SIP – Server, SIP – Proxy oder SIP – Client, dass der Benutzer über eine bestimmte Status – Änderung informiert werden möchte. Beispiel hierfür ist die Presence – Funktion des Instant Messengers, welche Auskunft darüber erteilt, ob ein Benutzer gerade online oder beschäftigt ist • Notify — Ist die oben beschriebene Mitteilung, dass sich ein Status geändert hat. Häufig wird hier ein XML – File angehängt, das den neuen Status beschreibt • UNSUBSCRIBE — Löscht die Statusbenachrichtigung wieder Die Response-Nachrichten werden in Form eines dreistelligen Zahlencodes in der ersten Headerzeile zurückgegeben. Die Festlegung entspricht weitgehend der des HTTP – Protokolls: • 100 - 199 — Vorläufige Bestätigung, mit dem Ziel den Request – Steller auf ein bestimmtes Ereignis warten zu lassen. Gebräuchlichstes Beispiel ist die ”180 – Ringing“ Response. Sie signalisiert dem Anrufer, dass die Gegenstelle klingelt. • 200 - 299 — Bestätigung des Requests. Die ”200 – OK“ Response ist hier die Standard Antwort. • 300 - 399 — Umleitung. Der Client sollte die vom Server neu übermittelte Adresse benutzen. • 400 - 499—Client Fehler. Die ”404-Not found“ Response ist auch aus der www – Welt bekannt. Die Fehlermeldung wird ausgegeben, wenn die angegebene SIP – URL nicht in eine IP-Adresse aufgelöst werden kann. • 500 - 599 — Server Fehler. Diese Error-Response zeigt häufig einen Ausfall oder Absturz der Software an. Die ”508-Not implemented“ - Response besagt, dass der Client den Request nicht bearbeiten kann, da diese Funktion noch -9- 2. Technologischer Hintergrund nicht implementiert ist (z.B. wenn ein SIP-Client eine Instant Message an ein älteres SIP – Phone schickt). Die antwortende Response-Nachricht enthält weitgehend dieselben Parameter, wie der aufrufende Request (sh. Abb. 2-1). Abbildung 2 - 1: Kommunikation über SIP Besonders wichtig ist die Adressierung der Endgeräte (im Nachfolgenden Client genannt) über email-ähnliche Adressen der Form ”sip:[email protected]“. Dabei spielt es keine Rolle, ob sich die IP – Adresse des Clients ändert, denn bei dem Start und in regelmäßigen Zeitabständen muss der Client sich bei seinem zuständigen SIP – Provider registrieren. Der Provider betreibt für die Namens-/ Adressumsetzung hierzu einen so genannten SIP – Proxy, der unter der entsprechenden Domain rund um die Uhr verfügbar ist. Die Clients schicken ihre Anfragen an den Proxy, der diese dann an die entsprechenden Gegenstellen weiterleitet. Man unterscheidet zwischen ”Stateless“- und ”Statefull“ – Proxies. Erstere realisieren hauptsächlich die oben beschriebene Adressumsetzung. Die ”Statefull“ - Proxy-Server merken sich zusätzlich für jede Verbindung zwischen den Clients den aktuellen Status der Sitzung. Dadurch ist es möglich, die getätigte Verbindung und deren Dauer zu protokollieren, um so eventuell eine Kostenabrechnung zu realisieren. - 10 - 2. Technologischer Hintergrund Abbildung 2 - 2: Verbindungsaufbau mit Proxy Server Zu beachten ist bei der oben gezeigten Abbildung 2-2, dass die eigentlichen Sprachdaten im RTP – Protokoll nicht über den SIP - Proxy laufen, sondern direkt, d.h. ”peer to peer“, übertragen werden. 2.2 SDP (Session Description Protocol) Das zuvor erwähnte SIP-Protokoll überträgt selbst keine verbindungsspezifischen Parameter, wie zum Beispiel das verwendete Internet-Protokoll, IP-Addresse, Port und die zur Verfügung stehenden Audio- / Video-Codecs. Es fügt hierfür das Session Description Protocol (SDP) in die eigene Payload ein. Auch das SDP-Protokoll ist ASCII formatiert, die Parameter werden durch Abkürzungen eingeleitet: • v — Protokollversion • o — Absender und Session-Identifier • s — Session-Name • t — Medienzeitstempel • m — Medienname und –adresse - 11 - 2. Technologischer Hintergrund • i — Session-Information • u — URI der Session • e — Emailadresse • p — Telefonnummer • b — Bandbreiten-Informationen • z — Zeitzonen-Einstellungen • k — Chiffrierungsschlüssel • a — Session Attribut Sollte es während einer aufgebauten Kommunikation zur Veränderung der Audio- / Video-Codecs kommen, so ist es im SIP Protokoll vorgesehen, dass man eine ReINVITE Nachricht verschicken kann, die in ihrem SDP Header die neuen Verbindungsparameter enthält. Es ist wichtig anzumerken, dass der Call-ID Parameter im SIP Header unverändert bleibt. Die Call-ID stellt die eindeutige Zuordnung eines SIP-Kommunikationselements zu einer bestimmten Session dar. Alle Nachrichten und Statusinformationen, die eine bestimmte Session betreffen, tragen dieselbe Call-ID, die mindestens aus einem vom Session-Initiator generierten Zufallscode besteht. [sip] 2.3 RTP (Realtime Transport Protocol) Das Realtime Transport Protocol (RTP) wurde von der Audio-Video Transport Group der IETF entwickelt und ist Bestandteil von H.323. Es liegt auf der Anwendungsschicht und kann netzwerkbasierte Video- oder Audiokommunikation abwickeln (sh. Abb. 2-3). Zur Unterscheidung der Medien unterscheidet RTP zwischen verschiedenen Codierungsformaten, sodass die übertragenen Daten anwendungsunabhängig verwendet werden können. Für diesen Zweck wurden verschiedene RTP-Profile für Audio und Video definiert. - 12 - 2. Technologischer Hintergrund Abbildung 2 - 3: RTP im TCP/IP-Protokollstapel Das RTP-Protokoll ist unabhängig von den darunter liegenden Protokollen, es wird in der Regel mit dem UDP-Protokoll betrieben, wobei die RTP-Datenpakete in die UDPDatenpakete eingepackt werden. Das RTP-Protokoll basiert auf einer Ende-zu-Ende-Verbindung und unterstützt Unicast-Verbindungen, aber auch IP-Multicast. Es erkennt und korrigiert fehlende, doppelte oder in falscher Reihenfolge empfangene Datenpakete. Im RTP-Header existieren neben diversen Datenfeldern für die Version und das Padding zwei Datenfelder für die eindeutige Identifizierung der Datenquellen und der Quelladressen. Die Statusinformationen der Quellen werden durch das RTCPProtokoll, das Bestandteil von RTP ist, durch periodisches Senden rückgemeldet. [itw] 2.4 Peer-to-Peer-Netze 2.4.1 Einführung Peer-to-Peer-Netzwerke erfreuen sich immer größerer Beliebtheit. Ständig werden neue Anwendungen auf dieser Basis entwickelt. Das Internet bietet einen großen Vorrat an Ressourcen, sowohl in Form von Speicherplatz, als auch an Rechenleistung. Es ist möglich, mit Hilfe von Peer-to-Peer-Netzwerken solche Ressourcen zu teilen, genauso wie sie zu vereinen. Datenmessungen sagen voraus, dass das Datentransfervolumen solcher Anwendungen stark steigt und einen sehr wesentlichen Teil des Internetverkehrs ausmacht. - 13 - 2. Technologischer Hintergrund Peer-to-Peer bedeutet Kommunikation unter Gleichen. Das heißt, dass solche Netzwerke im Gegensatz zum heute aktuellen Client/Server-Prinzip stehen (sh Abb. 2-4), bei dem Server Dienste anbieten, die von Clients genutzt werden. Ein Beispiel dafür sind Webserver und die auf Nutzerseite installierten Browser. In einem Peer-toPeer Netzwerk ist prinzipiell jeder Knoten gleich und nimmt die Rolle eines Servers, eines Clients und eines Routers an, der Nachrichten weiterleitet. Dafür wird ein Overlay-Netzwerk gebildet, das über der bestehenden Infrastruktur eines Netzwerkes, zum Beispiel des Internets, aufgebaut wird. Abbildung 2 - 4: Client/Server Verbindungsprinzip 2.4.2 Klassifizierung Man unterscheidet drei Arten von Peer-to-Peer Netzwerken: • Zentralisiert: Es gibt zentrale Server (sh. Abb. 2-5), die die Kommunikation steuern. Beispielsweise bei Napster wurden die Verzeichnisinhalte der Knoten auf solchen Servern gespeichert. Anfragen von Knoten wurden von diesen Maschinen bearbeitet und entsprechende Ergebnisknoten mit gewünschtem Inhalt zurückgeliefert. - 14 - 2. Technologischer Hintergrund Abbildung 2 - 5: Zentralisierte P2P-Architektur • Strukturiert, dezentralisiert: In solchen Systemen existieren keine zentralen Server mehr, jedoch herrscht eine bestimmte vordefinierte Netzwerkstruktur vor (sh. Abb. 2-6). In dieser Struktur werden Dateien an bestimmten Orten platziert, um Anfragen besser bearbeiten zu können. Solche Systeme sind beispielsweise Distributed Hash Tables (DHT) wie Tapestry, Pastry, Chord oder CAN. Abbildung 2 - 6: Strukturierte, dezentralisirte P2P-Architektur - 15 - 2. Technologischer Hintergrund • Unstrukturiert, dezentralisiert: In diesen Systemen gibt es auch keine zentralen Server (sh. Abb. 2-7). Hinzu kommt, dass die vordefinierte Netzwerkstruktur wegfällt und es keine Regelung über die Dateiplatzierungen mehr gibt. Dadurch werden die Suchmechanismen meist über Flooding umgesetzt, so dass eine sehr hohe Netzlast entsteht. Ein Beispiel für ein unstrukturiert dezentralisiertes Peerto-Peer-Netzwerk ist Freenet. Abbildung 2 - 7: Reine P2P-Architektur Zentralisierte Systeme haben zwar den Vorteil, dass Anfragen durch zentrale Indexe leichter realisiert werden können, jedoch gerade die zentralen Server bilden den Schwachpunkt dieser Netzwerke. Zum einen können dadurch schnell Engpässe entstehen, wenn die Anzahl der Anfragen auf einem Server steigt, und zum anderen funktioniert das Netzwerk ohne die Server nicht. Somit gibt es zentrale Angriffspunkte, um das gesamte Netzwerk einzuschränken. Ein weiterer Punkt ist die Vertraulichkeit der Daten. Dritte können leicht von wenigen Stellen aus das gesamte Netzwerk überwachen. Dezentralisierte Netzwerke haben diese Probleme nicht. Sie sind sehr viel robuster gegen Ausfälle und Überwachung, da es keine zentralen Instanzen gibt. Wenn wenige Knoten ausfallen, bleibt das Netzwerk weiterhin funktionsfähig. In unstrukturierten Systemen entsteht das Problem des Overheads der Suche. Im Gegensatz dazu stehen die strukturierten Netzwerke. DHT – Systeme (dt.: verteilte Hashtabellen) bieten eine Hashtabellen-ähnliche Funktionalität an, bei der Objekte auf Knoten abgebildet werden. Das System liefert auf die Anfrage nach einem Objekt den Knoten, der es speichert. [dp] - 16 - 2. Technologischer Hintergrund 2.5 Peer-To-Peer-Netze für Live Video Streaming Der folgende Abschnitt besteht zum Großteil aus einer Übersetzung des englischsprachigen Artikels „A survey on peer-to-peer video streaming systems“ [sr], welcher die unterschiedlichen P2P Streaming Architekturen analysiert. In letzter Zeit steigt die Zahl der Benutzer, die Video-over-IP im Internet nutzen. Das traditionelle Client/Server-Videostreaming kostet die Provider viel Geld wegen der benötigten Bandbreite. Die P2P Netze sind der beste Anreiz für neue verteilte Netzwerkanwendungen. Kürzlich wurden von einigen Anbietern P2P Streaming Systeme eingeführt, die Live- und Video-on-Demand (VoD) Streaming-Dienste anbieten, deren Kosten deutlich geringer ausfielen. In diesem Kapitel werden die existierenden P2P Lösungen für Live Video Streaming betrachtet. Die Topologien für diese P2P Streaming Systeme sind Baum (tree), Multi-Baum (multi-tree) und vermaschte (mesh) Strukturen. Im Folgenden werden die Herausforderungen und die Lösungen für die Live Video Streaming Systeme in einer P2P-Umgebung geschildert. 2.5.1 Einführung Im Jahr 2006 stieg die Zahl der ausgelieferten Video Streams im Internet um 38,8 % auf 24,92 Mrd. Allein youtube.com hostet mehr als 45 TBytes Videomaterial und die Anzahl der angeschauten Videos bis September 2006 stieg auf 1,73 Mrd. Bei steigenden Bandbreiten im Zugang wird erwartet, dass der Video-Verkehr der dominierende Datenverkehr in der Zukunft sein wird. Die Basislösung für Videostreaming ist das Client/Server Model. Der Client initiirt eine Verbindung mit dem Quellserver und der Videoinhalt wird direkt zum Client gestreamt. Eine Variation von diesem Model ist die Content Delivery Network (CDN) -basierte Lösung. Bei ihr werden zuerst die Inhalte zu anderen Zustellservern geschoben, die an strategischen Stellen im Netz platziert sind, so dass bei einem Download der Client aufgefordert wird, von dem am nähsten liegenen Server die Daten zu laden. CDN kürzt sehr effektiv die Zeit zum Download und kann damit mehr Benutzer gleichzeitig bedienen. Youtube setzt CDN ein, um seine Endnutzer zu versorgen. Die größte Herausforderung für die Server-basierte Architektur liegt bei der Skalierbarkeit. Die Videos mit hoher Qualität brauchen auch eine hohe Datenrate. Mit den heutigen Kompressionsverfahren benötigt man 400 kbps, um TV Qualität zu erreichen. Die Bandbreite des Providers muss proportional mit der Clientzahl wachsen. Das macht das Server-basierte Video Streaming teuer und stimuliert gleichzeitig die Entwicklung von P2P Anwendungen. Die Grundidee bei den P2P Applikationen ist, dass die Benutzer als Client und Server fungieren, bekannt als Peer. Im P2P Netzwerk laden die Benutzer nicht nur Daten herunter, sondern senden diese auch an andere Benutzer im Netz. Die Uploadgeschwindigkeit von den Endnutzern wird effizient ausgenutzt, um die sonst reduzierte Bandbreite beim Client/Server Prinzip zu ersetzen. Anwendungen wie BitTorrent und eMule werden verbreitet angewendet, um schnellen Datenaustausch über das Internet zu ermöglichen. Weiter wird die P2P Technologie eingesetzt, um Dienste wie Mediastreaming anzubieten. Anwendungen wie PPLive und PPStream wurden entwickelt, um Dienste wie Video-on-Demand und Live-TV zu unterstützen. - 17 - 2. Technologischer Hintergrund Studien zufolge haben in 2006 mehr als 200.000 Users gleichzeitig broadcast Videos geschaut, mit einer Bitrate von 400 bis 800 kbps. Die P2P Streaming Systeme können in zwei Gruppen klassifiziert werden. Das sind die Baum- und die vermaschten Strukturen. Die Baumstruktur besitzt gut organiserte Overlaystruktur und liefert Videos, indem sie die Daten an ihren untergeordneten Knoten (children peers) sendet. Ein grosser Nachteil von dieser Architektur ist ihre Anfälligkeit für Benutzerdynamik im Netz. Beispielsweise wenn sich ein Benutzer abmeldet, wird er die Übertragung an seine Kinder stören. Bei den vermaschten Strukturen sind die Peers nicht an eine statische Topologie gebunden. Stattdessen ist die Architektur auf das ständige An- und Abmelden von Benutzern spezialisert. Die Peers verbinden sich dynamisch mit einer Zufallszahl von anderen Peers in dem System. Periodisch tauschen sie Informationen über ihren Datenbestand aus. Die Peers beziehen die Videodaten von ihren Nachbarn, die bereits die Daten erhalten haben. Diese Eigenschaften machen die vermaschten Videostreaming Systeme sehr robust gegen Benutzerdynamik. Dennoch, die ständige Bewegung von Peers macht die Effizienz von Videoverteilung unberechenbar. Die Pakete können unterschiedliche Routen nehmen, um an das Ziel zu kommen. Als Konsequenz leiden die Benutzer an abfallender Videoqualität durch die niedrigen Bitraten, längere Startupverzögerungen und Einfrieren des Bildes während der Übertragung. Das Videostreaming kann man in zwei Kategorien klassifizieren: Live Video und Video-on-Demand. Bei der Live Übertragung werden die Inhalte in Echtzeit unter allen Nutzern verbreitet. Das Abspielen des Videos ist unter allen Peers synchronisiert. Dagegen erfreuen sich die Video-on-Demand Nutzer an der Flexibilität, sich diejenigen Videos anzuschauen, die sie möchten und wann sie möchten. Das Abspielen von einem Video ist nicht unter den Peers synchronisiert. In diesem Abschnitt werden die verschiedenen Overlaystrukturen näher beschrieben. 2.5.2 Baumbasierte Systeme In den jungen Jahren des Internet wurde das IP Level Multicast als effizienter Weg zum Streamen von Audio und Video eingesetzt. Bei der IP Mutlicast Session ist der Server mit allen Clients verbunden, indem sie einen Multicast Baum mit Hilfe der IP Router aufbauen. Leider ist dieses Verfahren mit einer hohen Komplexität und Verwaltungsaufwand verbunden. Deswegen fand das IP Level Multicast nie eine größere Anwendung im Internet. Stattdessen wurden die Funktionen auf der Anwendungsschicht implementiert. Videoserver und User formen das Application Layer Multicast, wo die Videoinhalte verbreitet werden. • Single-tree Streaming Ähnlich wie beim IP Multicast Baum, welcher auf Routern auf der Netzwerkschicht basiert, bilden die User einen Baum auf der Anwendungsschicht, dessen Wurzel beim Videoserver liegt (sh. Abb. 2-8). Jeder Peer verbindet sich mit dem Baum auf einer bestimmten Ebene. Er bekommt die Videodaten von seinen Eltern über sich und leitet die empfangenen Inhalte weiter an seine Kinder. Abbildung 2-8 illustriert einen Application Layer Multicast Baum mit zehn Knoten. - 18 - 2. Technologischer Hintergrund Es gibt zwei Peers auf Schicht 1, die ihre Daten direkt vom Streamingserver beziehen. Vier Peers auf Schicht 2 bekommen die Pakete von den Eltern von Schicht 1. Drei von denen leiten die erhaltenen Videodaten an vier weitere Peers auf die unterste Ebene weiter. Abbildung 2 - 8: P2P Video Streaming auf Basis von Application Layer Multicast Bei dieser Konstellation aus zehn Peers sind mehrere Möglichkeiten vorhanden, um einen Streamingbaum aufzubauen. Die Hauptbetrachtung beinhaltet die Tiefe des Baums und die Auslastung der inneren Knoten. Peers aus den niedrigen Schichten erhalten den Videostrom vor den Peers aus den höheren Schichten. Um die Verzögerungen zu minimieren, sollte man die Anzahl der Schichten so klein wie möglich halten. Mit anderen Worten, die Baumtopologie sollte so breit wie möglich auf einer Schicht sein. Die innen liegenden Knoten können mit ihrer Uploadgeschwindigkeit nur an eine begrenzte Zahl von Kinderknoten Daten weiterleiten. Die Bitrate beschränkt ihre Kapazität. Im Normalfall für die Zwecke von load balancing und Fehlerstabilität muss die Ausgangsgeschwindigkeit der Peers unterhalb ihres Maximum liegen. Ein anderer wichtiger Punkt bei der Baumstruktur ist die Wartung des Systems. Die Benutzer können im Regelfall sehr dynamisch sein. Der User kann eine Session angekündigt oder unangekündigt, etwa beim Rechner-Absturz, verlassen. Beim Verlassen wird die Verbindung zu allen seinen Kinderknoten unterbrochen und damit können sie keine Videosequenzen mehr empfangen. Abbildung 2-9 zeigt ein mögliches Szenario, wenn ein Peer nahe an dem Videoserver den Baum verlässt. So werden alle fünf der unterliegenden Knoten vom Quellserver getrennt. - 19 - 2. Technologischer Hintergrund Abbildung 2 - 9: Ausfall eines Knoten Abbildung 2-10 zeigt wie der Baum die Verbindung zu den Peers erneut herstellt, indem sie vom Server oder anderen Peers vererbt wird. Abbildung 2 - 10: Korrektur des Baumes nach dem Ausfall Die Baumstruktur kann entweder zentral oder verteilt aufgebaut und gewartet werden. Bei der zentralisierten Lösung kontrolliert der Server den Aufbau und die Wiederherstellung eines Baums. Wenn sich ein User im System anmeldet, kontaktiert er als erstes den Zentralserver. In diesem Fall entscheidet der Server auf Basis der momentanen Baumstruktur, an welcher Position sich der Peer verbindet und benachrichtigt seine Elternknoten. Der Server kann das Verlassen eines Knotens aufspüren, indem er seine Abmelde-Nachricht auswertet oder nach einem bestimmten Timeout-Verfahren. In beiden Fällen muss die Topologie des Systems neu kalkuliert werden. Bei großen Streaming Systemen kann es vorkommen, dass die Serverressourcen knapp werden. Um dies zu vermeiden, werden Algorithmen angewendet, um die nötigen Wartungsarbeiten verteilt zu erledigen. - 20 - 2. Technologischer Hintergrund Ein weiterer Nachteil der Ein-Baum-Strukturen ist, dass alle Knoten, die ein Blatt im Baum sind, also am Rande der Struktur sind, nicht mit ihrer Uploadgeschwindigkeit beitragen, sondern nur Daten bekommen. Bei größeren Systemen wird dadurch die Effizienz des Gesamtsystems erheblich beeinträchtigt. • Multi-tree streaming Bei den Multi-Baum Strukturen teilt der Server den Stream in mehrere Unterstreams (sub-stream) auf. Statt nur einen Baum zu benutzen, wird jeweils einen für jeden Sub-Stream eingesetzt. Jeder Peer meldet sich an jedem Baum an, um Daten von jedem Unterstream zu empfangen. Der Datenstrom von jedem Sub-Stream fließt Schicht für Schicht vom Server bis zu den Blättern. Ein Peer liegt an verschiedenen Stellen in jedem Baum. Beispielsweise kann er ein innerer Knoten in einem Baum sein und in dem Anderen ein Blatt sein. So wird immer die Uploadgeschwindigkeit von jedem Peer ausgenutzt, immer dann wenn er ein interner Knoten in einem Baum ist. Dies lässt sich auch verbessern, indem die Anzahl der Bäume, wo der Peer als interner Knoten fungiert, proportional zu seiner Uploadbitrate ist. In so einer MultiBaum Struktur mit m Sub-Streams ist jeder einzelne User ein innerer Peer in nur einem Baum und ein Blatt in den restlichen m-1 Unterbäumen. Abbildung 2-11 zeigt ein Beispiel mit 7 Peers und 2 Sub-Streams. Der Server splittet den Video in zwei Teile auf und verteilt die Daten entsprechend in den linken und in den rechten SubStream. Peer 0, 1 und 2 sind innere Knoten in dem linken Unterbaum und Blätter in dem rechten. Gleichartig sind auch Peers 3, 4 und 5 auf der anderen Hälfte aufgeteilt. Abbildung 2 - 11: Multi-tree basiertes Streaming mit zwei Sub-streams Jeder Benutzer besitzt eine Bandbreite von 1 und kann damit gleichzeitig an zwei seiner Kinder mit einer Bitrate von 0,5 Daten hoch laden. Hier ist zu merken, dass Peer 6 in beiden Sub-Bäumen ein Blatt ist. Dies ist deswegen der Fall, weil nur sechs Peers nötig sind, um an sieben Clients zu streamen. - 21 - 2. Technologischer Hintergrund 2.5.3 Vermaschte Systeme Wie bereits beschrieben, bei den Baum-basierten Strukturen hat ein Peer nur einen Elternknoten, von welchem er alle Inhalte herunterlädt. Dieses Design ist anfällig bei Benutzerdynamik. Verlässt ein Elternknoten das Netz, so bleibt seine Nachkommen unversorgt. Mit steigender Häufigkeit dieser An- und Abmeldevorgänge wird die Wartung des Systems zu einer großen Herausforderung. Dafür steigt in letzter Zeit auch die Zahl der vermaschten Strukturen. Bei denen gibt es keine statische Streamingtopologie. Die Peers bauen und trennen ihre Verbindungen sehr dynamisch. Sie verwalten eine Großzahl von Verbindungen zu anderen Peers im Netz. Ein Peer kann gleichzeitig mit mehreren anderen Peers Daten herunter- und hochladen. Wenn ein Nachbar das Netz verlässt, kann der User die Daten von den übrig gebliebenen weiter empfangen. Gleichzeitig sucht er nach anderen Peers, mit welchen er eine neue Verbindung initiieren kann, um eine Mindestzahl von Verbindungen aufrecht zu erhalten. Dies macht die vermaschten Strukturen sehr robust gegen Peerdynamik. In diesem Abschnitt wird ein tieferer Blick ins System verschafft. Ähnlich wie bei den P2P File Sharing Systemen (BitTorrent) besitzen die vermaschten Systeme einen Tracker, welcher eine Liste von aktiven Peers in der Videosession verwaltet. Wenn sich ein neuer Peer anmeldet, kontaktiert er als erstes den Tracker und teilt ihm Informationen wie IP Adresse und Portnummer mit. Der Tracker antwortet ihm mit einer Liste, die eine Menge von zufälligen anderen Peers beinhaltet. Die Anzahl kann sehr variieren, von einigen Wenigen bis zu Hunderten. Nach dem Erhalten der Initialliste versucht der neue Peer sich mit Einigen zu verbinden. Wenn eine neue Verbindung entsteht, dann fügt der User die Remoteadresse zu seiner Nachbarliste. Nachdem er genug Nachbarn gefunden hat, beginnt er mit dem Austausch von Videosequenzen. Abbildung 2-12 zeigt den Initialprozess. Um seine Liste aktuell zu halten, updatet der Peer sie ständig während der Session. Er kann auch beim Tracker nach einer aktuellen Liste nachfragen, oder mit anderen Peers sich die eigenen austauschen. Wenn ein Peer das Netz angekündigt verlässt, dann meldet er sein Verlassen beim Tracker und bei den anderen Peers, damit sie ihn aus der Liste streichen können. Um unangekündiges Verlassen zu erfassen, senden die Knoten unter sich keep-alive Nachrichten. So werden Peers aus der Liste entfernt, wenn sie nach einer Timeout-Zeitperiode keine Nachricht verschickt haben. - 22 - 2. Technologischer Hintergrund Abbildung 2 - 12: Abfrage der Peerliste vom Tracker-Server Jedes System implementiert eigene Algorithmen, mit wem die Peers eine Verbindung aufbauen. Diese Entscheidung wird von verschiedenen Kriterien beeinflusst, wie: • Auslastung und Ressourcenverfügbarkeit auf beiden Seiten, z.B. Anzahl der Verbindungen, Upload- und Downloadgeschwindigkeit, CPU- und Speicherauslastung. • Qualität einer potenziellen Verbindung, z.B. die Verzögerung und Verlustrate zwischen beiden Enden. • Gesuchte Inhalten, z.B. ob der User die Videoinhalten braucht, die ihm der Lokaluser anbieten könnte. Mit Hilfe dieser Kriterien wird eine bessere Streamingperformanz erreicht. Bei der Baumstruktur fließt der Datenstrom von der Videoquelle zu den Peers entlang des Baumes. Bei den vermaschten Topologien findet dieses Verfahren keinen Einsatz. Hier schneidet der Quellserver die Videoinhalte in viele kleine Stücke, die Videochunk heißen. Sie sind die Grundeinheiten und beinhalten Mediadaten für ein sehr kleines Zeitintervall, z.B. 0,1 Sekunden. Jedes Chunk hat eine eindeutige Sequenznummer, die an den Videoinhalt angepasst ist. Die Peers verbreiten die Chunks durch das Netz. So können sie verschiedene Wege nehmen, bis sie beim Benutzer angekommen sind. Meistens kommen sie in vermischter Reihenfolge an und deswegen müssen sie zuerst gepuffert werden, bis die restlichen Chunks erhalten sind. Dann können sie zum Abspielen gestellt werden. Die gepufferten Chunks werden weiter an den Nachbarn geschickt. Je nach Systemdesign können manche Peers die Chunks einige Minuten puffern. Beim Livestreaming steigt die Sequenznummer der gepufferten Chunks mit dem Playbackfortschritt konstant. - 23 - 2. Technologischer Hintergrund Es gibt zwei Arten von Datenaustausch bei den vermaschten Systemen: push und pull. Beim push-Modell schicken die Peers ihre Daten blind zu ihren Nachbarn. Da keine klare Eltern/Kinder Beziehung existiert, kann es vorkommen, dass zwei Peers das gleiche Chunk an einen Peer schicken. So wird die Uploadgeschwindigkeit mit unnötiger Redundanz belastet. Um dieses Problem zu umgehen, muss das Verschicken von Chunks unter den Nachbarn sorgfältig geplant werden. Dieses Verfahren muss auch beim Eintreffen oder Verlassen von Peers funktionieren. Ein anderer Weg, dieses Problem zu vermeiden, ist die pull-Methode. Die Daten werden „gezogen“ statt blind „gedrückt“. Die Peers tauschen die sog. buffer maps, die die Sequenznummern der verfügbaren Chunks beinhalten. Nachdem ein User die Buffer Maps von seinen Nachbarn erhalten hat, kann er entscheiden, welche Chunks von welchem Peer er braucht. Er sendet eine Anfrage an den entsprechenden Nachbarn und bekommt den richtigen Chunk. Damit wird die Redundanz von Chunks vermieden. Der häufige Austausch von Buffer Maps und die häufigen Chunk-Anfragen sind mit mehr Signalisierungsoverhead verbunden und können das Videostreaming zusätzlich verlangsamen. 2.6 OSGi - Dynamic Module System for Java 2.6.1 Einführung Das Konzept der Modularisierung ist heute als Mittel zur Kompläxitätsreduzierung bei der Entwicklung großer Anwendungssysteme unumstritten. In Java gestaltet sich die Umsetzung dieses Konzepts schwierig, da unterstützte Sprachkonzepte fehlen. Monolithische Anwendungssysteme sind die häufige Folge. [Osp] 2.6.2 Die OSGi Service Plattform Die OSGi Service Plattform löst dieses Problem, indem sie ein dynamisches Modulsystem bereitstellt. Es handelt sich um eine javabasierte Softwareplattform, die die dynamische Integration und das Fernmanagement von Softwarekomponenten (Bundles) und Diensten (Services) ermöglicht. Bundles und Services können zur Laufzeit in der Serviceplattform installiert und gestartet, gestoppt und deinstalliert werden, ohne dass die Plattform als Ganzes angehalten werden muss. [Osp] Die OSGi Service Platform wird von der OSGi Alliance vorangetrieben. Gegründet wurde die OSGi Alliance 1999. Ihr gehören (Stand März 2008) 30 Firmen aus recht unterschiedlichen Branchen als Full Member (Vollmitglied) an, wie etwa Sun Microsystems, IBM, Nokia, Motorola, Oracle, NEC, Hitachi, Sprint, Samsung, Siemens, Telefónica, BEA, Deutsche Telekom, Red Hat und ProSyst. Zu den Vollmitgliedern kommen (Stand März 2008) noch 9 sogenannte Adopters hinzu, darunter auch die Eclipse Foundation, sowie 14 Supporters, darunter z. B. LG Electronics. Es existieren Expert Groups für die Bereiche Residential, Enterprise, Mobile, Vehicle und Core Platform. [Owk] Durch die Standardisierung wurde die OSGi Service Plattform für den Einsatz in verschiedenen Bereichen interessant: - 24 - 2. Technologischer Hintergrund • Smart Phones: Durch den JSR 232. • Automotive: BMW setzt als Grundlage der Telematiksysteme ihrer künftigen Fahrzeuge auf die OSGi Service Plattform • Desktop-Applikation: Seit der Version 3.0 setzt Eclipse grundlegend auf OSGi. • Serverseitige Anwendungen: Ab Version 6.1 basiert der WebsphereApplikationserver auf der OSGi Service Plattform. 2.6.3 Implementierungen Es existieren folgende Implementationen von OSGi: • Eclipse Equinox: Eclipse Equonix ist die Basis aller Eclipse-Anwendungen, vor allem die Eclipse-DIE. Damit ist Equinox wohl die am weitesten verbreitete laufende OSGi Plattform auf dem Desktop • Prosyst mBedded Server Equinox Edition: Die mBedded Server Equinox Edition wird von der Firma Prosyst entwickelt. Seit März 2007 ist sie auch als Open-Source Produkt verfügbar. • Knopflerfish: Knopflerfish ist die Open-Source-Variante der kommerziellen OSGi-Implementierung Knopflerfish Pro. Die OSGi-R4-Implementation heißt Knopflerfish 2 und steht unter eigener Lizenz, die sich an der BSD-Lizenz orientiert. • Apache Felix: Felix [af] ist seit Anfang 2007 ein Top-Level-Projekt bei Apache. Felix basiert auf einer OSGi-Implementierung namens Oscar [os]. Felix steht unter der Apache Lizenz 2.0. 2.6.4 Das OSGi Framework Die Basiskomponente der Plattform ist das OSGi Framework, das einen Container für Bundles und Services zur Verfügung stellt. Die Abbildung 2-13 zeigt den Schichtenaufbau einer typischen OSGi-Architektur. - 25 - 2. Technologischer Hintergrund Abbildung 2 - 13: OSGi Systemschichtung [Quelle:WiKipedia] 2.6.5 Grundelemente Bundles: Ein Bundle ist eine fachlich oder technisch zusammenhängende Einheit von Klassen und Ressourcen, die eigenständig im Framework installiert bzw. deinstalliert werden kann. Die in einem Bundle enthaltenen Klassen und Ressourcen sind zunächst für andere Bundles nicht sichtbar. Um sie für andere Bundles nutzbar zu machen, muss ein Bundle die Packages, in denen die Klassen und Ressourcen enthalten sind, explizit exportieren. Dazu müssen die Packages, die ein Bundle benutzen möchte, von ihm importiert werden. Das Im- und Exportieren von Packages erfolgt über eine Manifest-Datei. Das OSGi Framework sorgt zur Laufzeit dafür, dass jedem Bundle die importierten Java-Packages bereitgestellt werden. Ist dies nicht möglich, weil etwa die benötigten Ressourcen nicht vorhanden sind, kann das Bundle im Framework nicht zur Ausführung gebracht werden. [Osp] Services: Als weiteres Mittel zur Entkopplung zwischen unterschiedlichen Modulen können Bundles Services verwenden, die systemweit zur Verfügung stehen. Ein Service ist ein Java-Objekt, das unter einem Interface-Namen bekannt gemacht wird. Die Registrierung eines Service erfolgt über die Service Registry, die zentral und bundleübergreifend bereitsteht und an der die angemeldeten Dienste von beliebigen - 26 - 2. Technologischer Hintergrund Bundles abgefragt werden können. Bundles, die einen bestimmten Dienst benötigen, können den entsprechenden Service dann von der Service Registry abfragen, ohne konkret wissen zu müssen, welche Implementierung dahintersteht. [Osp] Management Agents: Über einen Management Agent, der Bestandteil des jeweiligen Frameworks ist, können Bundles verwaltet werden (Installieren, Deinstallieren, Starten, Stoppen). Der Agent liegt gewöhnlich selbst in Form eines oder mehrerer Bundles vor. Im einfachsten Fall stellt ein Management Agent eine textbasierte Konsole bereit, über die Bundles mittels Kommandos verwaltet werden können. [Osp] - 27 - 3. Konzept 3. Konzept Im vorigen Kapitel wurden einige Internetprotokolle erläutert, die für die Multimediakommunikation im Internet zuständig sind. Anschließend wurden die P2PNetze vorgestellt, mit deren Hilfe man den Datenaustausch ermöglicht. In diesem Kapitel wird weiter auf den bisher vorgestellten Technologien aufgebaut. Es wird das Konzept vorgestellt, um während einer Audio- bzw. Videokonferenz Live Video zu streamen. Dafür muss man zuerst eine Konferenz aufbauen können. Ist es dann soweit, können sich die Teilnehmer in einem Peer-to-Peer Netz anmelden, um die gewünschten Videos auszutauschen. Wichtig für das Endergebnis ist, dass ein Weg gefunden wird, wie zwischen zwei Protokollen umgeschaltet werden kann. In diesem Fall bedeutet dies eine erneute Signalisierung für die beteiligten User Agents, um ihnen mitzuteilen, dass ein per P2P live gestreamter Video zur Session hinzugefügt wird. 3.1 SIP-basierte Konferenz mit Live Video Streaming SIP ist ein Protokoll zum Einleiten, Manipulieren und Beenden von Multimedia über IP-Sessions. Gemäß RFC3261 kann eine derartige Session auch eine Konferenz zwischen drei oder mehr Teilnehmern darstellen. Es existieren verschiedene Ansätze für das Aufbauen einer Konferenz. Eine Möglichkeit bietet sich mit Hilfe einer MCU (Multipoint Control Unit), auch als Conference Server bekannt. Er besteht aus zwei Teilen – Focus und Mixer. Im Falle einer SIP-basierten Sprach- bzw. Multimediakonferenz benötigt man ein Element, das für jedes an einer Konferenz beteiligte Endsystem jeweils die Rolle einer SIP-Kontaktinstanz übernimmt. Diese Rolle übernimmt der Focus. Des Weiteren müssen die von allen beteiligten User Agents abgehenden Nutzdaten (Audio/Video) zu einem Gesamtsignal gemischt und diesem wiederum allen beteiligten User Agents als ankommendes Nutzdatensignal zur Verfügung gestellt werden. Für diese Aufgabe wird den Mixer benötigt, welcher durch den Focus anhand der per SIP/SDP ausgetauschten Session- und MedienEigenschaften gesteuert wird. [sip] Es gibt auch die so genannten Ad-hoc Konferenzen, wo jeder User Agent eine Konferenz starten kann und andere Agents einlädt. Dieser Benutzer wird Focus User Agent genannt. In Abbildung 3-1 wird kurz dargestellt, wie der Verbindungsaufbau aussieht. - 28 - 3. Konzept Abbildung 3 - 1: Aufbau einer Ad-hoc Konferenz Der Focus User Agent versendet seine INVITE – Nachricht, in welcher der „isfocus“ Parameter gesetzt ist. Dieser Parameter besagt, dass der Absender eine Konferenz initiieren möchte. Weitere Informationen findet man in RFC4579 [r3]. Nachdem der Initiator die INVITE - Nachrichten an jeden verschickt hat, der an der Konferenz teilnehmen möchte, wartet er auf die entsprechenden Antworten. F1: INVITE sip:[email protected] SIP/2.0 Via: SIP/2.0/UDP l00.100.100.101:5060;branch=z9hG4bKfw19b Max-Forwards: 70 To: Alice <sip:[email protected]> From: Bob <sip:[email protected]>;tag=76341 Call-ID: [email protected] CSeq: 1 INVITE Contact: "Bob" <sip:100.100.100.101:5060;transport=udp>;isfocus Content-Type: application/sdp Content-Length: 158 v=0 o=bob 2890844526 2890844526 IN IP4 100.100.100.101 s=Phone Call c=IN IP4 100.100.100.101 t=0 0 m=audio 49170 RTP/AVP 0 a=rtpmap:0 PCMU/8000 - 29 - 3. Konzept Die eingeladenen User Agents analysieren die INVITE und wenn sie diese Art von Konferenz unterstützen, antworten sie mit dem Header-Argument Supported:conf. F2: SIP/2.0 200 OK Via: SIP/2.0/UDP l00.100.100.102:5060;branch=z9hG4bKfw19b To: Bob <sip:[email protected]>;tag=a53e42 From: Alice <sip:[email protected]>;tag=76341 Call-ID: [email protected] CSeq: 1 INVITE Contact: "Alice" <sip:[email protected]> Supported:conf Content-Type: application/sdp Content-Length: 155 v=0 o=Alice 2890844528 2890844528 IN IP4 l00.100.100.102 s=Phone Call c=IN IP4 l00.100.100.102 t=0 0 m=audio 60000 RTP/AVP 0 a=rtpmap:0 PCMU/8000 Das SIP Protokoll sieht vor, dass während einer bereits laufenden Kommunikation Änderungen vorgenommen werden können. Das Senden einer INVITE-Anfrage im Rahmen einer bereits bestehenden SIP-Session wird als re-INVITE bezeichnet. Üblicherweise dient das zur Modifizierung der Session, die sich auf den Nutzdatenaustausch zwischen den Endsystemen auswirkt. In dieser Anwendung wird sich genau diese Eigenschaft zu Nutze gemacht. Damit alle User Agents mitbekommen, dass Einer ein Video streamen möchte, verschickt er an Alle einen erneuten INVITE (re-INVITE), um die anderen Teilnehmer einzuladen. Wichtig ist es anzumerken, dass der SIP Header sich nicht verändert, und das CallID Feld muss sogar gleich bleiben, sonst wird das als ein separater Verbindungsaufbau erkannt. Das Neue ist das SDP-Teil. Dort werden alle relevanten Informationen übergeben, die die anderen User Agents benötigen, um sich zum Teil von dem Videostreaming zu machen. - 30 - 3. Konzept INVITE sip:[email protected] SIP/2.0 Via: SIP/2.0/UDP l00.100.100.101:5060;branch=z9hG4bKfw19b Max-Forwards: 70 To: Alice <sip:[email protected]> From: Bob <sip:[email protected]>;tag=76341 Call-ID: [email protected] CSeq: 1 INVITE Contact: "Bob" <sip:100.100.100.101:5060;transport=udp>;isfocus Content-Type: application/sdp Content-Length: 266 (z.B.) v=0 o=bob 2890844526 2890844526 IN IP4 100.100.100.101 s=Streaming Session i=A Streaming session declared within the session description protocol c=IN IP4 100.100.100.101 t=0 0 m=application 7200 TCP/STS 515 a=connection:new a=control: sts://l00.100.100.101 a=fmtp:515 bootstrap: uri="sts://l00.100.100.101:7200" Das m-Feld im SDP Header ist wie folgt definiert: m=<media> <port> <transport> <format-list> Es liefert Informationen über den Typ der Media Session, z.B.: m=application 7200 TCP/STS 515 Hier wird die Bezeichnung TCP/STS (für Striped Session) verwendet, was frei für unsere Zwecke erfunden ist. Sie wird verwendet, um die externe Anwendung zu kennzeichnen, die gestartet werden soll. Betrachten wir: a=connection:new a=control: sts:/100.100.100.101 a=fmtp:515 bootstrap: uri="sts:/100.100.100.101:7200" Das a-Feld beinhaltet die benötigten Attribute für die externe Anwendung. Sie beschreiben den Verbindungstyp, die Bootstrap IP und Port. Das Feld connection:new ist ein optionales Feld, welches meint, dass es möglich wäre, mehr als nur eine Verbindung von diesem User Agent zu haben. Dieser Fall wird aber nicht weiter behandelt. Mit control wird die IP Adresse des einladenden Peers mitgeteilt. Anschließend übergibt das fmtp-Feld die IP Adresse und Portnummer, die zum Einloggen benötigt werden. Die IP Adresse kommt doppelt vor, da es möglich wäre, dass die beiden abweichen. - 31 - 3. Konzept Als Antwort auf diese Einladung senden die anderen User Agents die entsprechenden Nachrichten wie folgt: SIP/2.0 200 OK Via: SIP/2.0/UDP l00.100.100.102:5060;branch=z9hG4bKfw19b To: Bob <sip:[email protected]>;tag=a53e42 From: Alice <sip:[email protected]>;tag=76341 Call-ID: [email protected] CSeq: 1 INVITE Contact: "Alice" <sip:[email protected]> supported:conf Content-Type: application/sdp Content-Length: 155 v=0 o=Alice 2890844528 2890844528 IN IP4 l00.100.100.102 s=Phone Call c=IN IP4 l00.100.100.102 t=0 0 m=application 7878 TCP/STS 515 a=fmtp:515 status=starting Die Nachricht sieht wie eine typische OK-Meldung aus, nur werden zwei Zusatzfelder mit versendet, die besagen, dass sich der eingeladene Peer in einer Boot-Phase befindet. Nachdem das Aufbauen einer Konferenz beschrieben wurde, wird ein tieferer Blick in die P2P Netze benötigt. Es wird die Pastry-Infrastruktur und das damit verbundene Konzept vorgestellt. 3.2 Das P2P Konzept In P2P-Architekturen ohne zentralen Verwaltungsserver besteht die Notwendigkeit zur Selbstorganisation und der dezentralen Datenhaltung. Es wird dazu eine Art Datenbank benötigt, die alle angemeldeten Clients, deren Ressourcen und die Route dahin kennt. [thr] Alle Implementierungen von DHT basieren auf Hashtabellen, die ihrerseits auf Hashfunktionen basieren. Die bekanntesten Hashalgorithmen sind SHA-1 und MD5, wobei sich SHA-1 in der P2P-Welt durchgesetzt hat. 3.2.1 Hashfunktionen Hash-Funktionen reduzieren Daten beliebiger Länge auf eine numerische Ausgabe mit fester Länge. Da die Quellmenge in den meisten Fällen größer als der Ergebniswertebereich ist, kann es vorkommen, dass mehrere Eingangswerte dasselbe Ergebnis erzeugen. Solche Fälle werden als Kollisionen bezeichnet. [thr] - 32 - 3. Konzept Der Hashwert zu einer Nachricht muss relativ schnell und einfach zu berechnen sein, jedoch die Umkehrung, also der Rückschluss von einem gegebenen Hashwert auf die dazugehörige Nachricht darf nur sehr schwer möglich sein. Man spricht deshalb auch von Einweg – Hashfunktionen. Das SHA-Verfahren wurde vom National Institute of Standards and Technology (NIST) in Zusammenarbeit mit der National Security Agency (NSA) 1994 als Nachfolger von MD5 entworfen. Es erzeugt Hash-Werte mit der Länge von 160 Bit. Wegen eines Designfehlers wurde 1995 der Algorithmus durch einen weiteren LinksShift erweitert und zu SHA-1 umbenannt. Beim SHA-1 werden die Eingangsdaten in 512-Bit Blöcke eingelesen, die man auf fünf 32-Bit-Wörter in den 160 Bits breiten Puffer verteilt. Es werden vier Runden pro Datum mit 20 Operationen pro Runde absolviert. [thr] Abbildung 3 - 2: Blockschaltbild MD5 (Quelle:Wikipedia) Um alle Hashwerte zu speichern, werden bei der DHT die so genannten HashTabellen gebildet. Eine Hash-Tabelle speichert zu einem beliebigen Schlüssel den dazu gehörigen Wert ab. Die Tabelle ist hierzu in zwei Spalten aufgeteilt, eine Spalte speichert den für die Adressierung der Spalte notwendigen Schlüssel ab, die andere speichert einen beliebigen dazu gehörigen Wert. Zusätzlich wird zu jedem Schlüssel der dazu gehörige Hash-Wert zur schnellen Identifizierung abgelegt (Indexierung). [thr] Hash-Tabellen implementieren immer die drei wesentlichen Funktionen: • Ablegen eines Schlüssel-Wert-Paars; eventuell bereits abgelegte Werte werden überschrieben. • Löschen einer Tabellenzeile anhand des Schlüssels. • Abfragen eines Wertes mit einem Schlüssel. - 33 - 3. Konzept Eine Suchanfrage mit einem Suchschlüssel kann sehr schnell ausgeführt werden, da mit Hilfe der Hashfunktion sich die Indexposition der richtigen Zeile ermitteln lässt. Allerdings funktioniert die Suche nur einseitig schnell. Soll anhand eines Wertes der dazugehörige Schlüssel ermittelt werden, müssen alle Werte der Tabelle bis zur Übereinstimmung auf Gleichheit mit dem Suchschlüssel überprüft werden. 3.2.2 Pastry Wenn man ein reines Peer-to-Peer Protokoll entwickelt, hat man mit vielen Designproblemen zu kämpfen. Einige der wichtigsten Designziele sind: • Dezentralisierung Etwas ältere Systeme wie Napster verwenden für die Suchanfragen spezielle Knoten. Das verspricht keine richtige Lastverteilung im Netz. Natürlich ist mit einer verteilten Suche ein hoher Kommunikationsaufwand verbunden. • Skalierbarkeit Diesen erhöhten Kommunikationsaufwand versucht das Protokoll Gnutella durch eine verteilte Suche nach dem Schneeballprinzip (der Vervielfältigung von Nachrichten) zu lösen. Dies ist eine effektive, aber keine effiziente Lösung. Es kommt zu einer starken Belastung der unter dem Netzwerk liegenden Infrastruktur, die mit der Zahl der Knoten wächst. Um dies zu verhindern, ist es notwendig, Nachrichten möglichst direkt an den zuständigen Knoten zu schicken. [pt] Idealerweise bringt man ins Netzwerk eine Struktur, die ein schnelles Auffinden des gesuchten Knotens möglich macht. Ein effizientes Routing-Protkoll soll helfen, dass der Absender nicht die Struktur des ganzen Netzes kennen muss, sondern dass er nur das nächste Ziel lokal bestimmen muss. • Selbstorganisation Gegen eine total erfasste Struktur spricht, dass das dynamische Weg- und Hinzunehmen von Knoten erschwert würde, da alle Knoten von Ausfällen in Kenntnis gesetzt werden müssten. Genau das ist es aber, was das System effizient leisten und organisieren soll, das eigene Wachsen und Schrumpfen. [pt] Pastry versucht die oben genannten Designziele zu verwirklichen. Pastry ist eine dezentralisierte key-basierte Routing-Infrastruktur für Peer-to-Peer Anwendungen. Sie ist von der Protokollfamilie der DHTs. Es bietet auf Applikationsschicht einen skalierbaren und verteilten Objekt-Lokations- und Objekt-Routing-Dienst in einem potentiell sehr großen Netzwerk. Einzelnen Knoten wird dafür eine Verarbeitungsschicht samt API angeboten, die es ermöglichen soll, eine Vielzahl von Peer-to-Peer Applikationen zu verwirklichen (z.B. verteilte Datei- oder Gruppenkommunikationssysteme). [pt] Pastry findet Einsatz in verschiedenen Applikationen, darunter Web Caching (Squirrel), archival file storage (PAST), application-level multicast (Scribe) und video/content streaming (SplitStream). Die Pastry-DHT wird in einem Gemeinschaftsprojekt von Microsoft Research, der Rice University, der Purdue - 34 - 3. Konzept University und der University of Washington gepflegt. Sie kann sowohl das 128 Bit MD5-Hash-Verfahren als auch das 160 Bit SHA-1 Verfahren anwenden. Jeder Knoten in der DHT besitzt eine NodeId, meist 128 Bit und hält 3 Tabellen zum Routing bereit - Leaf, Neighborhood set und die Routing Tabelle (sh. Abb. 3-3). Abbildung 3 - 3: Pastry Tabellen Fordert man effizientes Routing zwischen den Knoten eines vermaschten Netzes kommt man schnell auf die Idee eine binärbaumartige Struktur zu realisieren. Diese ermöglicht theoretisch logarithmischen Aufwand bei der Übermittlung von Nachrichten. Bei einer Schlüssellänge von 128 Bit würden im herkömmlichen Binärbaum aber 128 Hops notwendig, um zu einem Blatt zu gelangen, was keine gute Performanz verspricht. Pastry erhöht deshalb die Anzahl der Unterknoten auf 2^b (b standardmäßig 4), wodurch der Baum abflacht und im ungünstigsten Fall 32 Hops notwendig sind. Ein Pastry Node kennt also maximal 2^b direkte Nachbarknoten, die entsprechend den Ebenen eines Baumes ein gemeinsames Präfix besitzen. Abbildung 3 - 4: Pastry, Routingtabelle - 35 - 3. Konzept Die Abbildung 3-4 zeigt die Routingtabelle eines Pastry Nodes mit nodeID = 65a1x, b=4. Abbildung 3 - 5: Routing einer Nachricht In Abbildung 3-5 sieht man die prinzipielle Vorgehensweise beim Routen einer Nachricht zu dem Knoten mit NodeID d46a1c. Die Routing-Strategien werden jedoch von einem Overlay-Netzwerk, das auf der DHT aufbaut, verarbeitet. Die Besonderheit von Pastry besteht darin, dass ein externes Programm zur Bestimmung der Distanz (und somit auch der RoutingStrategie) zwischen den P2PClients aufgerufen werden kann, so kann z. B. eine Optimierung nach Router- Hop-Counts, Verbindungslaufzeiten, Netzwerkbandbreite oder Kombinationen verschiedener Verfahren erfolgen. Pastry bietet auch ein Application Programming Interface (API), welches eine überschaubare Anzahl an Befehlen besitzt, um die komplette Kommunikation innerhalb des Netzwerkes zu initialisieren und abzuwickeln: • nodeID = pastryInit(Credentials, Application) Der Aufruf der Funktion pastryInit startet den lokalen Knoten und registriert ihn im Netzwerk. Er liefert die lokale Knoten-ID zurück und verlangt beim Aufruf sowohl eine applikationsabhängige Struktur als auch ein Application-Handle, um Rückmeldungen weiterleiten zu können. • route(msg,key) Die Funktion route veranlasst Pastry die übergebene Nachricht an den Knoten mit der ebenfalls übergebenen Knoten ID zu übermitteln. Applikationen, die Pastry verwenden, müssen ihrerseits die folgenden Methoden exportieren: - 36 - 3. Konzept • deliver(msg, key) Die Methode deliver wird von Pastry aufgerufen, falls der lokale Knoten der numerisch naheste zu key ist. Übergeben wird dann die Nachricht msg. • forward(msg, key, nextId) Wird eine Nachricht empfangen, die eigentlich zur Weiterleitung gedacht ist, trifft sie per forward ein. Sie kann modifiziert oder durch Setzen von msg auf NULL terminiert werden. • newLeafs(leafSet) Die Methode newLeafs wird von Pastry immer dann aufgerufen. wenn sich Änderungen im lokalen Leaf-Set ergeben. Derzeit existiert sowohl eine freie Pastry Implementation der Rice University als auch ein Paket, das von Microsoft Research zur Verfügung gestellt wird. FreePastry [fp] ist eine OpenSource Implementation von Pastry und wird in Java entwickelt. Sie bietet ein objektorientiertes Framework für die Erstellung von P2P-Anwendungen. Im Folgenden werden zwei Anwendungen vorgestellt, die auf Pastry basieren und für die spätere Aufgabe eine Rolle spielen werden. 3.2.3 Scribe Scribe ist ein System, das für Kommunikations- und Nachrichtendienste für beliebig große Gruppen entwickelt wurde. Es nutzt Multicast auf Anwendungsebene, um Daten in themenbasierten Gruppen zu verteilen. Scribe ist effizient, flexibel, und unterstützt auch sehr dynamische Gruppen, mit vielen beitretenden und verlassenden Mitgliedern. Zudem skaliert es sehr gut. Dies liegt daran, dass Scribe auf Pastry aufsetzt. In Pastry bekommt jedes Mitglied eine zufällige ID zugewiesen, welche z.B. durch kryptografische Hashwert-Berechnung des Öffentlichen Schlüssels eines Mitglieds ermittelt werden kann. In Pastry können nun Daten von einem Mitglied zu jedem beliebigen anderen Mitglied versendet werden, wenn dessen ID bekannt ist. Die IDs sind jeweils kurze Sequenzen von Ziffern der Länge 2b, wobei b eine kleine Konstante, meist zwischen 2 und 6, ist. Jedes Mitglied im Pastry-Overlay hat eine Routing-Tabelle, eine Menge von Nachbarn sowie eine Menge von Blättern. Die Routing-Tabelle eines Mitglieds h enthält Informationen über eine Menge von Mitgliedern, die ein gewisses Präfix mit h gemeinsam haben. In Reihe i teilen sich die ersten i−1 Ziffern ihre ID mit Mitglied h. In der Routing-Tabelle gibt es 2b Reihen mit je 2b Einträgen. Wenn ein Mitglied keine anderen Mitglieder mit passendem Präfix bestimmter Länge kennt, so ist die entsprechende Reihe in der Routing-Tabelle leer. Die Menge der Nachbarn eines Mitglieds h enthält Mitglieder, die nach Distanzabschätzung nahe bei h liegen. Die Menge an Blättern enthält Mitglieder, deren IDs numerisch nahe bei der von h liegen. Diese Menge ist in zwei gleichgroße Hälften unterteilt, in denen jeweils nur Mitglieder liegen, deren ID kleiner bzw. größer ist, als die von h. [om] - 37 - 3. Konzept Das Scribe Multicast-Protokoll nutzt Pastry als Overlay-Netzwerk. Daher ist die Steuerungstopologie die gleiche wie in Pastry. Die Nachbarn eines Mitglieds auf der Steuerungstopologie berücksichtigen dessen Routing-Tabelle sowie seine Menge an Nachbarn und Blättern. Die Unicast-Pfade zwischen Mitgliedern werden dann durch feste Regeln definiert. Eine Nachricht mit einer Ziel-ID y wird zu einem Mitglied geleitet, dass y als ID hat. Wenn kein solches Mitglied im Overlay-Netz existiert, so wird die Nachricht an Mitglied z geleitet, dessen ID y numerisch am Nächsten ist. Dieses Routing erfolgt durch das Weiterleiten der Nachricht an ein anderes Mitglied in der Routing-Tabelle, das ein längeres Präfix mit y gemein hat als das weiterleitende Mitglied selbst. Falls kein solches Mitglied gefunden werden kann, so wird die Nachricht an eines aus der Menge der Blätter weitergeleitet, das numerisch näher an y liegt. Eine Multicast-Gruppe in Scribe besteht typischerweise aus einer Teilmenge der Mitglieder eines Pastry-Netzwerks (sh. Abb. 3-6). Jede MulticastGruppe hat ihre eigene ID, auch Themen-ID (engl. topics) genannt. Das Mitglied, dessen ID am nächsten an der Themen-ID liegt, wird RP (Rendezvous Punkt) für die Gruppe. Die Datentopologie im Scribe-Netzwerk ist dann die Vereinigung der PastryUnicast-Pfade von allen Mitgliedern der Scribe-Gruppe zum RP. Die Beschaffenheit des Datenpfades wird somit während der Beitritts-Prozedur der einzelnen Mitglieder definiert. [om] Abbildung 3 - 6: Scribe Aufbau Um einer Scribe-Gruppe beizutreten, muss ein neues Mitglied erst dem PastryNetzwerk beigetreten sein. Dessen Anmeldevorgang wird hier nicht weiter beschrieben. Wenn ein Mitglied n dann einer Scribe-Gruppe beitreten möchte, schickt es eine Anfrage über das Pastry-Netzwerk zur Themen-ID der Gruppe. Diese Anfrage wird von Pastry automatisch zum RP der Gruppe geleitet. Alle Mitglieder auf - 38 - 3. Konzept dem Unicast-Pfad von n zum RP, die noch nicht Mitglieder des MulticastDatenbaumes sind, fügen sich selbst zum Datenbaum hinzu. [om] 3.2.4 SplitStream Splitstream [ss] ist eine Weiterentwicklung von Scribe und wurde vor allem für Media Streaming entwickelt. Es geht primär darum, den Inhalt in Streifen zu schneiden, und diese dann über knotendisjunkte Bäume zu verteilen. Knotendisjunkt nennt man zwei Mengen, wenn es keine Knoten gibt, der in beiden enthalten ist. Ein gewaltiger Vorteil von SplitStream ist, dass es die Bandbreite der einzelnen Mitglieder berücksichtigt, oder die Mitglieder selbst über ihre Bandbreite entscheiden. Die meisten Multicast-Protokolle benutzen einen einzigen Baum für die Datenübertragung. Was die Last auf die einzelnen Knoten angeht, ist natürlich dies nicht optimal, da es nicht ausgeglichen ist. So müssen die Blätter eines Baumes die empfangenen Inhalte nicht an andere Mitglieder leiten. Dafür sind nur die innenliegenden Knoten des Baumes zuständig. In einem ausgeglichenen Baum steigt der prozentuale Anteil der Blätter gegenüber den Knoten. In einem binären Baum sind mehr als 50% der Knoten Blätter. In einem Baum der Stelligkeit 16 sind es schon über 90%. Es sind also nur 10% der Mitglieder des Baumes dafür zuständig, die Daten weiterzuleiten. Die eingehende Bandbreite ist für alle Knoten im Baum dieselbe. Die ausgehende Bandbreite ist jedoch für die Blätter 0, wo sie für die innenliegenden Knoten im letzten Fall das 16-fache der eingehenden Bandbreite beträgt. Selbst in einem binären Baum, der in den meisten Fällen unpraktikabel tief ist, wäre sie noch doppelt so hoch wie die eingehende Bandbreite. Gerade der Upstream ist bei aktuellen DSL-Verbindungen aber beschränkt und bei Benutzern mit Modem oder ISDN sind breitbandige Datenübertragungen sowieso nur begrenzt möglich. [op] Wie im Kapitel 2.5 beschrieben wurde, wurde SplitStream entwickelt, um diese Ungleichheit zu überwinden und alle Mitglieder einer kooperativen Umgebung gleichmäßig, mit Rücksicht auf ihre individuellen Fähigkeiten, einzubinden. Dazu wird der Inhalt in n Streifen geschnitten und über knotendisjunkte Bäume verteilt (sh. Abb. 3-7). Analog zum wahren Leben wird eine Struktur aus vielen Bäumen ein Wald genannt. Jedes Mitglied in SplitStream ist ein innenliegender Knoten in exakt einem Baum und ein Blatt in allen anderen. So erhält jedes Mitglied den gesamten Inhalt. Jeder innenliegende Knoten im Wald sollte nun den Streifen, den er selbst weiterleitet, an n andere Mitglieder leiten. Die eingehende Bandbreite ist daher gleich der ausgehenden Bandbreite für jedes Mitglied im Wald. Einzelne Knoten können jetzt ihre eingehende Bandbreite kontrollieren, indem sie nicht alle der n Streifen abonnieren. Genauso können sie ihre ausgehende Bandbreite kontrollieren, indem sie die Zahl der Söhne, die sie zulassen, verringern. Somit kann SplitStream sich den Ressourcen der einzelnen Mitglieder anpassen. [op] SplitStream bietet eine generische Infrastruktur zum Verteilen von bandbreitenintensiven Inhalten über Multicast. Eine Anwendung, die SplitStream nutzen will, muss selbst entscheiden, wie die Inhalte kodiert und in Streifen geschnitten werden. SplitStream erstellt nur den Multicast-Wald für die Streifen unter Berücksichtigung der Bandbreitenbeschränkungen der einzelnen Knoten/Mitglieder. [op] Eine Anwendung muss: - 39 - 3. Konzept • den Inhalt so kodieren und in Streifen schneiden, dass jeder Streifen ungefähr dieselbe Bandbreite benötigt. • sicherstellen, dass jeder Streifen ungefähr die gleiche Menge Informationen enthält und es keine Hierarchie unter den Streifen gibt. • einen Mechanismus bereitstellen um den zwischenzeitlichen Verlust von Streifen zu tolerieren. an Um den letzten Punkt zu erfüllen, könnte eine Anwendung explizit Algorithmen bereitstellen, um nicht empfangene Daten von anderen Mitgliedern des Netzes zu holen. Sie kann aber genauso gut Redundanzen in den einzelnen Streifen einfügen, so dass verlorene Streifen aus einer Teilmenge der anderen Streifen berechnet werden können. Dadurch braucht jeder Streifen etwas mehr Bandbreite als B/n, wobei B die Bandbreite des gesamten Inhalts ist. Dafür kann der Inhalt aus weniger als n Streifen zurück gewonnen werden. Beispielsweise könnte ein Video mit Multiple Description Coding (MDC) kodiert und die einzelnen Beschreibungen als Streifen versendet werden. Aus jeder beliebigen Teilmenge kann das Video gewonnen werden. Die Qualität des Videos steigt proportional zur Anzahl der empfangenen Streifen. Die Übertragung reisst auch nicht ab, wenn einige Mitglieder ausfallen, und ein Mitglied von einem der Bäume abgeschnitten ist. Es wird während der Reparatur das Video nur in geringfügig schlechterer Qualität wiedergegeben. Diese Art der Kodierung erlaubt es Mitgliedern mit geringerer Bandbreite auch, explizit weniger Streifen zu abonnieren, und damit das Video in geringerer Qualität abzuspielen. Bei anderen Multicast - Protokollen mit nur einem Baum ist hier ein völlig neuer Stream nötig, um verschiedene Qualitäten anzubieten. [op] Eine andere Möglichkeit ist das Multicasting von Dateien, wo jeder Datenblock mit Erasure - Codes kodiert werden kann, um n Blöcke zu generieren, bei denen nur eine Teilmenge der n Blöcke nötig ist, um den Originalblock wieder herzustellen. Dann wird jeder Streifen genutzt, um einen anderen der n Blöcke zu versenden. Mitglieder müssten alle Streifen abonnieren, und wenn ein Streifen ausfällt, werden die Daten durch die Redundanzen aus den anderen Blöcken generiert. [op] - 40 - 3. Konzept Abbildung 3 - 7: SplitStream Baum 3.3 Das OSGi-Schichtenmodell Die OSGi Framework-Spezifikation ist in mehrere logische Schichten aufgeteilt. Jede Schicht fasst zusammengehörige Aufgaben bzw. Verantwortlichkeiten zusammen (sh. Abb. 3-8). Abbildung 3 - 8: OSGi-Schichten (Quelle: osgi.org) • Module-Schicht: Die Module-Schicht definiert das Bundle als grundlegende Modularisierungseinheit innerhalb der OSGi Service Plattform. • Lifecycle-Management-Schicht: Die Schicht spezifiziert, welche Zustände ein Bundle während seines Lebenszyklus innerhalb der OSGi Service Plattform besitzen kann. • Security-Schicht: Hier werden sicherheitsrelevante Aspekte spezifiziert wie bspw. der Umgang mit signierten Bundles und die Möglichkeit zur Einschränkung der Ausführungsrechte einzelner Bundles. - 41 - 3. Konzept • Service-Schicht: Die Service-Schicht spezifiziert ein allgemeines ServiceModell, in dem Serivces systemweit über eine Service Registry verfügbar gemacht werden können. • Execution environment: Alle Java-Umgebungen wie J2SE, CDC, CLDC, MIDP, usw. 3.3.1 Module-Schicht Die Module-Schicht ist die unterste logische Schicht in der OSGi-FrameworkSpezifikation. Sie definiert das dem Framework zugrunde liegende Modulkonzept. Darin wird das Bundle als die Einheit spezifiziert, die eigenständig im Framework installiert und deinstalliert werden kann. Technisch betrachtet ist ein Bundle ein Java-Archiv (jar-Datei), das die zur Implementierung einer gewünschten Funktionalität erforderlichen Klassen und Ressourcen enthält. Zusätzlich zum eigentlichen Inhalt besitzt ein Bundle ein Bundle Manifest, in dem weitere Informationen über das Bundle, wie Namen oder die benötigten Packages, beschrieben werden. Das Bundle Manifest ist die Datei MANIFEST.MF im Verzeichnis META-INF in dem jar-Archiv. Die Informationen aus dem Bundle Manifest werden vom Framework verwendet, um das Bundle korrekt betreiben zu können [osp]. Manifest-Version: 1.0 Bundle-Activator: com.java.example.HelloActivator Bundle-Name: Example plugin Bundle-Description: Example Bundle Bundle-Vendor: SWT Bundle-Version: 0.0.1 Import-Package: org.osgi.framework Folgende Eigenschaften sind zwingend in der Manifest-Datei anzugeben: • Manifest-Version: Zwingend notwendig für ein Manifest-File ist die Version. • Bundle-Name: Name des Bundles. • Bundle-Activator: Diejenige Klasse, die das Interface BundleActivator implementiert. • Import-Package: Importiert ein Paket, das von einem anderen Bundle exportiert wurde. Hier kann auch die Version spezifiziert werden. Zwingend ist hier org.osgi.framework. - 42 - 3. Konzept Optional sind: • Bundle-SymbolicName: Eindeutiger Name des Bundles, wird verwendet für Bundle-Repositories. • Export-Package: Gibt an, welche Pakete von diesem Bundle exportiert werden. Andere Bundles können diese verwenden, indem sie sie importieren. • Bundle-Description: Beschreibung des Bundles. • Bundle-Version: Version des Bundles. Bildet mit dem SymbolicName eine eindeutige ID. • Bundle-Classpath: Verwendet in das Bundle eingebettete JARs, so kann dieses Property dazu verwendet werden, um den Classpath des Bundles mit diesen zu erweitern. • Bundle-Source: Pfad zum Source-JAR des Bundles, falls vorhanden. • Bundle-DocURL: Pfad zur Dokumentation. • Bundle-Vendor: Hersteller. • Bundle-Copyright: Copyright-Angaben. Eine besondere Eigenschaft der OSGi Service Platform ist, dass die Abhängigkeiten zwischen Bundles explizit verwaltet werden. So sind die Packages eines Bundles zunächst für andere Bundles nicht sichtbar. Um sie in anderen Bundles nutzen zu können, müssen sie erst durch das implementierende Bundle exportiert werden. Um ein Package in einem anderen Bundle nutzen zu können, muss dieses Bundle durch das entsprechende Package dann explizit importiert werden. Die beiden Operationen werden durch die entsprechenden Headers innerhalb der Manifest-Dateien erledigt. 3.3.2 Die Lifecycle-Management-Schicht In der Lifecycle-Management-Schicht werden die dynamischen Aspekte von Bundles festgelegt. Insbesondere werden die Zustände definiert, in denen sich ein Bundle während seines Lebenszyklus innerhalb des OSGi Frameworks befinden kann. Jedes Bundle kann installiert, gestartet, gestoppt und wieder deinstalliert werden (sh. Abb. 3-9). Zusätzlich kann es während der Laufzeit aktualisiert (update) werden. Diese Zustände werden manuell durch entsprechende Kommandos gestartet. Es existieren auch verschiedene Management Agents, die die Manipulation von den Zuständen steuern. Wie ein Management Agent jedoch den Zugriff auf das Framework ermöglicht, ist in der OSGi Service Platform Specification nicht festgelegt. - 43 - 3. Konzept Abbildung 3 - 9: Zustandsdiagramm eines Bundles (Quelle: wimpi.coalevo.net) 3.3.3 Die Service-Schicht Die Service-Schicht legt fest, wie innerhalb des Frameworks Objekte in Form von OSGi Services verwendet werden. Ein OSGi Service ist ein einfaches Java-Objekt, das an einer zentralen Stelle (Server Registry) unter einem Interfacenamen angemeldet wird. Dort kann es von anderen Bundles, die den Service nutzen möchten, über den bei der Anmeldung angegebenen Namen abgefragt und verwendet werden. Für gewöhnlich wird ein OSGi Service unter dem Namen des Interfaces registriert, das die öffentlichen Methoden des Service definiert. Dieses Interface wird auch als Service Interface bezeichnet. Services können innerhalb des OSGi Frameworks jederzeit an der Service Registry registriert und wieder abgemeldet werden. Das bedeutet, dass Nutzer dieser Services immer damit rechnen müssen, dass ein Service nicht mehr zur Verfügung steht. Um das Arbeiten mit dynamischen Services zu ermöglichen bzw. zu vereinfachen, stehen mit Service Listenern, dem Service Tracker und den Declarative Services Möglichkeiten zur Verfügung, die ein Bundle nutzen kann, um auf Veränderungen der Services zu reagieren.[osp] 3.3.4 Die Security-Schicht Die Security-Schicht ist ein Bestandteil des Security-Konzeptes der OSGi Services Plattform, der es ermöglicht, die Ausführungsrechte einzelner Bundles gezielt einzuschränken. Sie basiert auf dem Java-Sicherheitsmodell, das erweitert wurde, um es an die speziellen Anforderungen von OSGi anzupassen. Dadurch können die Ausführungsrechte für jedes Bundle individuell eingestellt werden. - 44 - 3. Konzept 3.3.5 Die Framework Services Die OSGi Framework Specification definiert fünf Framework Services, die die Implementierung von Management Agents unterstützen. Die ersten beiden Services implementieren OSGi-spezifische Dienste, die grundsätzlich in Management Agents benötigt werden und in der zugrunde liegenden JAVA-Umgebung nicht vorhanden sind: • Package Admin Service: Der Package Admin Service spezifiziert einen Dienst, über den Management Agents die Package-Abhängigkeiten zwischen installierten Bundles abfragen können. • Start Level Service: Über den Start Level Service kann ein Management Agent die relative Start- bzw. Stopp-Reihenfolge von Bundles innerhalb des OSGi Frameworks abfragen. Der Start-Level kann für jedes installierte Bundle sowie für das Framework selber individuell eingestellt werden. Folgende drei Services stellen Anpassungen bzw. Erweiterungen von bereits in der Java-Laufzeitumgebung verfügbare Dienste dar: • Conditional Permission Admin Service: Dieser Service beschreibt die OSGiErweiterungen des Java-Security-Frameworks. Es definiert eine API zum Anlegen und Verwalten von Berechtigungen auf Bundle-Ebene und stellt sicher, dass Änderungen der Berechtigungen wirksam werden, ohne dass das Bundle oder die JAVA-VM neu gestartet werden muss. • Permission Admin Service: Der Permission Admin Service dient ebenfalls der Verwaltung von Berechtigungen, gilt aber als veraltet. • URL Handler Service: Der URL Handler Service ermöglicht es Bundles, eigene URL Handler für die Klasse java.net.URL im System anzumelden, so dass einmal installierte URL Handler wieder entfernt werden können. - 45 - 4. Realisierung 4. Realisierung Nachdem die theoretischen Aspekte von SplitStream eingeführt wurden, wird in diesem Kapitel anhand eines Java-Beispiels gezeigt, wie eine entsprechende Anwendung entsteht. Das Beispiel beschreibt, wie man einen SplitStream Client erzeugt, ihn an ein schon bestehendes Pastry-Netz bindet und wie man die Daten in Stripes aufsplitten kann. Das so enstandene Programm wird als ein OSGi Plugin in das Projekt eingebunden. Die Entwicklung eines Plugins wird auch als weiteres Beispiel in diesem Kapitel geschildert. Die Einbindung des Bundles erfolgt in das Oscar Framework, was eine freie Implementierung des OSGi-Standards ist. Da die entwickelte Applikation als eine Erweiterung des SIP Communicators funktionieren soll, wird hier auch gezeigt, wie man in den Client eigene Module einbinden kann. Zuletzt werden die konkreten Implementierungen beschrieben. Dies sind der Peer-toPeer Live Video Streaming Proxy und dessen Einbindung in den SIP Communicator. 4.1 Erstellen eines SplitStream Clients Wie im vorigen Kapitel erläutert wurde, nutzt SplitStream separate Scribe-Multicast Bäume (sh. Abb. 4-1), um die n Streifen zu verteilen. Dazu nutzt es die Eigenschaften von Pastry aus, um knotendisjunkte Bäume zu erstellen (sh.Abb. 4-2). Da Pastry die Pakete immer an Mitglieder weiterleitet, die ein längeres Präfix mit der Ziel-ID gemein haben, werden die Pakete meist im ersten Schritt schon an ein Mitglied geleitet, dessen erstes Zeichen mit der Themen-ID übereinstimmt. Da jeder Scribe-Baum durch die Routen aller Mitglieder zur Themen-ID erstellt wird, kann davon ausgegangen werden, dass sämtliche innere Knoten eines Baumes mindestens das erste Zeichen mit der Themen-ID gemein haben. Es kann also angenommen werden, dass die inneren Knoten aller Scribe-Bäume disjunkt sind, wenn für die verschiedenen Streifen einfach Themen-IDs gewählt werden, die sich an der ersten und damit wichtigsten Stelle unterscheiden. Der dadurch entstehende Wald aus Scribe-Bäumen ist disjunkt, was die inneren Knoten angeht. [op] Abbildung 4 - 1: SplitStream, einzelne Stripes - 46 - 4. Realisierung Abbildung 4 - 2: SplitStream mit zwei Stripes SplitStream ist Teil von FreePastry ab Version 1.3.1. Dieses ist OpenSource unter einer BSD-ähnlichen Lizenz und zu bekommen unter http://freepastry.rice.edu. FreePastry basiert auf Java und ist daher plattformübergreifend einsetzbar. [op] Im Folgenden wird anhand eines Programmierbeispiels der Einsatz von SplitStrem erläutert. Man benötigt die FreePastry Bibliothek (FreePastry-x.x.jar), um die entsprechenden Klassen zu benutzen. Die wichtigen Punkte hier sind, wie man • Einen SplitStreamClient erzeugt. Der Client wird benötigt, um überhaupt Daten von SplitStream zu bekommen. • Einen Channel erzeugt oder sich dort anhängt. • Sich abonniert (Subscribe), um Daten von einem SplitStream Channel zu bekommen. • Daten über die einzelnen Stripes verschickt (Publish). • Daten empfängt (Receive). Das Beispiel besteht aus zwei Dateien – MySplitStreamClient.java und SplitStreamTutorial.java. In der Ersten wird die Funktionalität des SplitStream Clients implementiert und in der Zweiten wird eine kleine Testumgebung entwickelt, um eine Kommunikation zwischen mehreren Knoten zu simulieren. Das SplitStream – Interface besitzt zwei Methoden. joinFailed() wird aufgerufen, wenn ein Fehler beim Stripe-Abonnieren auftritt. In diesem Fall sollte man erneut - 47 - 4. Realisierung versuchen, sich zu abonnieren. Die Zweite Methode deliver() wird aufgerufen, wenn Daten über den Stripe s ankommen (sh. Listing 4-1). public interface SplitStreamClient { /* This is a call back into the application to notify it that one of the stripes was unable to to find a parent, and thus unable to recieve data. @param s The stripe which the join failed on */ public void joinFailed(Stripe s); /* Is called when data is received on a stripe which this client has registered interest @param data The data that was received @param s The stripe the data as received on */ public void deliver(Stripe s, byte[] data); Listing 4 -1: SplitStream Interface In der MySplitStreamClient Klasse werden einige Variablen deklariert, die hier aufgelistet werden. DATA_LENGTH gibt die Größe in Bytes der zu versendenden Daten an. NUM_PUBLISHES steht für die Anzahl der Wiederholungen beim Senden. Die ChannelId Klasse entspricht dem Topic (das Thema), unter welchem ein Channel entsteht. mySplitStream stellt die Implementierung von SplitStream dar. myStripes beinhaltet alle Stripes eines Channels. Die RandomSource Klasse wird benötigt, um zufällige Daten als Payload zu erzeugen. Die Daten, die versendet werden, werden als erstes in ein Array gepackt, wobei die ersten zwei Bytes für die Sequenznummer und die Stripe-ID reserviert werden. Die restlichen 8 Bytes werden mit Random Data gefüllt. Es werden 10 Nachrichten über alle 16 Stripes verschickt, was eine Gesamtmenge von 160 Nachrichten entspricht (sh. Listing 4-2). - 48 - 4. Realisierung // The lenght of a message in bytes. public static final int DATA_LENGTH = 10; // The number of messages to publish. public static final int NUM_PUBLISHES = 10; byte seqNum = 0; // My handle to a SplitStream impl. SplitStream mySplitStream; // The Id of the only Channel we are subscribing to. ChannelId myChannelId; // The channel. Channel myChannel; // The stripes... acquired from myChannel. Stripe[] myStripes; // Data source... protected RandomSource random; Listing 4 – 2: Eigenschaften der MySplitStreamClient Klasse Jetzt müssen die Variablen initialisiert werden. Dies geschieht in dem Konstruktor MySplitStreamClient() (sh. Listing 4-3). - 49 - 4. Realisierung public class MySplitStreamClient implements SplitStreamClient, Application { public MySplitStreamClient(Node node) { this.endpoint = node.buildEndpoint(this, "myinstance"); // use this to generate data this.random = endpoint.getEnvironment() .getRandomSource(); // construct Scribe mySplitStream = new SplitStreamImpl(node,"splitStreamTutorial"); // The ChannelId is built from a normal PastryId Id temp = new PastryIdFactory(node.getEnvironment()) .buildId("my channel"); // construct the ChannelId myChannelId = new ChannelId(temp); // now we can receive messages endpoint.register(); } Listing 4 – 3: Initialisieren der Variablen Zuerst wird ein Endpoint mit Hilfe von Node erstellt. Dies erlaubt, dass man mehr als nur eine Implementierung gleichzeitig an einem Node starten kann. Sie können untereinander kommunizieren, nur wenn sie den gleichen instance-String haben. Im nächsten Schritt wird Random Data erzeugt und anschließend die SplitStream Implementierung. Dann wird die ChannelId initialisiert mit dem Hashwert des „my channel“ – String. Mit Hilfe der register() - Funktion wird der Endpunkt registriert und ist bereit, Daten zu empfangen. Die subscribe() Methode wird benötigt, damit sich der Node an einem Channel (myChannelId) anhängt (sh. Listing 4-4). So wird der Knoten einen internen Teilnehmer des Kanals und ein innenliegender Knoten eines Baums. Allein das reicht aber nicht aus. Man muss sich explizit für jeden Stripe abonnieren, um die Daten zu empfangen, die man benötigt. - 50 - 4. Realisierung public void subscribe() { /* attaching makes you part of the Channel, and volunteers to be an internal node of one of the trees */ myChannel = mySplitStream.attachChannel(myChannelId); /* subscribing notifies your application when data comes through the tree */ myStripes = myChannel.getStripes(); for (int curStripe = 0; curStripe < myStripes.length; curStripe++) { myStripes[curStripe].subscribe(this); } } Listing 4 – 4: Anhängen an einem Channel Die ID-Nummer jedes Stripes ist identisch mit der ID des Kanals. Unterschiedlich ist nur die erste Stelle der Nummer. Sie entspricht der Nummer des Stripes. Zum Beispiel, wenn die ChannelID <0x26F8A8..> ist, werden die Stripes 0 bis 15 die Nummern <0x06F8A8>, <0x16F8A8> bis <0xF6F8A8> haben. In einer Anwendung werden oft aber nicht alle Stripes benötigt. Die Schwierigkeit besteht darin, dass man ein deterministisches Verfahren entwickelt, mit dessen Hilfe jeder Knoten die gleiche Menge an Daten bekommt. Dies bedeutet, dass man sich am ersten Kanal für die Stripes 0 bis 3 abonniert, beim Zweiten 4 bis 7, usw. So ist der Knoten ein Blatt (Leaf) der anderen Bäume und selber ein innerlicher Knoten von myChannel, welcher Daten weiterleitet. In der Praxis werden häufig Verfahren vorgeschlagen, die auf Hashwerten basieren. Wenn man selbst Daten über Multicast publizieren möchte, dann wird das in der publish() Methode gemacht (sh. Listing 4-5). Die Daten werden in die oben erläuterte Struktur gepackt – Bit 1 für die Sequenznummer und Bit 2 für die Stripe-ID, über welche die Daten verschickt werden. Die restlichen 8 Bits sind für die Random Data reserviert. Das Absenden geschieht einfach, indem man über den Stripe die publish(data) Methode aufruft. Das gleiche macht man für alle 16 Stripes mit Hilfe der for - Schleife. - 51 - 4. Realisierung public void publish() { for (byte curStripe = 0; curStripe < myStripes.length; curStripe++) { // format of the data: // first byte: seqNum // second byte: stripe // rest: random byte[] data = new byte[DATA_LENGTH]; // yes, we waste some random bytes here random.nextBytes(data); data[0] = seqNum; data[1] = curStripe; // print what we are sending System.out.println("Node "+endpoint.getLocalNodeHandle()+" publishing "+seqNum+" "+printData(data)); // publish the data myStripes[curStripe].publish(data); } // increment the sequence number seqNum++; // cancel after sending all the messages if (seqNum >= NUM_PUBLISHES) publishTask.cancel(); } Listing 4 – 5: Publizieren von Daten Es wurde am Anfang gesagt, dass die Daten NUM_PUBLISHES Male versendet werden. Damit dies aber in Zeitabständen passiert, wird ein Timer verwendet. Durch scheduleMessage() wird gesagt, dass eine PublishContent Nachricht alle 5 Sekunden intern versendet wird (sh. Listing 4-6). Wenn sie ankommt, wird die deliver() Methode aufgerufen, welche von ihrer Seite publish() aufruft, um die Daten über die Stripes zu versenden. - 52 - 4. Realisierung public void startPublishTask() { publishTask = endpoint.scheduleMessage(new PublishContent(), 5000,5000); } public void deliver(Id id, Message message) { if (message instanceof PublishContent) { publish(); } } class PublishContent implements Message { public int getPriority() { return Message.MEDIUM_PRIORITY; } } Listing 4 – 6: Festlegen der Zeitabstände zum Versenden Das Ankommen von Daten über die Stripes wird von der deliver() Methode abgefangen (sh. Listing 4-7). Sie zeigt den Inhalt mit Hilfe der printData() Methode an, welche die Daten aus einem Byte-Array nach Integer umwandelt. /** * Called whenever we receive a published message. */ public void deliver(Stripe s, byte[] data) { System.out.println(endpoint.getId()+" deliver("+s+"):seq:"+data[0]+" stripe:"+data[1]+" "+printData(data)+")"); } private String printData(byte[] data) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < data.length-1; i++) { sb.append((int)data[i]); sb.append(','); } sb.append((int)data[data.length-1]); return sb.toString(); } Listing 4 – 7: Abfangen der angekommenen Daten Damit ist die Arbeit für den SplitStreamClient erledigt. Jetzt es bleibt noch die angekündigte Umgebung, um mehrere Knoten an dem gleichen Host zu erzeugen. Dies wird in der zweiten Datei - SplitStreamTutorial.java gemacht. - 53 - 4. Realisierung public class SplitStreamTutorial { // this will keep track of our Scribe applications Vector apps = new Vector(); public SplitStreamTutorial(int bindport, InetSocketAddress bootaddress, int numNodes, Environment env, boolean useDirect) throws Exception { // Generate the NodeIds Randomly NodeIdFactory nidFactory = new RandomNodeIdFactory(env); // construct the PastryNodeFactory PastryNodeFactory factory; if (useDirect) { NetworkSimulator sim = new EuclideanNetwork(env); factory = new DirectPastryNodeFactory(nidFactory, sim, env); } else { factory = new SocketPastryNodeFactory(nidFactory, bindport,env); } Listing 4 – 8: Vorbereiten der Versuchsumgebung Am Anfang wird ein Vector vorbereitet, in welchem die neu erzeugten Nodes abgelegt werden. In dem SplitStreamTutorial – Konstruktor wird die Pastry NodeFactory initialisiert, welche für das Erzeugen der Nodes zuständig ist (sh. Listing 4-8). Anschließend werden die Nodes in einer for-Schleife erzeugt und initialisiert. Da ein Boot-Prozess etwas Zeit benötigt, wird eine halbe Sekunde gewartet, um die nötigen Nachrichten auszutauschen. Ist der Node gestartet, erzeugt man ein Objekt von unserer MySpliStreamClient Klasse, welche in dem oben beschriebenen Vektor hinzugefügt wird (sh. Listing 4-9). for (int curNode = 0; curNode < numNodes; curNode++) { /* construct a node, passing the null boothandle on the first loop will cause the node to start its own ring */ PastryNode node = factory.newNode(bootHandle); if (bootHandle == null) { if (useDirect) { bootHandle = node.getLocalHandle(); } else { /* This will return null if we there is no node at that Location */ bootHandle =((SocketPastryNodeFactory)factory). getNodeHandle(bootaddress); } } - 54 - 4. Realisierung /* the node may require sending several messages to fully boot into the ring */ synchronized(node) { while(!node.isReady() && !node.joinFailed()) { // delay so we don't busy-wait node.wait(500); // abort if can't join if (node.joinFailed()) { throw new IOException("Could not join the FreePastry ring. Reason:" +node.joinFailedReason()); } } } MySplitStreamClient app = new MySplitStreamClient(node); apps.add(app); } Listing 4 – 9: Erzeugen der Nodes Nachdem der Vektor gefüllt ist, wird in ihm iteriert und von jedem SplitStream Client die subscribe() Methode aufgerufen, welche das Stripes-Abonieren durchführt (sh. Listing 4-10). Der erste Client in der Liste ist auch derjenige, der das Multicast Versenden von Nachrichten erledigt. // for the first app subscribe then start the // publishtask Iterator i = apps.iterator(); MySplitStreamClient app = (MySplitStreamClient)i.next(); app.subscribe(); app.startPublishTask(); // for all the rest just subscribe while (i.hasNext()) { app = (MySplitStreamClient) i.next(); app.subscribe(); } // now, print the tree env.getTimeSource().sleep(5000); Listing 4 – 10: Starten der Nodes Startet man die Applikation, so sieht man am Anfang das Erzeugen von den 10 Nodes. Wie schon gesagt wurde, ist der erste Node derjenige, der die Nachrichten versenden wird. In diesem Beispiel ist es <0x7A49D8..> . Der Node versendet die Random - Daten über alle 16 Stripes, hier fett gedruckt. Gleich danach sieht man, dass die anderen Nodes beginnen, die Nachrichten zu bekommen. Zum Beispiel, jeder der für Stripe 0 abonniert ist, bekommt die Daten, die an diesem Stripe - 55 - 4. Realisierung versendet wurden. Hier ist nur ein Ausschnitt gezeigt, da die Gesamtzahl der Nachrichten 160 beträgt (sh. Listing 4-11). Finished Finished Finished Finished Finished Finished Finished Finished Finished Finished creating creating creating creating creating creating creating creating creating creating new new new new new new new new new new node node node node node node node node node node TLPastryNode[DNH TLPastryNode[DNH TLPastryNode[DNH TLPastryNode[DNH TLPastryNode[DNH TLPastryNode[DNH TLPastryNode[DNH TLPastryNode[DNH TLPastryNode[DNH TLPastryNode[DNH Node [DNH <0x7A49D8..>] publishing 62,66,31,-50,37,-21,44,97 Node [DNH <0x7A49D8..>] publishing 49,-69,82,15,-113,-84,-7,4 Node [DNH <0x7A49D8..>] publishing 104,-15,38,98,68,-39,-29,61 Node [DNH <0x7A49D8..>] publishing -48,-5,30,-52,-47,-31,-86,67 <0x7A49D8..>] <0x1526D4..>] <0xE109BA..>] <0x710D8A..>] <0xAB9A4C..>] <0x78E6F2..>] <0x6FCB89..>] <0x199612..>] <0xA3160F..>] <0xA05351..>] 0 0,0, 0 0,1, 0 0,2, 0 0,3, ……… Node [DNH <0x7A49D8..>] publishing 0 0,15, 5,13,117,93,-77,81,-12,33 <0x1526D4..> deliver(Stripe [StripeId <0x06F8A8..>]): seq:0 stripe:0 0,0,62,66,31,-50,37,-21,44,97) <0x1526D4..> deliver(Stripe [StripeId <0x16F8A8..>]): seq:0 stripe:1 0,1,49,-69,82,15,-113,-84,-7,4) <0x1526D4..> deliver(Stripe [StripeId <0x86F8A8..>]): seq:0 stripe:8 0,8,46,20,24,43,26,-32,-80,67) <0x710D8A..> deliver(Stripe [StripeId <0x86F8A8..>]): seq:0 stripe:8 0,8,46,20,24,43,26,-32,-80,67) <0x6FCB89..> deliver(Stripe [StripeId <0x46F8A8..>]): seq:0 stripe:4 0,4,-104,53,125,-23,-44,-8,88,-108) <0x6FCB89..> deliver(Stripe [StripeId <0x56F8A8..>]): seq:0 stripe:5 0,5,47,-54,117,80,-10,-90,-116,45) Listing 4 – 11: Programmausgabe Dieses Beispiel wurde von der offiziellen Seite von freepastry.org entnommen. Detaillierte Informationen und andere Programmierbeispiele kann man auch von dort erhalten. - 56 - 4. Realisierung 4.2 Entwicklung eines OSGi Pugins In diesem Kapitel werden anhand einiger Programmierbeispiele die grundlegenden Funktionalitäten des OSGi Frameworks vorgestellt. Als erstes wird ein „Hello World“ – Bundle erzeugt, welches ein einziges Package mit genau einer Klasse Bundle-Aktivator enthält. Der Bundle-Aktivator implementiert die Ausgabe „Hello World“, die beim Bundle-Start erfolgt. Zusätzlich wird beim Stoppen vom Bundle die „Goodbye World“- Nachricht ausgegeben. package com.sample.helloworld; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; public class Activator implements BundleActivator { public void start(BundleContext context) throws Exception { System.out.println("Hello world"); } public void stop(BundleContext context) throws Exception { System.out.println("Goodbye World"); } } Listing 4 – 12: Die Activator – Klasse Die Activator-Klasse besitzt zwei Methoden start() und stop(). Die start()-Methode wird aufgerufen, wenn das Bundle vom OSGi Framework aufgerufen wird. Analog dazu wird die stop()-Methode beim Stoppen aufgerufen (sh. Listing 4-12). Die entsprechende Manifest-Datei sieht wie folgt aus: Manifest-Version: 1.0 Bundle-Activator: com.sample.helloworld.Activator Bundle-Name: Example plugin Bundle-Description: Example Bundle Bundle-Vendor: SWT Bundle-Version: 0.0.1 Import-Package: org.osgi.framework Listing 4 – 13: Manifest-Datei Als nächstes wird die Quelltextdatei kompiliert, vorausgesetzt, sie liegt im Verzeichnis c:\classes. Anschließend werden die kompilierte Datei und die ManifestDatei in ein Archiv gepackt (sh. Listing 4-14). - 57 - 4. Realisierung > javac -d c:\classes *.java > jar cfm example1.jar manifest.mf -C c:\classes tutorial\example1 Listing 4 – 14: Kompilieren eines Bundles Um das neue Bundle bekannt zu machen, muss es zuerst im OSGi Framework installiert werden. Danach ist es bereit, gestartet zu werden. Der Screenshot in Abbildung 4-3 zeigt an, dass das Bundle unter die Nummer 13 installiert wurde. Mit dem Kommando start/stop kann das Bundle gesteuert werden. Abbildung 4 - 3: Oscar GUI Wie bereits erwähnt, ist OSGi eine Service Oriented Architecture (SOA). Dies bedeutet, dass bei größeren Projekten eine große Zahl von Bundles vorhanden sein wird. Sie werden auch unter sich Daten austauschen müssen. Dafür werden die Services gebraucht (sh. Abb. 4-4). Abbildung 4 - 4: OSGi Services (Quelle:[sm]) - 58 - 4. Realisierung Ein Service ist ein Dienst, der von einem Bundle für andere Bundles zur Verfügung gestellt wird. Dies ermöglicht eine Kommunikation zwischen den einzelnen Bundles. Im folgenden Beispiel wird das anschaulicher gemacht, indem wir das vorherige „Hello World“ – Bundle in zwei neue Bundles aufsplitten. Zuerst definieren wir den Service. Dies geschieht mittels eines Java-Interfaces in der Datei HelloService.java. Dieses Interface definiert die Schnittstelle nach Außen und ist als Service Description bekannt (sh. Listing 4-15). public interface HelloService { public String sayHello(); } Listing 4 – 15: Definition vom HelloService-Interface Die Implementierung des Interfaces geschieht in HalloServiceImpl.java. public class HelloServiceImpl implements HelloService{ public String sayHello() { return "Say Hello"; } } Listing 4 – 16: Implementierung des HelloService-Interfaces Die einzige Methode sayHello() wird überschrieben und liefert einen String zurück. Wie bereits erwähnt, braucht jedes Bundle einen Activator – HelloServiceActivator.java. Beim Starten des Bundles wird ein Objekt des Typs HelloServiceImpl erzeugt, auf welches die Interface-Variable helloService verweist. Anschließend wird der Service beim Service Registry registriert, indem als Name der Klassenname übergeben wird und die Interface-Variable als Schnittstelle für die von Außen aufrufenden Bundles (sh. Listing 4-17). Die stop()-Methode steht dafür, dass beim Stoppen von dem Bundle auch der Service von der Service Registry abgemeldet wird. - 59 - 4. Realisierung public class HelloServiceActivator implements BundleActivator { ServiceRegistration helloServiceRegistration; public void start(BundleContext context) throws Exception { HelloService helloService = new HelloServiceImpl(); helloServiceRegistration = context. registerService(HelloService.class.getName(), helloService, null); } public void stop(BundleContext context) throws Exception { helloServiceRegistration.unregister(); } } Listing 4 – 17: Definieren von star/stop – Methoden Von der Seite des Service Providers ist schon alles erledigt, bis auf die ManifestDatei. Wichtig ist das Export-Package-Feld, welches angibt, dass das Package exportiert wird (sh. Listing 4-18). Dadurch können andere Bundles es importieren und Daten mit ihm austauschen. Manifest-Version: 1.0 Bundle-Activator: com.sample.HelloServiceActivator Bundle-ManifestVersion: 2 Bundle-Name: HelloService Plug-in Bundle-SymbolicName: com.sample.HelloService Bundle-Version: 1.0.0 Bundle-Vendor: SWT Bundle-Localization: plugin Export-Package: com.sample.service Import-Package: org.osgi.framework Listing 4 – 18: Setzen des Export-Package-Feldes in einer Manifest-Datei Nachdem die Quelltextdateien kompiliert sind und das Package erzeugt ist, kann ein Verbraucher (Service Requester) das Bundle in seiner Manifest-Datei importieren. Damit kann er später die exportierten Services abfragen. Man sieht im ImportPackage-Feld den Namen des Packages, welches vorher exportiert wurde (sh. Listing 4-19). Auf diese Art und Weise kann man bei größeren Projekten vorgehen und damit bleibt alles modular und übersichtlich. - 60 - 4. Realisierung Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: HelloWorld Plug-in Bundle-SymbolicName: com.sample.HelloWorld Bundle-Version: 1.0.0 Bundle-Activator: com.sample.helloworld.Activator Bundle-Vendor: SWT Bundle-Localization: plugin Import-Package: com.sample.service, org.osgi.framework Listing 4 – 19: Setzen des Import-Package-Feldes in einer Manifest-Datei Da der Consumer wiederum ein Bundle ist, braucht er auch einen Activator. In der Datei HelloWorldActivator.java findet man die Implementierung des Verbrauchers (sh. Listing 4-20). Wichtig ist hier das Abrufen des Services. In der start()-Methode wird eine Service-Referenz implementiert, welche benötigt wird, um auf den HelloService zuzugreifen. Über die Methoden getServiceReference() und getService() wird das OSGi Framework aufgefordert, die richtige Instanz zu finden und einen Verweis auf sie mittels des Interfaces (HelloService) zurückzuliefern. Danach kann die Methode sayHello() aufgerufen werden, welche den „Hello World“-String liefert. Wird das Bundle gestoppt, so wird in der stop()-Methode auch der geholte Service wieder freigegeben. public class Activator implements BundleActivator { ServiceReference helloServiceReference; public void start(BundleContext context)throws Exception { helloServiceReference= context. getServiceReference(HelloService.class.getName()); HelloService helloService =(HelloService)context. getService(helloServiceReference); System.out.println(helloService.sayHello()); } public void stop(BundleContext context) throws Exception { context.ungetService(helloServiceReference); } } Listing 4 – 20: Implementierung des Consumer-Bundles Zusammenfassend kann man sagen, dass sich die Verwendung von OSGi als einfach gestaltet, da die eigentliche Applikation sehr wenig OSGi-spezifischen Code beinhaltet. Man benötigt nur einen Activator, um das Boot-Verhalten zu kontrollieren. Aufpassen muss man, wenn man externe Bundles verwendet, da sie jederzeit verschwinden können. Dafür wird ein ServiceListener empfohlen, der auf solche - 61 - 4. Realisierung Ereignisse reagiert. Als Ganzes ist aber OSGi eine hervorragende Plattform, um Service-orientierte Anwendungen zu entwickeln. 4.3 Erstellen von eigenen Modulen für den SIP Communicator Der SIP Communicator (SC) ist ein multiprotokollfähiger, frei verfügbarer Instant Messenger mit dem außerdem Audio- und Videotelefonie möglich ist. Da er komplett in Java implementiert ist, gehört er zu den wenigen multiprotokollfähigen Instant Messengern, die auf allen Plattformen laufen. Der Sip Communicator setzt auf das OSGi Framework, welches das Programmieren neuer Erweiterungen (plugins) sehr vereinfacht. Der SIP Communicator unterstützt alle gängigen Protokolle bekannter Instant Messenger, darunter zählen SIP, Jabber, AIM/ICQ, MSN, Yahoo! Messenger, Bonjour, IRC, RSS. Da das Projekt ein Open Source Projekt ist, eignet es sich besonders gut zum Weiterentwickeln und um wichtige Dinge daraus zu lernen. Den Entwicklern steht eine Plattform zur Verfügung, welche das Einbinden von eigenen Plugins ins Projekt vereinfacht. Im folgenden Kapitel wird gezeigt, wie man sein eigenes Plugin entwickelt und zum Bestandteil des Projekts macht. Um ein Plugin zu schreiben, benötigt man als erstes den Quelltext des Projekts. Diesen kann man sich entweder von der Projektseite holen, oder aus der SVN Repository herunterladen. Um das zweite zu machen, muss man ein Mitglied des Projekts werden, was aber nur eine kleine Anmeldung benötigt. Im Java-Umfeld stehen zwei kostenlose und ausgereifte IDEs zur Verfügung - Eclipse und NetBeans. Hier wurde mit Eclipse gearbeitet, was aber nicht heißt, dass es mit NetBeans viel anders ist. Damit man mit Eclipse das Subversion Control System benutzen kann, benötigt man den Subclipse Modul, welcher einen SVN Client darstellt. Wie man das Modul installiert, kann man von der offiziellen Sip Communicator Seite entnehmen. Nachdem die Quelldateien in der Eclipse IDE eingebunden sind, kann man mit dem Erstellen des Plugins beginnen. Die Klassenhierarchie besitzt eine feste Struktur, wobei für die Plugins das src.net.java.sip.communicator.plugin Directory vorgesehen ist. Dort kann man auch sein eigenes Package implementieren. Da wir hier mit dem OSGi Framework arbeiten, meint man mit Package ein OSGi Bundle. Wie jedes Bundle benötigt unser Plugin einen Activator, welcher MyPluginActivator.java genannt wird. Für das Package wird auch die entsprechende Manifest-Datei benötigt, hier myplugin.manifest.mf, welche den Bundle-Activator, die externen Bibliotheken und andere Bundlebeschreibungen angibt (sh. Listing 4-21). - 62 - 4. Realisierung Bundle-Activator: net.java.sip.communicator.plugin.exampleplugin.MyPluginActiv ator Bundle-Name: Example plugin Bundle-Description: An example showing how to make plugins Bundle-Vendor: sip-communicator.org Bundle-Version: 0.0.1 Import-Package: org.osgi.framework, net.java.sip.communicator.util, net.java.sip.communicator.service.contactlist, net.java.sip.communicator.service.contactlist.event, net.java.sip.communicator.service.gui, net.java.sip.communicator.service.gui.event, net.java.sip.communicator.service.protocol, javax.swing, javax.swing.event, javax.swing.table, javax.swing.text, javax.swing.text.html, javax.accessibility, javax.swing.plaf, javax.swing.plaf.metal, javax.swing.plaf.basic, javax.imageio, javax.swing.filechooser, javax.swing.tree, javax.swing.undo, javax.swing.border Listing 4 – 21: Manifest-Datei eines Plugins im Sip Communicator Der BundleActivator hat die Aufgabe, die Plugin-Komponente im OSGi Bundle Context zu registrieren. Die Klasse ExamplePluginMenuItem ist die eigentliche Implementation der Funktionalität des Bundles. Sie wird weiter nicht betrachtet, da es nicht so interessant ist. Wichtiger dagegen sind die Schnittstelles zum Sip Communicator und der Zeitpunkt des Aufrufs. Mittels des containerFilter wird festgelegt, dass beim Betätigen der rechten Maustaste über einen Chat-Kontakt der Pluginname als Menüeintrag erscheint. Über bc.registerService() wird die Komponente registriert (sh. Listing 4-22). - 63 - 4. Realisierung package net.java.sip.communicator.plugin.exampleplugin; import java.util.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.util.*; import org.osgi.framework.*; public class MyPluginActivator implements BundleActivator { public void start(BundleContext bc) throws Exception { ExamplePluginMenuItem examplePlugin = new ExamplePluginMenuItem(); Hashtable<String, String> containerFilter = new Hashtable<String, String>(); containerFilter.put( Container.CONTAINER_ID, Container. CONTAINER_CONTACT_RIGHT_BUTTON_MENU.getID()); bc.registerService(PluginComponent.class.getName(), examplePlugin,containerFilter); } public void stop(BundleContext bc) throws Exception { } } Listing 4 – 22: Implementieren des BundleActivators Da der Sip Communicator aus mehreren Modulen besteht, muss eine bestimmte Reihenfolge existieren, wie die einzelnen Module kompiliert bzw. ausgeführt werden. Dafür wird Ant eingesetzt, was ein Java-basiertes Werkzeug zum automatisierten Erzeugen von Programmen aus Quelltext darstellt. Damit erfüllt es den gleichen Zweck wie das sehr verbreitete Programm make, nämlich die automatisierte Erstellung von installierbaren Software-Paketen aus existierendem Quelltext, Bibliotheken und sonstigen Dateien. Gesteuert wird Ant durch eine XML-Datei, die so genannte Build-Datei. Sie heißt standardmäßig build.xml. In der Build-Datei wird ein Projekt definiert. Dies ist das Wurzelelement der XML-Datei. Zu einem SoftwareProjekt sollte genau eine Build-Datei und damit genau ein Ant-Project gehören. Das Ant-Projekt enthält Targets. Diese sind vergleichbar mit Funktionen in Programmiersprachen und können von außen, zum Beispiel vom Entwickler über die Kommandozeile oder die Entwicklungsumgebung gezielt aufgerufen werden. Die Targets sollten in ihrer Gesamtheit alle bei der Arbeit mit einem Software-Projekt anfallenden Tätigkeiten abdecken. Ein Target besteht aus Aufrufen von Tasks. Sie sind vergleichbar mit Befehlen in Programmiersprachen. Damit das Beispiel-Bundle auch zusammen mit den anderen Komponenten kompiliert wird, muss man sein eigenes Target in der build.xml Datei eintragen (sh. Listing 4-23). - 64 - 4. Realisierung <!-- BUNDLE-PLUGIN-EXAMPLE PLUGIN --> <target name="bundle-plugin-exampleplugin"> <jar compress="false" destfile="${bundles.dest}/exampleplugin.jar" manifest="${src}/net/java/sip/communicator/ plugin/exampleplugin/exampleplugin.manifest.mf"> <zipfileset dir= "${dest}/net/java/sip/ communicator/plugin/exampleplugin" prefix="net/java/sip/communicator/plugin/exampleplugin"/> </jar> </target> Listing 4 – 23: Beschreibung eines Ant - Targets in der build.xml Datei Die aufgeführten Anweisungen erzeugen eine Jar-Datei unter dem Namen exampleplugin.jar. Damit der Target bundle-plugin-exampleplugin ausgeführt wird, muss er zu der Liste mit den anderen Targets hinzugefügt werden, die die Reihenfolge des Kompiliervorgangs bestimmt (sh. Listing 4-24). <!--ALL BUNDLES--> <target name="bundles" depends="bundle-util,bundle-configuration,bundleconfiguration-slick, bundle-history,bundle-history-slick,bundle-messagehistory, bundle-msghistory-slick, bundle-callhistory, bundlecallhistory-slick, bundle-netaddr,bundle-netaddr-slick,bundle-slickless, bundle-slick-runner,bundle-sip,bundle-sip-slick,bundlefileaccess, bundle-fileaccess-slick,bundle-media,bundle-media-slick, bundle-protocol,bundle-icq,bundle-icq-slick,bundle-mock, bundle-jabber,bundle-jabber-slick,bundle-swing-ui, bundle-msn,bundle-msn-slick, bundle-contactlist,meta-contactlist,meta-contactlist-slick, bundle-plugin-icqaccregwizz,bundle-plugin-jabberaccregwizz, bundle-plugin-msnaccregwizz,bundle-plugin-sipaccregwizz, bundle-version,bundle-version-impl,bundle-shutdown, bundle-growlnotification, bundle-plugin-exampleplugin"/> Listing 4 – 24: Festlegung der Kompilierreihenfolge des Ant - Targets Jetzt muss nur noch dem OSGi Framework mitgeteilt werden, dass beim Starten des Sip Communicator auch dieses Bundle gestartet werden soll. Dies macht man in der Konfigurationsdatei felix.client.run.properties, welche den Ablauf beim Starten des Programms bestimmt. Der Plugin wird unter dem Feld felix.auto.start.67 abgelegt, wo auch andere Bundles aufgelistet sind (sh. Listing 4-25). - 65 - 4. Realisierung felix.auto.start.67= \ reference:file:sc-bundles/accountinfo.jar \ reference:file:sc-bundles/chatalerter.jar \ reference:file:sc-bundles/shutdown.jar \ reference:file:sc-bundles/utoaway.jar \ reference:file:sc-bundles/keybindingChooser.jar \ reference:file:sc-bundles/generalconfig.jar \ reference:file:sc-bundles/dictaccregwizz.jar \ reference:file:sc-bundles/exampleplugin.jar Listing 4 – 25: Konfigurationsdatei definiert welche Plugins gestartet werden. 4.4 SplitStream Erweiterungen für den SIP Communicator 4.4.1 P2P-Live-Video-Streaming-Proxy Der P2P-Live-Video-Streaming-Proxy ist eine Anwendung, die mit Hilfe von Application Layer Multicast Audio- bzw. Videoströme senden und empfangen kann. Sie wurde im Rahmen einer Diplomarbeit entwickelt, um die Splitstream-Fähigkeiten von FreePastry zu demonstrieren, Ströme aufzusplitten und über verschiedene Routen zu versenden. Die Applikation wird als RTP Proxy ausgelegt. Das heißt sie wird auf Netzwerkebene zwischen Streaming-Server und Mediaplayer geschaltet und muss deshalb nicht über deren Fähigkeiten verfügen. Für die interne Kommunikation zwischen RTP-Proxy und Streamingserver / Mediaplayer dient die lokale LoopbackSchnittstelle der Netzwerkkarte, sodass nur der RTP-Proxy außerhalb des Rechners über das Netzwerk zu sehen ist (sh. Abb. 4-5). Abbildung 4 - 5: Schema des P2P-Live-Video-Streaming-Proxys (Quelle:[th]) Die Anwendung ist unter Java entwickelt worden, da das Pastry API auch eine Java Bibliothek darstellt. Das Klassendiagramm in Abbildung 4-6 zeigt die innere Struktur von dem Programm und die möglichen Erweiterungsstellen. - 66 - 4. Realisierung Abbildung 4 - 6: Klassendiagramm des P2P-Live-Video-Streaming-Proxys - 67 - 4. Realisierung Man könnte die Klassen in drei Gruppen ordnen – Klassen für die GUI, für das SplitStream API und für die RTP Funktionalität. Die Grafikklassen stellen eine einfache Bedienoberfläche zur Verfügung. Die RTP Funktionalität ist dafür da, damit beim Eintreffen von Paketen beim Empfänger, sie in der richtigen Reihenfolge an den lokalen Mediaplayer wiedergegeben werden. Gründe dafür können starke Laufzeitschwankungen (Jitter) sein. Diese Aufgabe übernimmt die RtpSorter Klasse, die die eingehenden RTP Pakete nach deren RTP-Sequenz-Nummer sortiert und gegebenenfalls bis zum Eintreffen der Vorgänger RTP Pakete zwischenspeichert. Die wichtigen Klassen hier sind die SplitStream Klassen. Der SplitStreamHandler kümmert sich um das Erzeugen der SplitStream-Instanz und das Anmelden an dem Pastry-Netz. Er erzeugt zwei Instanzen des RTPSplitStreamServer und des RTPSplitStreamClient. Die erste Klasse erfüllt die Aufgabe, die vom Streaming Server ankommenden Pakete zu empfangen und aufgesplittet über die verschiedenen Stripes rotierend zu verschicken (sh. Listing 4-26). public void getRtpData(byte[] data, int dataLength) { stripes[(stripePosition++ % tripes.length)].publish(data); } Listing 4 – 26: Rotierendes Verschicken von Pakete Der SplitStreamClient ist zuständig für den Datenfluss in der umgekehrten Richtung. Er empfängt die Datenpakete, die über den Multicast Layer ankommen, und leitet sie an den Video Player weiter. Die drei beschriebenen Klassen werden eine weitere Rolle beim Einbinden der Anwendung in den externen Programmen spielen, da die Steuerung des Datenflusses über sie läuft. 4.4.2 Anbindung an den SIP Communicator In diesem Abschnitt werden die Änderungen präsentiert, die an dem Sip Communicator vorgenommen wurden. Dabei handelt es sich um Änderungen, die die SIP-Signalisierung betreffen. Zugleich werden auch die Klassen zusammen mit den Methoden vorgestellt, die eine Rolle in diesem Zusammenhang spielen. Der Sip Communicator besitzt eine klar definierte Klassenstruktur für die entsprechenden Aufgaben, die erfüllt werden müssen. Beispielsweise alles, was das GUI betrifft, findet man unter net.java.sip.communicator.impl.gui.*. Die einzelnen Plugins werden in extra Packages implementiert und man findet sie unter net.java.sip.communicator.plugin.*. Alle Protokolle, die der Sip Communicator implementiert (ICQ,SIP,IRC,etc.) sind unter net.java.sip.communicator.impl.protocol.* untergebracht. Da im SIP Protokoll Änderungen vorgenommen werden mussten, wurden auch einige Klassen in protocol.sip Package bearbeitet. Eine der wichtigsten Klassen hier ist die OperationSetBasicTelephonySipImpl Klasse. Sie implementiert die komplette Logik zur Anrufsteuerung des SIP Protokolls. In dieser Klasse findet man Methoden wie createInviteRequest(), prcocessInvite() oder sayBye() usw., die eine INVITE-Nachricht schicken und entgegennehmen oder ein Telefonat beenden. - 68 - 4. Realisierung Wie schon in den früheren Kapiteln beschrieben wurde, werden für diese Arbeit Methoden gebraucht, die ein erneutes Senden einer INVITE–Nachricht (re-INVITE) im Rahmen einer bereits existierenden Session ermöglichen. Die Methoden, die diese Aufgabe erfüllen sollen, sind in Abbildung 4-6 gezeigt. Abbildung 4 - 7: Ablauf beim Senden von re-INVITEs Wie man erahnt, sendet die createReInvite() – Methode erneut eine INVITE Nachricht. Angekommen beim Empfänger löst sie den Aufruf der ProcessReInvite()Methode auf, welche mit einem OK die Einladung bestätigt. Andere wichtige Methoden in dieser Klasse sind: • public void startAllReInvites():Es werden an alle Konferenz-Teilnehmer ReINVITEs geschickt • public synchronized void createReCall(CallParticipantSipImpl c): Die Methode erzeugt und verschickt die erneute Einladung. • public void processRequest(RequestEvent requestEvent): Eine der wichtigsten Methoden. Wird immer dann aufgerufen, wenn eine Anfrage ankommt. Es wird geschaut, was für eine Anfrage angekommen ist und entsprechend reagiert (sh. Listing 4-27). if (request.getMethod().equals(Request.INVITE)) { ... } else if (request.getMethod().equals(Request.ACK)) { .. } else if (request.getMethod().equals(Request.BYE)) { .. } usw. Listing 4 – 27: Abfangen der ankommenden Anfragen - 69 - 4. Realisierung Im Falle von einem INVITE wird überprüft, ob ein Dialog mit dem Benutzer bereits existiert. if (serverTransaction.getDialog().getState() == null) Falls nicht, dann wird die Anfrage als eine Einladung zum Telefonieren interpretiert. Ansonsten handelt es sich um eine re-INVITE Nachricht. Die Funktion wurde hier erweitert, um festzustellen, ob in dem SDP Teil der Message eine SplitStream Anfrage existiert (sh. Listing 4-28). String rawRequest=new String(request.getRawContent()); String boostrapIP=rawRequest. split("bootstrap:")[1].split("sts://")[1]; Listing 4 – 28: Herauslesen der Bootstrap IP-Adresse Ist das der Fall, dann wird ein SplitStream Client erzeugt, welcher den P2P-LiveVideo-Streaming-Proxy repräsentiert. • public void processResponse(ResponseEvent responseEvent): Dies ist die Methode, die immer aufgerufen wird, wenn der Client eine Antwort einer vorher gesendeten Nachricht bekommt. Je nach Response-Art wird die entsprechende Funktion aufgerufen. Für uns interessant ist die OK-Antwort. Es wird unterschieden, ob sie aus einer INVITE oder BYE – Nachricht stammt. War der Request eine Einladung, dann wird die processInviteOK() Methode aufgerufen (sh. Listing 4-29). if (response.getStatusCode() == Response.OK) if(method.equals(Request.INVITE)) { processInviteOK(clientTransaction,response); } else if (method.equals(Request.BYE)) { //ignore } Listing 4 – 29: Antworten auf eine OK-Bestätigung Die restlichen Methoden dieser Klasse werden hier nicht weiter betrachtet. Um einen tiefere Übersicht zu bekommen, empfiehlt sich ein Blick in den Quelltext zu werfen. Die nächste Klasse, die hier eine Rolle spielt, ist CallSessionImpl. Man findet sie unter net.java.sip.communicator.impl.media.*. Dieses Package beinhaltet alle nötigen Funktionen, um die Audio- bzw. Videoübertragung zu ermöglichen. - 70 - 4. Realisierung Die Klasse CallSessipnImpl erzeugt zwei RTP Manager (einen für Video und einen für Audio), wenn ein Anruf initialisiert wird. Da der Sip Communicator auf JMF aufbaut, sind momentan noch keine Konferenzgespräche möglich. Da aber in dieser Arbeit Konferenzgespräche vorausgesetzt werden, musste der Teil für die Audio/Video Kommunikation bearbeitet werden. Anstatt JMF wurden zwei bereit bestehenden Anwendungen integriert – Rat und Vic. • Robust Audio Tool (Rat) ist eine Open-Source – Anwendung, die für Audio Konferenzen und Streamen übers Internet eingesetzt wird. Sie läuft sowohl unter verschiedenen Linux Derivaten als auch unter Windows. Rat basiert auf IETF Standards und benutzt RTP über UDP/IP als Transportprotokoll. Das Tool kann entweder im Point-To-Point Kommunikation oder im IP MulticastModus eingesetzt werden. Für das zweite wird vorausgesetzt, dass sich alle Clients in einem Multicast – fähigen Netzwerk befinden. • Video Conferencing Tool (Vic) ist eine Echtzeit Anwendung für Videokonferenzen übers Internet, welche von der „Network Research Group“ am „Lawrence Berkeley National Laboratory“ in Zusammenarbeit mit der „University of California, Berkeley“ entwickelt worden ist. Vic ist mit einer flexiblen und erweiterbaren Architektur konzipiert, um heterogene Umgebungen unterstützen zu können. Das Tool setzt auch auf das RTP Protokoll auf und für die Konferenz benötigt es eine IP Multicast – fähige Umgebung. Um den Sip Communicator konferenzfähig zu machen, wurden die beiden Tools anstelle von JMF eingebaut. Wenn man die Klasse CallSessionImpl betrachtet, findet man die Methoden startstreaming() und stopstreaming(). Sie sind verantwortlich für das Starten bzw. Stoppen der Audio und Videoübertragung (sh. Listing 4-30). An dieser Stelle wurden die bisherigen RTP Manager mit den zwei Tools ersetzt. Runtime.getRuntime().exec(RATPlayerPath+ " 224.5.6.7/8910"); Runtime.getRuntime().exec(VICPlayerPath+ " 224.1.1.1/9999"); Listing 4 – 30: Starten von VIC und RAT Wie man sieht, wird für die Anwendung einfach ein extra Prozess gestartet. Es wird eine Multicast-IP vergeben, die frei wählbar ist. Wichtig ist, dass die restlichen Clients die gleiche IP Adresse aufrufen. Sie müssen entsprechend in dem gleichen Netz sein. Eine weitere wichtige Klasse ist die CallManager Klasse. Sie beinhaltet zwei Buttons – „Call“ und „Hangup“ auf dem Frontend Panel des GUIs. Damit werden auch die eingehenden und ausgehenden Telefonate bedient. Das Button-Panel wurde um noch einen Knopf erweitert, welcher für den Start einer Konferenz durch die Nutzer benötigt wird. Das Hinzufügen eines neuen Buttons geschieht erstmal in der Klasse ImageLoader. Dort werden die Buttons geladen und mit einer festen ImageID versehen (sh. Listing 4-31). - 71 - 4. Realisierung //Button zum Hinzufügen von Konferenzgespräch. public static final ImageID BUTTON_CONFERENCE = new ImageID("BUTTON_CONFERENCE"); //Button zum Hinzufügen von Konferenzgespräch. public static final ImageID BUTTON_CONFERENCE_ROLLOVER = new ImageID("BUTTON_CONFERENCE_ROLLOVER"); public static final ImageID BUTTON_CONFERENCE_PRESSED = new ImageID("BUTTON_CONFERENCE_PRESSED"); Listing 4 – 31: Festlegen der Icons eines Buttons In der Datei images.properties befinden sich die Pfade aller Buttons. Die wichtigste Methode in der Klasse CallManager ist actionPerformed(). Sie wird immer dann aufgerufen, wenn ein Button gedrückt wurde. Für unsere Konferenzerweiterung wurde auch eine Teilnehmer-Maske erstellt, welche die nötigen Operationen für den Konferenzaufbau und die Erzeugung des SplitStream – Client beinhaltet. Wenn man einen Benutzer zu einer Konferenz einladen möchte, dann muss man ihn zuerst zu der Konferenz hinzufügen. Dies geschieht, indem man den User selektiert und mit dem neuen Button in die Teilnehmer – Maske hinzufügt (sh. Abb. 4-8). Teilnehmer t = Teilnehmer.getInstance(); t.setVisible(true); t.addContact(contact); t.setTelephony( mainFrame.getTelephonyOpSet(selectedCallProvider)); Listing 4 – 32: Hinzufügen von Nutzern zu einer Konferenz Die Methode addContact() in der Teilnehmer Klasse sammelt alle eingefügten Benutzer in einem Vector, an welchen die INVITE Nachrichten verschickt werden sollen (sh. Listing 4-32). Zusätzlich hat man die Möglichkeit, die Pfade der externen Tools (Vic, Rat, Vlc) einzustellen und die zu streamende Videodatei auszuwählen. - 72 - 4. Realisierung Abbildung 4 - 8: Starten einer Konferenz Das Senden von INVITE und später re-INVITE Nachrichten geschieht in einem extra Thread, um die Arbeit mit dem Programm GUI nicht zu beeinträchtigen. Ist ein Konferenzgespräch schon aufgebaut, kann man seinen SplitStream Client starten und den anderen Teilnehmern die Aufforderung schicken, sich in dem Pastry-Netz anzumelden (sh. Listing 4-33). SSImpl= new SplitStreamImpl(); SSImpl.createSplitStreamClient(); SSImpl.StartSSClient(null, "7200"); Listing 4 – 33: Starten des SplitStream Client Die SplitStreamImpl Klasse ist die Implementierung des P2P-Live-Video-StreamingProxys. Die in diesem Kapitel vorgestellten Klassen sind nur ein kleiner Ausschnitt aus dem kompletten Quelltext. Sie sind aber diejenigen, die angepasst werden mussten, um unsere Anforderungen zu erfüllen. Möchte man seine eigenen Änderungen machen, empfiehlt sich, dass man sich an der Developer Mailing List des SIP Communicators anmeldet und dort seine Fragen stellt. - 73 - 5. Zusammenfassung 5. Zusammenfassung 5.1 Ergebnisse In dieser Arbeit wurde zunächst ein Verfahren für die SIP-Signalisierung entwickelt, das für den Aufbau von Konferenzen eingesetzt werden kann. Anschließend wird durch das Senden von re-INVITE Nachrichten aus dem SIP Protokoll heraus eine externe Anwendung aufgerufen. Es lassen sich beliebige Anwendungen ansteuern und man ist frei, Startargumente zu übergeben, wie z.B. die Bootstrap-IP-Adresse eines P2P-Netzes. Als Nächstes wurde der SIP Communicator als Open Source Produkt präsentiert, welches sehr geeignet für die Integration von neuen IM-Protokollen ist. Seine modulare Struktur basierend auf der OSGi Service Plattform macht ihn besonders geeignet, um eigene Module zu testen. Das OSGi Framework wird immer populärer und stillt den Durst nach SOA für die Java-Entwicklung. Mit steigender Videoqualität steigt entsprechend auch die Datenmenge, die über das Netz übertragen werden muss. Mit dem herkömmlichen Client/Server-Prinzip wird es für den Server zu einer enormen Belastung, alle Clients zu bedienen, im Hinblick auf Ressourcen und die damit verbundenen Kosten. SplitStream schließt genau diese Lücke. Die Serverlast wird auf die Peers übertragen und zusätzlich wird die verfügbare Uploadgeschwindigkeit der Nutzer beachtet. Den Peers steht eine kooperative Umgebung zur Verfügung, mit Rücksicht auf ihre individuellen Fähigkeiten. Das Integrieren des SplitStream Clients in den SIP Communicator stellt ein neues Feature dar, das es in dieser Variante unter den bekanntesten Instant Messengern noch nicht gibt. Damit werden die Fähigkeiten der P2P Netze auch für die IM Clients erschlossen. Es sind auch andere Dienste als Videostreaming denkbar, wie etwa Newsticker oder Social Networks. Zusammenfassend kann man sagen, dass die P2P Netze eine interessante und praktische Architektur bieten. Inzwischen kann man alle Internet-Anwendungen auch in P2P-Netzen realisieren. 5.1 Weiterführende Aufgaben Als eine neue und wenig entwickelte Umgebung funktioniert SplitStream nicht reibungslos. Entwickelt man eine Applikation, so muss man selbst entscheiden, wie die Inhalte kodiert und in Streifen geschnitten werden. Dabei muss man auch beachten, dass jeder Streifen ungefähr dieselbe Bandbreite bekommt. Man muss auch selbst einen Mechanismus entwickeln, um die zwischenzeitlichen Verluste von Streifen zu tolerieren. Die vorgeschlagenen Multiple Description Coding (MDC) oder Erasure – Codes müssen entsprechend in der Anwendung implementiert werden. - 74 - 5. Zusammenfassung Ein weiterer Punkt ist die Sicherheit. Es sind wenig konkrete Maßnahmen implementiert, die eine zufriedenstellende Sicherheit bieten. Es bestehen viele Angriffsszenarien, um das Netz zu stören, beginnend mit Man-In-The-Middle-Attack bis Join/Leave – Flooding. SplitStream wird weiterentwickelt und mit den neueren Versionen werden bestimmte Aufgaben wahrscheinlich schon in der Technologie implementiert sein, damit der Entwickler entlastet wird. Gleichgültig mit welchen Algorithmen und Technologien, das Internet wird in den kommenden Jahren mit Sicherheit vom Videosharing geprägt. Da die VideosharingDienste nicht nur Videos zum individuellen Anschauen bieten sollen, sondern auch Treffpunkte für Interessensgruppen, können mit Sicherheit viele neue Geschäftsmodelle entstehen. - 75 - Anhang A Anhang A Use Case für Integration von SplitStream mit SIP This example describes a possible usage of the SDP Protocol to switch between SIP and another protocol. In our case we have a SIP User Agent(UA) who is going to invite his chat friends in a conferencing call. At some point he wishes to show a video to his friends (for example a holiday video). The specific aspect is that this video will be streamed in several stripes in p2p mode using splitstream. This means that we are using an external application that establishes the p2p connections. We only want the conferencing users to be able to see this video. So it must be possible to switch from the SIP session to the external application that is responsible for the handling of the connections. This draft explains our thoughts to use the SDP header of a SIP (re-)Invite message to transmit information about how to join to the ring of users (bootstraping). %%%%%%%%% %PART 1 % %%%%%%%%% At first Bob creates a conferencing call. He uses the "isfocus"-tag (rfc4579) in his INVITE message to invite all participants to the conference. Alice answers with 200 OK and sets the option "Supported:conf" in the message header. The same INVITE messages are sent to all participants. After all participiants were invited the conferencing can start. The figure shows how Alice and Bob have established the session. Call-ID: [email protected] Bob Alice | | | INVITE Contact: "Bob";isfocus F1 | |------------------------------------>| | 180 Ringing F2 | |<------------------------------------| | 200 OK Supported:conf F3 | |<------------------------------------| | ACK F4 | |------------------------------------>| | RTP | |<===================================>| | | - 76 - Anhang A The following message shows the details of the session setup. Bob -> Alice (INVITE) Conference Call (Bob sets isfocus-Tag) Bob's IP l00.100.100.101 Alice's IP l00.100.100.102 F1: INVITE sip:[email protected] SIP/2.0 Via: SIP/2.0/UDP l00.100.100.101:5060;branch=z9hG4bKfw19b Max-Forwards: 70 To: Alice <sip:[email protected]> From: Bob <sip:[email protected]>;tag=76341 Call-ID: [email protected] CSeq: 1 INVITE Contact: "Bob" <sip:100.100.100.101:5060;transport=udp>;isfocus Content-Type: application/sdp Content-Length: 158 v=0 o=bob 2890844526 2890844526 IN IP4 100.100.100.101 s=Phone Call c=IN IP4 100.100.100.101 t=0 0 m=audio 49170 RTP/AVP 0 a=rtpmap:0 PCMU/8000 F3: SIP/2.0 200 OK Via: SIP/2.0/UDP l00.100.100.102:5060;branch=z9hG4bKfw19b To: Bob <sip:[email protected]>;tag=a53e42 From: Alice <sip:[email protected]>;tag=76341 Call-ID: [email protected] CSeq: 1 INVITE Contact: "Alice" <sip:[email protected]> Supported:conf Content-Type: application/sdp Content-Length: 155 v=0 o=Alice 2890844528 2890844528 IN IP4 l00.100.100.102 s=Phone Call c=IN IP4 l00.100.100.102 t=0 0 m=audio 60000 RTP/AVP 0 a=rtpmap:0 PCMU/8000 - 77 - Anhang A F4: ACK sip:[email protected] SIP/2.0 Via: SIP/2.0/UDP l00.100.100.101:5060;branch=z9hG4bK321g Max-Forwards: 70 To: Alice <sip:[email protected]>;tag=a53e42 From: Bob <ip:[email protected]>;tag=76341 Call-ID: [email protected] CSeq: 1 ACK Content-Length: 0 %%%%%%%%% %PART 2 % %%%%%%%%% Now Bob wants to start an external application with the other participants. This means he must switch from SIP to another protocol. A possible way is to send reINVITEs to the participants with changed SDP header. The SIP Header would retain the Call-Id but in the new SDP header the external application and the required arguments will be described. The figure drafts the re-INVITE message flow between Bob and Alice. Call-ID: [email protected] Bob Alice | | | re-INVITE SDP: sts-bootstrap F1 | |------------------------------------>| | 180 Ringing F2 | |<------------------------------------| | 200 OK SDP: status=starting F3 | |<------------------------------------| | ACK F4 | |------------------------------------>| | Alice starts her external p2p-application. In the SDP header of Bob's re-INVITE message you can find the following fields: m=<media> <port> <transport> <format-list> The m-field contains information about the type of media session. m=application 7200 TCP/STS 515 We use STS (Striped Session) to identify the external application that we start. 515 was freely invented and associated with the arguments needed by the application. - 78 - Anhang A a=connection:new a=control: sts://l00.100.100.101 a=fmtp:515 bootstrap: uri="sts://l00.100.100.101:7200" The a-fields contain attributes of the external application. They describe the type of the connection and a bootstrap IP:PORT needed by the external application. - connection:new is an optional field and means that it could be possible to have more than one connection to the peer. But we do not expect this case. - control: describes the IP address of the peer - fmtp:515 describes the bootstrap IP and PORT where the invited peers shall join. re-INVITE: Bob -> Alice (Striped Session) Bob starts re-INVITEs to all session partners, with the stripe-options (sts) in the SDP protocol for bootstrapping. F1: INVITE sip:[email protected] SIP/2.0 Via: SIP/2.0/UDP l00.100.100.101:5060;branch=z9hG4bKfw19b Max-Forwards: 70 To: Alice <sip:[email protected]> From: Bob <sip:[email protected]>;tag=76341 Call-ID: [email protected] CSeq: 1 INVITE Contact: "Bob" <sip:100.100.100.101:5060;transport=udp>;isfocus Content-Type: application/sdp Content-Length: 266 (z.B.) v=0 o=bob 2890844526 2890844526 IN IP4 100.100.100.101 s=Streaming Session i=A Streaming session declared within the session description protocol c=IN IP4 100.100.100.101 t=0 0 m=application 7200 TCP/STS 515 a=connection:new a=control: sts://l00.100.100.101 a=fmtp:515 bootstrap: uri="sts://l00.100.100.101:7200" - 79 - Anhang A F3: SIP/2.0 200 OK Via: SIP/2.0/UDP l00.100.100.102:5060;branch=z9hG4bKfw19b To: Bob <sip:[email protected]>;tag=a53e42 From: Alice <sip:[email protected]>;tag=76341 Call-ID: [email protected] CSeq: 1 INVITE Contact: "Alice" <sip:[email protected]> supported:conf Content-Type: application/sdp Content-Length: 155 v=0 o=Alice 2890844528 2890844528 IN IP4 l00.100.100.102 s=Phone Call c=IN IP4 l00.100.100.102 t=0 0 m=application 7878 TCP/STS 515 a=fmtp:515 status=starting F4: ACK sip:[email protected] SIP/2.0 Via: SIP/2.0/UDP l00.100.100.101:5060;branch=z9hG4bK321g Max-Forwards: 70 To: Alice <sip:[email protected]>;tag=a53e42 From: Bob <ip:[email protected]>;tag=76341 Call-ID: [email protected] CSeq: 1 ACK Content-Length: 0 - 80 - Anhang B Anhang B Abbildungsverzeichnis Abbildung 2 - 1: Kommunikation über SIP ................................................................ 10 Abbildung 2 - 2: Verbindungsaufbau mit Proxy Server ............................................. 11 Abbildung 2 - 3: RTP im TCP/IP-Protokollstapel ...................................................... 13 Abbildung 2 - 4: Client/Server Verbindungsprinzip ................................................... 14 Abbildung 2 - 5: Zentralisierte P2P-Architektur......................................................... 15 Abbildung 2 - 6: Strukturierte, dezentralisirte P2P-Architektur.................................. 15 Abbildung 2 - 7: Reine P2P-Architektur .................................................................... 16 Abbildung 2 - 8: P2P Video Streaming auf Basis von Application Layer Multicast ... 19 Abbildung 2 - 9: Ausfall eines Knoten....................................................................... 20 Abbildung 2 - 10: Korrektur des Baumes nach dem Ausfall...................................... 20 Abbildung 2 - 11: Multi-tree basiertes Streaming mit zwei Sub-streams................... 21 Abbildung 2 - 12: Abfrage der Peerliste vom Tracker-Server ................................... 23 Abbildung 2 - 13: OSGi Systemschichtung............................................................... 26 Abbildung 3 - 1: Aufbau einer Ad-hoc Konferenz...................................................... 29 Abbildung 3 - 2: Blockschaltbild MD5 ....................................................................... 33 Abbildung 3 - 3: Pastry Tabellen............................................................................... 35 Abbildung 3 - 4: Pastry, Routingtabelle .................................................................... 35 Abbildung 3 - 5: Routing einer Nachricht .................................................................. 36 Abbildung 3 - 6: Scribe Aufbau ................................................................................. 38 Abbildung 3 - 7: SplitStream Baum........................................................................... 41 Abbildung 3 - 8: OSGi-Schichten.............................................................................. 41 Abbildung 3 - 9: Zustandsdiagramm eines Bundles ................................................. 44 Abbildung 4 - 1: SplitStream, einzelne Stripes.......................................................... 46 Abbildung 4 - 2: SplitStream mit zwei Stripes ........................................................... 47 Abbildung 4 - 3: Oscar GUI....................................................................................... 58 Abbildung 4 - 4: OSGi Services ................................................................................ 58 Abbildung 4 - 5: Schema des P2P-Live-Video-Streaming-Proxys ........................... 66 Abbildung 4 - 6: Klassendiagramm des P2P-Live-Video-Streaming-Proxys............. 67 Abbildung 4 - 7: Ablauf beim Senden von re-INVITEs.............................................. 69 Abbildung 4 - 8: Starten einer Konferenz.................................................................. 73 - 81 - Anhang C Anhang C Listingverzeichnis Listing 4 - 1: SplitStream Interface........................................................................... 13 Listing 4 - 2: Eigenschaften der MySplitStreamClient Klasse .................................. 49 Listing 4 - 3: Initialisieren der Variablen ................................................................... 50 Listing 4 - 4: Anhängen an einem Channel.............................................................. 51 Listing 4 - 5: Publizieren von Daten ......................................................................... 52 Listing 4 - 6: Festlegen der Zeitabstände zum Versenden....................................... 53 Listing 4 - 7: Abfangen der angekommenen Daten.................................................. 53 Listing 4 - 8: Vorbereiten der Versuchsumgebung................................................... 54 Listing 4 - 9: Erzeugen der Nodes ........................................................................... 55 Listing 4 - 10: Starten der Nodes ............................................................................. 55 Listing 4 - 11: Programmausgabe............................................................................ 56 Listing 4 - 12: Die Activator – Klasse ....................................................................... 57 Listing 4 - 13: Manifest-Datei ................................................................................... 57 Listing 4 - 14: Kompilieren eines Bundles................................................................ 58 Listing 4 - 15: Definition vom HelloService-Interface ............................................... 59 Listing 4 - 16: Implementierung des HelloService-Interfaces................................... 38 Listing 4 - 17: Definieren von star/stop – Methoden................................................. 60 Listing 4 - 18: Setzen des Export-Package-Feldes in einer Manifest-Datei ............. 60 Listing 4 - 19: Setzen des Import-Package-Feldes in einer Manifest-Datei ............. 61 Listing 4 - 20: Implementierung des Consumer-Bundles ......................................... 61 Listing 4 - 21: Manifest-Datei eines Plugins im Sip Communicator.......................... 63 Listing 4 - 22: Implementieren des BundleActivators ............................................... 64 Listing 4 - 23: Beschreibung eines Ant - Targets in der build.xml Datei .................. 65 Listing 4 - 24: Festlegung der Kompilierreihenfolge des Ant - Targets .................... 65 Listing 4 - 25: Konfigurationsdatei definiert welche Plugins gestartet werden ......... 66 Listing 4 - 26: Rotierendes Verschicken von Pakete................................................ 68 Listing 4 - 27: Abfangen der ankommenden Anfragen............................................. 69 Listing 4 - 28: Herauslesen der Bootstrap IP-Adresse ............................................. 70 Listing 4 - 29: Antworten auf eine OK-Bestätigung .................................................. 70 Listing 4 - 30: Starten von VIC und RAT.................................................................. 71 Listing 4 - 31: Festlegen der Icons eines Buttons .................................................... 72 Listing 4 - 32: Hinzufügen von Nutzern zu einer Konferenz..................................... 72 Listing 4 - 33: Starten des SplitStream Client .......................................................... 73 - 82 - Anhang D Anhang D Literaturverzeichnis [af] Apache Felix http://felix.apache.org [DHT1] Distributed Hash Tables http://www.dbis.ethz.ch/education/ss2005/dbs_alg_05/DHT.pdf [dp] Diplomarbeit, Datenverteilung in Peer-To-Peer Overlay-Netzwerken http://www.xml-und-datenbanken.de/sada/da-pohl.pdf [fp] Free Pastry http://freepastry.org/ [itw] RTP (realtime transport protocol) http://www.itwissen.info/definition/ lexikon/realtime-transport-protocol-RTP-RTP-Protokoll.html [Lyl] Diplomarbeit Lyubomir Lyubenov Security for SIP-based P2P Internet Telephony http://www.swt.hs-mannheim.de/cms/servlet/Download? document=1/17854929933700.pdf [om] Overlay-Multicast http://www-rnks.informatik.tu-cottbus.de/content/ unrestricted/teachings/2005/WS/ SeminarP2P/ausarbeitungen/05-overlay_multicast.pdf [os] Oscar Tutorial http://oscar-osgi.sourceforge.net/tutorial/index.html [Osp] Die OSGi Service Platform . ISBN : 978-3-89864-457-0 Gerd Wütherich, Nils Hartmann, Bernd Kolb, Matthias Lübken. dpunkt.verlag [Owk] OSGi Alliance http://de.wikipedia.org/wiki/OSGi [pt] Die Übersetzung von dem Pastry-overview http://www4.informatik.unierlangen.de/Lehre/SS02/HS_DOOS/pdf/handout-sitiholm.pdf [r1] Session Initiation Protocol RFC 3261 http://www.ietf.org/rfc/rfc3261.txt - 83 - Anhang D [r2] Session Initiation Protocol RFC 2543 http://www.ietf.org/rfc/rfc2543.txt [r3] Session Initiation Protocol (SIP) Call Control - Conferencing for User Agents RFC 4579 http://www.ietf.org/rfc/rfc4579.txt [sip] SIP,TCP/IP und Telekommunikationsnetze ISBN: 3-486-27529-1 U.Trick/F.Weber oldenburg-verlag [sm] Seminar-Arbeit OSGi - The Dynamic Module System for Java http://www.gruntz.ch/courses/sem/ss07/OSGi.pdf [sr] “A survey on peer-to-peer video streaming systems” http://www.springerlink.com/content/c62114g6g4863t32/ Springer Verlag New York [ss] SplitStream http://freepastry.org/SplitStream/default.htm [thr] Masterarbeit Thorsten Kummermehr P2P Live Video Streaming http://www.swt.hs-mannheim.de/cms/servlet/ Download?document=1/17863270761600.pdf [Wiki] Wikipedia http://de.wikipedia.org - 84 -