Ein File-Sharing-Client auf Basis von XMPP und

Transcription

Ein File-Sharing-Client auf Basis von XMPP und
Ein File-Sharing-Client auf Basis von
XMPP und BitTorrent
Release Bachelorarbeit
Jan Hartmann
Aug 22, 2016
Contents
1
Abstract
1
2
Einleitung
3
3
Planung
3.1 Anforderungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Konzept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
5
6
4
Zusammenhänge und Grundlagen
7
4.1 XMPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
4.2 BitTorrent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
5
Implementierung
5.1 Allgemeines zur Implementierung
5.2 Entwurf . . . . . . . . . . . . . .
5.3 BitTorrent . . . . . . . . . . . . .
5.4 XMPP . . . . . . . . . . . . . .
5.5 Web . . . . . . . . . . . . . . . .
5.6 Inter-Process Communication . .
5.7 Abschluss der Implementierung .
6
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Beurteilung der Ergebnisse
6.1 Vor- und Nachteile der serverlosen Dateiübertragung
6.2 libtorrent . . . . . . . . . . . . . . . . . . . . . . .
6.3 XMPP Ansätze . . . . . . . . . . . . . . . . . . . .
6.4 Threading . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13
13
14
16
21
29
34
37
.
.
.
.
41
41
42
42
42
7
Ausblick
45
8
Zusammenfassung
47
9
Anhänge
49
i
9.1
9.2
Übersicht der IPC Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Inhaltsverzeichnis der CD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
10 Literaturverzeichnis
51
10.1 Bücher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
10.2 URLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Bibliography
ii
53
CHAPTER 1
Abstract
Die in XMPP üblichen Dateiübertragungen sind aufgrund eingeschränkter Funktionalität der
Server oder inkompatibelen XMPP Clients häufig langsam oder es kommen wegen mangelnder
Kompatibilität keine Übertragung zustande. Außerdem ist es nicht möglich, Dateien dauerhaft
zum Download anzubieten. Diese Thesis beschreibt die Möglichkeit, Metadaten von zu übertragenden Dateien und IP Adressen des Nutzers per XMPP zu verteilen, die Dateiübertragung selbst
aber über BitTorrent zu führen. Dies ermöglicht verteilte Downloads sowie dauerhafte Freigaben. Um die Möglichkeiten und Limitierungen dieser Methode aufzuzeigen wurde ein XMPPund BitTorrent Client implementiert.
1
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
2
Chapter 1. Abstract
CHAPTER 2
Einleitung
Das eXtensible Messaging and Presence Protocol (XMPP), umgangssprachlich “Jabber”, ist ein
offenes Kommunikationsprotokoll, das im Wesentlichen eine Technologie darstellt, in XML eingebettete Daten zu streamen und das sich seit seiner Veröffentlichung 1999 [XMPa] (page 54) stark
verbreitet hat. Beispielsweise arbeiten der Facebook-Chat, WhatsApp und GoogleTalk mit XMPP.
Ausserdem verwenden Apple, Cisco, IBM, Nokia und Sun XMPP in einigen ihrer Produkte. (Vgl.
[XMP.8] (page 53))
Durch die Möglichkeit, XMPP Server ähnlich wie Email Server in einem dezentralen Netzwerk zu
betreiben, ist aber auch ein Netzwerk von vielen öffentlichen, privat betriebenen XMPP Servern
gewachsen. Listen einiger öffentlicher Server finden sich beispielsweise unter jabberes.org/servers
[jab] (page 53) oder xmpp.net/directory [imo] (page 53).
Allerdings bringt die Tatsache, dass XMPP auf einem textbasierten Protokoll aufbaut, auch
Nachteile mit sich.
So werden im Falle der Übertragungen von Binärdaten diese erst Base64 kodiert, um sie in den
XML Stream einzubetten. Dies vergrößert die Datenmenge auf ca. 4/3 für diese sogenannten InBand Übertragungen und häufig wird die Übertragungsrate dieser XML-Datenpakete dann noch
von den beteiligten Servern gedrosselt.
Deshalb stellen In-Band Übertragungen in den meisten Fällen nur den Fallback Modus dar,
bevorzugt werden Out-of-Band Übertragungen.
Das bedeutet, dass eine separate Client-zu-Client Verbindung hergestellt wird, über die die
Datenübertragung stattfinden soll. Da hier aber mehrere Erweiterungen existieren, die wiederum
von beteiligten Clients sowie Servern unterstützt werden müssen, schlagen diese Übertragungen
häufig fehl, sodass in vielen Fällen die Clients auf In-Band Übertragung zurückfallen oder die
Übertragung gar nicht zustande kommt.
Diese Thesis untersucht eine andere Methode, die Dateiübertragungen abzuwickeln: über das Peerto-Peer Protokoll BitTorrent (BT).
XMPP dient hierbei nur noch dazu, die Daten weiter zu leiten, die für das Starten einer Datenübertragung per BitTorrent nötig sind. Das Herstellen der Verbindung zwischen den Clients sowie die
eigentliche Datenübertragung finden komplett über BitTorrent statt.
3
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Dazu wurde ein XMPP- und BitTorrent Client in Python implementiert, der dazu dient, die für den
Datenaustausch wichtigen Informationen unter den Teilnehmern zu verteilen und gegebenenfalls
die Datenübertragungen abzuwickeln.
Diese Thesis geht im Kapitel Planung (page 5) auf die Anforderungen an das Programm und das
Konzept ein. Darauf folgt das Kapitel Zusammenhänge und Grundlagen (page 7), in dem das
benötigte Wissen über XMPP und dessen Erweiterungen, sowie BitTorrent vermittelt wird. Das
Kapitel Implementierung (page 13) unterteilt sich in Kapitel zum Entwurf der Anwendung und zur
konkreten Umsetzung der einzelnen Komponenten. Darauf folgt die Beurteilung der Ergebnisse
im Kapitel Beurteilung der Ergebnisse (page 41) und eine Zusammenfassung mit Ausblick auf
Erweiterungsmöglichkeiten in Kapitel Zusammenfassung (page 47).
4
Chapter 2. Einleitung
CHAPTER 3
Planung
3.1 Anforderungen
Da es sich bei der geplanten Anwendung um einen Prototypen handelt, der in erster Linie dazu
dient, das Konzept der Datenübertragung zu testen, sollen die Anwendungsfälle auf ein für die
grundlegenden Funktionen wichtiges Minimum reduziert bleiben.
Fig. 3.1: Anwendungsfälle
Das Diagramm Anwendungsfälle (page 5) zeigt die geplanten Anwendungsfälle die implementiert
werden sollen, um grundlegende Funktionalität zu gewährleisten und die Anwendung sinnvoll
nutzen zu können.
So sollen eigene Freigaben erstellt und entfernt werden können (AF/10/ und AF/20/) sowie Freigaben anderer Nutzer aufgelistet und durchsucht werden, als auch Downloads angestoßen werden können. (AF/30/ bis AF/40/) Als Teil der Konfiguration soll außerdem ein XMPP Account
eingestellt werden können. (AF/60/ und AF/70/)
5
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
3.2 Konzept
Fig. 3.2: Grafik zum Konzept der Anwendung
Diagramm 3.2 zeigt das Grundkonzept der Anwendung. Erreicht werden soll also im ersten
Schritt eine Datenfreigabe über BitTorrent und das Verteilen der nötigen Freigabeinformationen
über XMPP an die Kontakte der jeweiligen Kontaktliste.
Freigabeinformationen umfassen die von BitTorrent benötigten Prüfsummen der freigegebenen
Daten, die zur eindeutigen Identifikation dienen, dazu Dateinamen und -Größe, was den Anwendern hilft den Inhalt einzuschätzen, sowie Adresse und Port unter der die Freigaben für andere
Teilnehmer zu finden sind.
Eine Gegenstelle, die die Freigabeinformationen empfangen hat, kann dann über IP-Adresse, Port
und die Prüfsumme einen Download von der Gegenstelle anstoßen. Falls für eine Prüfsumme (und
damit eine Freigabe) mehrere Adressen vorliegen, kann der Download auch von mehreren Quellen
gleichzeitig erfolgen.
Sobald ein Teil der Daten von einer Gegenstelle heruntergeladen wurde, wird diese als neue
Freigabe des Kontakts auch an dessen Kontakte übermittelt und agiert so als ein neuer Knotenpunkt, der ebenfalls Teile dieser Datei zum Download anbietet.
Auf diese Art kann in relativ kleinem Kreis ein verteiltes Filesharing stattfinden.
6
Chapter 3. Planung
CHAPTER 4
Zusammenhänge und Grundlagen
Um das in der Einleitung beschriebene Konzept umzusetzen werden im folgenden Kapitel
“XMPP” einige Grundlagen über die Funktionsweise von XMPP und dessen Erweiterung Personal
Eventing Protocol (PEP) erläutert.
Außerdem wird im darauf folgenden Kapitel “BitTorrent” auf einige Grundlagen zur Funktion von
BitTorrent und dessen Verfahren zur Identifikation von Daten eingegangen.
4.1 XMPP
Das Extensible Messaging and Presence Protocol ist im Grunde eine Technologie zum XML streamen (vgl. [XMP16] (page 53)) und kann so benutzt werden, um alle möglichen Arten textbasierter
Informationen zu versenden und zu empfangen.
Ein Stream beginnt immer mit dem Öffnen eines Stream Tags mit “<stream>” und endet mit dem
Schließen desselben mit “</stream>”. Innerhalb dieses Streams können eine beliebige Menge an
XML Elementen, die sogenannten “Stanzas”, versendet werden. Die XMPP Core RFC definiert
ein Stanza als “discrete semantic unit of structured information that is sent from one entity to
another” [Ext] (page 53), also als ein XML Tag in den wiederum Tags eingebettet sein können.
Für die Tiefe eins des Streams, also unmittelbar dem Stream Stanza untergeordnet, sind drei
Basis-Stanzatypen definiert, die sich im default-Namespace “jabber:client” bzw. “jabber:server”
befinden:
<message/> <presence/> <iq/>
Jedes dieser Basisstanzas erfüllt unterschiedliche Funktionen.
So ist mit dem Message Stanza ein “Push” Mechanismus verbunden, um Nachrichten direkt an
andere Teilnehmer zu verschicken, beispielsweise:
Das Presence Stanza funktioniert als “Publish-Subscribe” Mechanismus. In der Basisfunktionalität
ist dies der Verfügbarkeitsstatus: ist ein Kontakt online oder nicht. Es wird also jeder Kontakt,
der die eigene Presence abonniert hat, automatisch über Statusänderungen benachrichtigt. Diese
7
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
ist üblicherweise erweitert durch ein Show und Status Stanza, das eine nähere Beschreibung der
Anwesenheit gibt:
Um diese Nachrichten zu empfangen, wird eine Presence-Subscription benötigt, also ein Handshake, bei dem die Gegenstelle das “Abonnement” des Kontakts akzeptiert. Dies wird üblicherweise von den Clients durchgeführt, wenn ein neuer Kontakt zum Roster hinzugefügt werden soll.
1
Das Presence Stanza wird durch viele XMPP Extension Protocols (XEP) erweitert, insbesondere XEP-0060 (“Publish-Subcribe”) und XEP-0163 (“Personal Eventing Protocol”), auf die noch
näher eingegangen wird.
Das Info/Query Stanza (IQ) implementiert einen Query-Response Mechanismus und ist vergleichbar mit der HTTP Funktionalität.
Ein IQ Stanza kann eins von vier type-Attributen haben:
Listing 4.1: 4 IQ Stanzatypes [Ext] (page 53)
get -- The stanza is a request for information or requirements.
set -- The stanza provides required data, sets new values, or
˓→replaces existing values.
result -- The stanza is a response to a successful get or set request.
error -- An error has occurred regarding processing or delivery of a
˓→previously-sent get or set (see Stanza Errors).
Zur Verdeutlichung wie diese unterschliedlichen Funktionen ineinander greifen, sei dieses Beispiel
aus XMPP: The Definitive Guide [SAST09] (page 53) gegeben.
Listing 4.2: XML Beispielstream aus [SAST09] (page 53) (s.17)
C: <stream:stream>
C: <presence/>
C: <iq type="get">
<query xmlns="jabber:iq:roster"/>
</iq>
S: <iq type="result">
<query xmlns="jabber:iq:roster">
<item jid="[email protected]"/>
<item jid="[email protected]"/>
<item jid="[email protected]"/>
</query>
</iq>
1
Der Handshake überschreitet die für diese Thesis benötigten Grundlagen, deshalb sei an dieser Stelle auf das
Buch “XMPP - The definitive Guide” von Peter Saint-Andre, Kevin Smith und Remko Tronçon [XMP16] (page 53)
verwiesen.
8
Chapter 4. Zusammenhänge und Grundlagen
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
C: <message from="[email protected]"
to="[email protected]">
<body>Off with his head!</body>
</message>
S: <message from="[email protected]"
to="[email protected]">
<body>You are all pardoned.</body>
</message>
C: <presence type="unavailable"/>
C: </stream:stream>
Üblicherweise wird sich ein User mit seiner “Jabber ID” (JID) anmelden. Diese besteht aus dem
Accountnamen, der Serveradresse und einer Ressource, die die jeweiligen Endpunkte unterscheidet, im Format “username@serveraddresse/resource”. Die Kombination aus Accountname und
Serveraddresse wird “bare” JID genannt, kommt die Ressource hinzu, spricht man von der “full”
JID.
4.1.1 Erweiterungen
‘The “X” in XML and XMPP stands for “extensible,” so payload types are limited
only by your imagination!’ [SAST09] (page 53)
Dadurch, dass XMPP auf der Extensible Markup Language aufbaut kann es relativ leicht um eigene
Funktionen erweitert werden. Die XMPP Standards Foundation führt hierzu eine Liste der eingereichten Erweiterungen als XMPP Extension Protocols (XEP). Diese umfassen zu diesem Zeitpunkt
379 Dokumente.
Als Möglichkeit, mit wenig Aufwand definierte Informationen an die eigenen Kontakte zu senden,
soll hier eine Einführung in das Personal Eventing Protocol (PEP, definiert in XEP-0163), bzw.
eine seiner Anwendungen, das auf PEP aufbauende “User Tune” (XEP-0118) gegeben werden.
PEP / User Tune
“Instead of extending <presence> stanzas directly, it is a best practice to make use
of the Personal Eventing Protocol, or PEP, defined in XEP-0163, which allows users
to subscribe to the extra data they are interested in. The PEP extension, along with
Entity Capabilities (XEP-0114) and Service Discovery (XEP-0015), make providing
extended presence-type information efficient and opt-in.” [pro35] (page 53)
Mit dem Personal Eventing Protocol (PEP) existiert eine gute Möglichkeit, nutzerbezogene Informationen zu teilen. Hier wird jedem Nutzeraccount eine PubSub Node zugeordnet, auf der er
4.1. XMPP
9
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Informationen in die jeweiligen Namespaces publishen kann.
Mithilfe von Entity Capabilities (XEP-0115) [XEPa] (page 54) kann ein Kontakt dem Server mitteilen, welche Namespaces er unterstützt (PEP spricht hier von “interest”), und wird daraufhin
nach diesen Namespaces gefilterte Listen mit Userinformationen bekommen. Außerdem wird der
Server falls nötig Updates ausliefern.
Eine zweite Möglichkeit, PEP Nachrichten zu erhalten ist das “auto-subscribe” Feature, bei dem
die gesamte Presence eines Users abonniert wird. In diesem Fall bekommt der Client immer alle
Nodes, es wird nicht gefiltert.
Bereits in vielen Clients umgesetzt sind die auf PEP basierenden Erweiterungen “User Geolocation” (XEP-0080), “User Mood” (XEP-0107), “User Activity” (XEP-0108) und “User Tune”
(XEP-0118). All diese XEPs sind darauf ausgelegt, Informationen, die sich auf den aktuellen
Useraccount beziehen, an interessierte Kontakte auszuliefern.
Ein übersichtliches Beispiel zur Anwendung von PEP ist in der User Tune Spezifikation gegeben.
Listing 4.3: Beispiel: Publishing an event xep-0118 [XEPb] (page 54)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<iq type='set'
from='[email protected]/14793c64-0f94-11dc-9430-000bcd821bfb'
id='tunes123'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<publish node='http://jabber.org/protocol/tune'>
<item>
<tune xmlns='http://jabber.org/protocol/tune'>
<artist>Yes</artist>
<length>686</length>
<rating>8</rating>
<source>Yessongs</source>
<title>Heart of the Sunrise</title>
<track>3</track>
<uri>http://www.yesworld.com/lyrics/Fragile.html#9</uri>
</tune>
</item>
</publish>
</pubsub>
</iq>
In Beispiel: Publishing an event xep-0118 [XEP-0118:online] (page 10) sendet User
‘[email protected]‘ vom Endpunkt ‘14793c64-[...]’ ein PEP Event Stanza auf die Node ‘http:
//jabber.org/protocol/tune‘, was dem Namespace des eingebetteten Stanza “tune” entspricht und
keine aufrufbare URL, sondern nur ein Name für Namespace und Node ist.
Daraufhin werden alle User in seiner Kontaktliste, die die Presence oder den Namespace abonniert
haben, das aktuelle pubsub Stanza bekommen.
Im Kapitel XMPP (page 21) Implementierung/XMPP wird beschrieben, wie eine eigene PEP Er-
10
Chapter 4. Zusammenhänge und Grundlagen
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
weiterung die für BitTorrent benötigten Informationen einbetten kann.
4.2 BitTorrent
Für die Datenübertragung soll das BitTorrent Protokoll genutzt werden. Dieses nutzt, wenn keine
Protokollerweiterungen genutzt werden, einen “Tracker” genannten Server zur Vermittlung der
Teilnehmer (“Peers”).
“BitTorrent is a protocol for distributing files. It identifies content by URL and is designed to integrate seamlessly with the web. Its advantage over plain HTTP is that
when multiple downloads of the same file happen concurrently, the downloaders upload to each other, making it possible for the file source to support very large numbers
of downloaders with only a modest increase in its load.”
—[wwwa] (page 54)
Sinngemäß übersetzt:
“BitTorrent ist ein Protokoll zum Verteilen von Dateien. Es bestimmt Inhalt anhand
einer URL und ist dazu entworfen, sich nahtlos ins Internet zu integrieren. Der Vorteil
zu HTTP ist, dass wenn multiple Downloads derselben Datei zur gleichen Zeit stattfinden, die Downloader zueinander uploaden. Dadurch kann eine Dateiquelle sehr
viele Downloader bei geringem Anstieg seiner Last haben.”
Der Vorteil von BitTorrent als Übertragungsprotokoll ist also, dass wenn mehr als ein Kontakt
dieselbe Datei zum Download anbietet, auch von mehreren Kontakten gleichzeitig heruntergeladen werden kann. Hierzu würde normalerweise der Tracker die Peers vermitteln. In dieser
Implementierung soll dies jedoch über XMPP geschehen.
Die Identifikation der Dateien findet laut der BitTorrent Protocol Specification ([wwwa] (page 54))
über ein “info dict” im torrent-File statt. In dieser Implementierung soll jedoch eine andere Methode genutzt werden: Die in der BitTorrent Extension Protocol (BEP) 9 beschriebene Unterstützung
für Magnet Links.
“The purpose of this extension is to allow clients to join a swarm and complete a
download without the need of downloading a .torrent file first. This extension instead
allows clients to download the metadata from peers. It makes it possible to support
magnet links, a link on a web page only containing enough information to join the
swarm (the info hash).” [wwwb] (page 54)
Das in der Spezifikation beschriebene Format eines Magnet Links ist dabei wie folgt:
magnet:?xt=urn:btih:<info-hash>&dn=<name>&tr=<tracker-url>&x.pe=<peeraddress>
Da kein Tracker benötigt wird um Informationen zu verteilen und dynamisch Peer Adressen
hinzugefügt werden sollen, wird hier also nur der Info Hash benötigt. Dieser ist der SHA-1 Hash
des info dict des torrent-Files.
4.2. BitTorrent
11
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Da in der zur Implementierung genutzten Libary (libtorrent) die Möglichkeit besteht, einen neuen
Torrent auf Basis eines Magnet Links anzulegen, der nur einen Info Hash enthält, und später dynamisch Peer Adressen hinzuzufügen, ist es möglich das komplette Peer Management zur Laufzeit
abzuwickeln.
12
Chapter 4. Zusammenhänge und Grundlagen
CHAPTER 5
Implementierung
5.1 Allgemeines zur Implementierung
Als Programmiersprache zur Implementierung des Prototypen wurde aufgrund der bisherigen
Programmiererfahrungen des Autors und des Vorhandenseins aller nötigen Bibliotheken Python
gewählt.
Die Verzeichnisstruktur des Projektes ist dabei angelehnt an die Empfehlungen des “Hitchhikers
Guide To Python” [hit] (page 53). Eine der Übersichtlichkeit wegen vereinfachte Version der
Struktur sieht wie folgt aus:
Listing 5.1: Projektstruktur
bitween/
components/
bitweend.py
bitweenc.py
docs/
conf.py
index.rst
tests/
requirements.txt
setup.py
bitween/
Der Name des Programms und der Name des Verzeichnisses, das den Programmcode
enthält.
Im Unterverzeichnis “components” befinden sich die Module, in denen die jeweiligen
Funktionen und Klassen implementiert wurden. Ein Modul umfasst dabei jeweils eine
Datei “__init__.py”, die das Verzeichnis als Modul in Python importierbar macht. Da
in Python keine privaten Methoden existieren, werden in der __init__.py alle Funktionen oder Klassen aus dem Modul importiert, die von anderen Modulen benötigt
13
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
werden könnten. So wird eine logische Abgrenzung zu Elementen geschaffen, die
nur im Modul benötigt werden, und solchen, die für die Nutzung von anderen Modulen gedacht sind. Der Aufbau der einzelnen Komponenten wird in den folgenden
Kapiteln besprochen.
bitweend.py
Der Einstiegspunkt für das Programm, zum Starten des Daemons. (Bitweend ist hier
kurz für Bitween Daemon)
bitweenc.py
Client für die JSON-RPC API des Programms.
docs/
Verzeichnis, das alle benötigten Dateien zum Generieren der Dokumentation enthält.
Im einfachsten Fall die vom Dokumentationsgenerator Sphinx benötigte Konfigurationsdatei conf.py und eine reStructuredText-Datei index.rst, die als Einstiegspunkt
für die Dokumentation dient.
tests/
Das tests-Verzeichnis enthält alle Testläufe. Durch das Hinzufügen der __init__.py
wird hier eine automatische Testdiscovery ermöglicht. “python setup.py test”, ausgeführt im Wurzelverzeichnis des Projektes, würde hier automatisch alle hinterlegten
Tests ausführen.
requirements.txt
Die requirements.txt enthält eine Liste der über den Python Paketmanager pip installierbaren Abhängigkeiten des Projekts.
setup.py
Das Setupscript enthält alle Informationen, um das Programm mit den Python Distutils
bzw. pip zu installieren
5.2 Entwurf
Das Programm gliedert sich in verschiedene Kernkomponenten (Abbildung 5.1), die in den folgenden Kapiteln besprochen werden:
• der XMPP Client (XmppClient)
Der XMPP Client ist dafür zuständig eine Verbindung mit dem gewünschten
XMPP Server herzustellen, bei jeder Aktualisierung der Torrents eine neue Übersicht über die angebotenen Shares an den Server zu übermitteln und Aktualisierungen aus der Kontaktliste zu empfangen. Darüber hinaus startet der Client
14
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Fig. 5.1: Modulübersicht
alle weiteren benötigten Prozesse und dient somit als “Aufseher” über die Startreihenfolge und eventuelle Abhängigkeiten. Als XMPP Libary wird hier die Python
Bibliothek SleekXMPP verwendet.
• der BitTorrent Client (BitTorrentClient)
Der BitTorrent Client lädt beim Start gespeicherte Torrents der letzten Session. Er
stellt im Falle von hinzugefügten Torrents eine Verbindung zu allen IP-Adressen
(und damit zu allen anderen BitTorrent Clients) her, die bisher per XMPP empfangen wurden. Als Libary wird libtorrent verwendet, eine in C++ geschriebene
Bibliothek mit optionaler Python Anbindung.
• eine Nutzerschnittstelle zur Bedienung (Web)
Die JSON-RPC API des Web Moduls dient als Schnittstelle für Frontends. Da das
Programm theoretisch als Daemon auf einem entfernten Rechner laufen könnte,
öffnet es einen Port zur Steuerung. Hier wurde mit Hilfe des Frameworks Flask
ein minimales Webinterface und eine JSON-RPC Schnittstelle für andere externe
Anwendungen entwickelt.
• ein Modul zur IPC (Subscriber)
Da alle genannten Module in eigenen nebenläufigen Threads laufen, wird eine
Komponente zur Inter-Process Communication benötigt. Hierzu wurde eine
Publish-Subscribe Pattern implementiert, die das Zuweisen der Nachrichten zu
Subscribern übernimmt. Außerdem dient es als Basisklasse, von der alle Klassen,
deren Objekte Nachrichten empfangen, abgeleitet werden. Dazu wurde eine einfache Scheduling Funktion implementiert.
Außerdem wurden Klassen zur Abstrahierung der Daten zur Laufzeit geschrieben:
• Addresses für die eigenen IP-Adressen und BitTorrent Ports
5.2. Entwurf
15
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
• Handles für die einzelnen Torrent Handles
• ContactShares für alle empfangenen Shares
5.3 BitTorrent
Als erster Teil der beschriebenen Problemstellung soll die Implementierung eines BitTorrent
Clients zur Übertragung der Nutzdaten besprochen werden.
Als Anforderung an die Komponente stellt sich, dass diese über die komplette Laufzeit des Programms neben der XMPP Komponente laufen muss. Daher arbeitet der BitTorrent Client in einem
eigenen Thread.
Dazu ist eine Kommunikation mit anderen Programmteilen nötig, deren genaue Implementierung
im Kapitel Inter-Process Communication (page 34) erläutert wird. Dieses Kapitel beschränkt sich
auf die benötigten Schnittstellen und geht auf deren Zweck ein.
5.3.1 Aufbau der Komponente
Wie in Diagramm 5.2 zu sehen, leitet sich die BitTorrentClient Klasse aus der Thread Klasse ab, die
sich in der Python Standard Libary befindet und somit zum Lieferumfang jeder Python Installation
gehört.
Außerdem erbt BitTorrentClient von der Klasse Subscriber, deren Implementierung im Kapitel
Inter-Process Communication (page 34) erläutert wird und die Funktionen zur Prozesskommunikation bereitstellt.
Als BitTorrent Libary wurde libtorrent verwendet. Über diese kann ein “session” Objekt erzeugt
werden, über das Einstellungen wie die zu nutzenden Ports gemacht werden können, und das
Hilfsfunktionen wie das Erstellen eines Torrent Objektes aus einem Magnet Link zur Verfügung
stellt.
Zur Verwaltung der Torrent Handles, also der Objekte, die die jeweiligen Torrents repräsentieren,
wurde außerdem die Klasse “Handles” als Wrapper um eine Liste implementiert. Genutzt wird
vom Client nur append() und remove() um Torrent Handles anzuhängen bzw. zu entfernen. Die
get_shares() Methode wird vom XMPP Client genutzt um die eigenen Handles als Liste von Dictionarys mit einigen Eckdaten auszulesen.
5.3.2 Erstellen des BitTorrentClient Objekts
16
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Fig. 5.2: Klassendiagramm BitTorrent
5.3. BitTorrent
17
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Listing 5.2: initalisieren des BitTorrent Clients
def __init__(self):
Thread.__init__(self)
Subscriber.__init__(self, autosubscribe=True)
[...]
Im ersten Schritt werden im Konstruktor die beiden Basisklassen, Thread und Subscriber, initalisiert.
Subscriber wird hier mit “autosubscribe=True” erstellt. Dies bedeutet, dass alle Methoden, die mit
“on_” beginnen, automatisch als Topic zum Empfangen von Nachrichten registriert werden. So ist
es relativ einfach möglich, aus anderen Programmteilen beispielsweise einen Torrent hinzuzufügen
oder das Beenden des Threads anzustoßen.
Danach wird überprüft, ob eine SQLite Datenbank in Homeverzeichnis des Nutzers existiert. Der
Dateiname ist festgelegt auf ”.bitween.db”. Ist diese Datei nicht präsent, wird sie erzeugt. Dazu
wird in der Methode setup_db() eine neue Tabelle “torrents” mit den Spalten “magnetlink”, “torrent”, “status” und “save_path” angelegt. Diese werden benötigt um die Torrents zu persistieren.
Als nächstes wird das session Objekt erzeugt und je nach geladener Konfiguration Einstellungen
gemacht: Ports auf denen BitTorrent arbeiten soll werden festgelegt (oder, wenn nicht gesetzt, dynamisch von der Libary gewählt), UPNP und NATPMP werden aktiviert wenn gewünscht. Diese
Techniken werden benutzt um automatisch Ports in der NAT Table zu setzen und werden üblicherweise für den Betrieb hinter einem DSL Router benötigt.
Zu guter Letzt werden die in der SQLite Datenbank vorhandenen Torrents geladen und mit dem
session Objekt verknüpft.
Danach ist der BitTorrentClient für den Start vorbereitet.
5.3.3 Der Run-Loop
Die Aktivität eines Thread Objektes wird in der run() Methode der Klasse definiert. Diese kann
dann nach dem Erzeugen des Objektes mit start() gestartet werden.
In diesem Fall wird, solange Variable “end” des BitTorrentClient Objektes False ist, eine Methode
handle_queue() aufgerufen, danach mit der Methode handle_alert() die Meldungen des session
Objektes verarbeitet und danach eine Sekunde gewartet.
Listing 5.3: handle_queue() Methode
def handle_queue(self):
if self.has_messages():
topic, args, kwargs = self.get_message()
try:
f = getattr(self, 'on_%s' % topic)
18
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Fig. 5.3: BitTorrent run() Loop (1) (Fortsetzung in Abb. BitTorrent run() Loop (2) (page 21))
5.3. BitTorrent
19
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
f(*args, **kwargs)
except Exception as e:
logger.error('something went wrong when calling on_%s: %s
˓→' % (topic, e))
handle_queue() überprüft, ob Nachrichten vorliegen: die von Subscriber geerbte Methode
get_message() wird aufgerufen und das Ergebnis in die Variablen “topic”, “args”, “kwargs”
geschrieben. Es folgt ein try-except Block, in dem versucht wird, eine Methode mit dem Namen “on_” verknüpft mit “topic” und “args” als Argumente und “kwargs” als Named Arguments
aufzurufen. Wie für Python Methoden üblich sollte args eine Liste sein, kwargs ein Dictionary.
Ein Beispiel zur Funktion:
get_message() liefert als topic den String “test”, als args = [2, 4] und als kwargs
= {‘name’: ‘Peter’}. Dann wird im try-Block eine Funktion mit Namen “on_test”
gesucht und der Variable f zugewiesen. In dieser Klasse würde an dieser Stelle schon
eine Exception geworfen und eine Fehlermeldung ausgegeben werden. Wäre die
Funktion vorhanden, würde dann on_test(2, 4, name=’Peter’) aufgerufen werden.
So können alle Funktionen die mit “on_” beginnen “von außen” genutzt werden. Beispielsweise
kann ein neuer Torrent per SHA1 Hash über die Methode on_add_hash() hinzugefügt werden. In
dieser würde dann ein neuer Torrent angelegt und entsprechende IP-Adressen und Ports hinzugefügt, unter denen der Torrent zu finden ist. Dazu müssen natürlich in der XMPP Komponente die
entsprechenden Informationen gesammelt worden sein.
In der handle_alert() Methode wird jeweils eine Meldung der Session verarbeitet. So wird zum
Beispiel bei einem “torrent_update_alert” eine Nachricht mit topic “publish_shares” erzeugt,
was den XMPP Client veranlassen würde, eine Liste der aktuellen Torrents zu senden. Ein
“portmap_alert” wäre zu erwarten, wenn ein Port per NAT gemapped wurde. In diesem Fall würde
eine Nachricht auf topic “set_port” mit dem externen Port als Argument erzeugt.
5.3.4 Beenden des Run-Loops
Wird on_exit() aufgerufen, wird die “end” Variable auf True gesetzt und das saubere Beenden
des Threads wird eingeleitet. Als erstes werden alle Einträge aus der SQLite Datenbank entfernt,
damit nur Torrents, die noch Teil der Session sind, gespeichert werden können. Dann wird für
jeden Torrent das Erzeugen der “resume data” angetriggert.
Danach läuft eine Schleife, solange noch Torrent Handles vorhanden sind. Da für jeden Torrent ein “save_resume_data_alert” erwartet wird, kann im Handling dieses Alerts der Torrent in
die SQLite Datenbank gespeichert und aus der Session entfernt werden. Wird stattdessen ein
“save_resume_data_failed_alert” empfangen, wird der Torrent ohne zu speichern aus der Session
entfernt. Das kommt vor, wenn ein Torrent neu hinzugefügt wurde und das Programm beendet
wird, bevor genug Daten geladen wurden um ein komplettes Torrent File zu erzeugen.
Um nun eine Übersicht der eigenen Torrents zu versenden und Daten über andere Torrents zu
20
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Fig. 5.4: BitTorrent run() Loop (2)
empfangen, wird die XMPP Komponente benötigt, die im folgenden Kapitel beschrieben wird.
5.4 XMPP
Im vorigen Kapitel BitTorrent (page 16) wurde die Implementierung eines BitTorrent Clients
beschrieben, der eine Liste der zu verteilenden Torrents generiert, und der andererseits die IP
Adressen und Ports der zu downloadenden Torrents benötigt.
Die XMPP Komponente muss nun also diese Liste inklusive der eigenen IP Adressen an alle Kontakte verteilen und außerdem eine Liste der empfangenen Torrents und der entsprechenden Quellen
führen.
Das hier verwendete Python Modul SleekXMPP bietet hier die Möglichkeit diese Funktionen in
einem Plugin zu implementieren, das in einem ansonsten übersichtlichem XMPP Client geladen
werden kann.
Die folgenden Kapitel beschreiben die Stanzas, in denen die benötigten Informationen übertragen
werden sollen, sowie den Aufbau des Plugins. Danach wird das Einbinden in den XMPP Client
erläutert.
5.4. XMPP
21
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
5.4.1 Benötigte Stanzas
Die benötigten Informationen umfassen mehrere gekapselte Elemente.
Es wird davon ausgegangen, dass ein XMPP Account an mehreren Ressourcen zur gleichen Zeit
online ist. Diese wiederum haben sehr wahrscheinlich unterschiedliche IP Adressen und Ports und
bieten verschiedene Torrents an.
Daraus ergibt sich folgende Struktur der Daten (hier als Beispiel in Pseudo-XML):
Listing 5.4: Beispiel der XML-Struktur
<Ressourcen>
<1. Ressource>
<Addressen>
<addresse ip='1.1.1.1' port=11>
<addresse ip='2.2.2.2' port=22>
</Addressen>
<Shares>
<share hash='123123123' name='beispiel1' size=123>
<share hash='234234234' name='beispiel2' size=234>
</Shares>
</1. Ressource>
...
<n. Ressource>
<Addressen> ... </Addressen>
<Shares> ... </Shares>
</n. Ressource>
</Ressourcen>
Diese logische Verschachtelung wurde in den folgenden Stanzas abgebildet.
Jede Stanzaklasse wurde von ElementBase abgeleitet, der Basisklasse für Stanzas aus SleekXMPP.
Mithilfe dieser können die XML Elemente einfach als Klassen und Attribute von Klassen behandelt
werden, ohne das XML als String behandelt werden muss.
Das “äußerste” Stanza ist das UserShareStanza. Diesem Container Stanza können über die Methode add_resource() Ressourcen, also angemeldete XMPP Clients als Endpunkte, hinzugefügt werden. In diesem ResourceStanza können nun per add_address() und add_share() AddressStanzas
und ShareItems eingebettet werden.
Die Verknüpfung der jeweiligen Stanzas erfolgt dabei aus dem jeweils übergeordnetem Stanza.
22
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Fig. 5.5: Klassendiagramm der benötigten Stanzas
Listing 5.5: UserShareStanza mit add_resource() Methode
class UserSharesStanza(ElementBase):
name = 'user_shares'
namespace = 'https://xmpp.kwoh.de/protocol/shares'
plugin_attrib = 'user_shares'
def add_resource(self, resource=''):
[...]
resource_stanza = ResourceStanza(None, self)
resource_stanza['resource'] = resource
return resource_stanza
Hier wird in der Methode add_resource() ein neues ResourceStanza erzeugt. “ResourceStanza(None, self)” verknüpft das neu erstellte Stanza mit “self”, dem UserSharesStanza.
Der Namespace ist hier Erkennungsmerkmal aller zum Plugin gehörigen Stanzas und wird genutzt
um eingehende Stanzas dem Plugin zuzuordnen.
Diese Stanzastruktur wird vom im folgenden Kapitel beschriebenen Plugin benutzt.
5.4.2 Aufbau des Plugins
Im SleekXMPP Plugin wird nun die beschriebene Datenstruktur benutzt, um die zu verteilenden
Daten zu senden bzw. auszulesen.
Jedes SleekXMPP Plugin wird implementiert, indem eine neue Klasse aus der SleekXMPP Klasse
BasePlugin abgeleitet wird und in dieser die benötigten Methoden überschrieben werden.
5.4. XMPP
23
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Fig. 5.6: Klassendiagramm XMPP Erweiterung
24
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Hier wird eine neue Klasse UserShares erstellt und die Methoden plugin_init() und plugin_end()
überschrieben. Diese werden später vom Client beim Starten bzw. Beenden des Plugins ausgeführt.
Außerdem wurden hier die Methoden publish_shares() und stop() implementiert.
publish_shares() wird aufgerufen sobald der Client startet, außerdem wenn Änderungen an den
Torrents oder des BitTorrent Clients stattfinden, beispielsweise falls ein neuer Torrent hinzugefügt
wird oder sich der NAT Port ändert.
on_shares_publish() hingegen stellt das Gegenstück zu publish_shares() dar: diese Methode soll
das Empfangen der Daten abwickeln.
Hier soll ein Plugin implementiert werden, das auf dem bereits in Kapitel Zusammenhänge und
Grundlagen (page 7) beschriebenen Personal Eventing Protocol (PEP) aufsetzt.
Aufgrund der Funktionalität vom PEP müssen Informationen nur gesendet werden, wenn sich etwas an den zu verteilenden Daten ändert. Der XMPP Server wird selbst dafür sorgen, das Clients,
die zur Laufzeit erst online gehen, die aktuellen Daten bekommen und im Falle von Aktualisierungen alle betreffenden Clients ein Update erhalten.
Dabei muss beachtet werden, dass eine Limitierung vom PEP umgangen werden muss: es werden
keine multiplen Ressourcen pro Account unterstützt. Da allerdings bei der Anmeldung eine Liste
der bisherigen veröffentlichten Daten vom Server gesendet wird, auch an den eigenen Account,
kann diese Liste einfach um die neue Ressource erweitert werden.
5.4.3 Start des Plugins
Listing 5.6: plugin_init() Methode
def plugin_init(self):
register_stanza_plugin(
UserSharesStanza, ResourceStanza, iterable=True)
register_stanza_plugin(
ResourceStanza, ShareItemStanza, iterable=True)
register_stanza_plugin(
ResourceStanza, AddressStanza, iterable=True)
self.xmpp['xep_0163'].register_pep('shares', UserSharesStanza)
self.xmpp.add_event_handler('shares_publish', self.on_shares_
˓→publish)
Wird das Plugin vom Client geladen, wird zuerst die plugin_init() Methode aufgerufen. In dieser
werden die vom Plugin genutzten Stanzas registriert und das UserShares Stanza unter dem Namen
“shares” im PEP Plugin registriert. Das PEP Plugin wird daraufhin den Namespace des UserShares Stanzas als unterstütztes Feature der Service Discovery hinzufügen. Auf diese Art werden
nur solche Clients die Informationen erhalten, die das Plugin unterstützen. Außerdem werden in
register_pep() die Events “shares_publish” und “shares_retract” angelegt.
5.4. XMPP
25
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Als nächstes wird ein Event Handler für shares_publish registriert. In der damit verknüpften Methode on_shares_publish() soll das Empfangen und Einpflegen der Daten erfolgen.
5.4.4 Empfangen von Daten
Wird nun ein UserShare Stanza empfangen, wird über den Namespace identifiziert, dass das UserShare Plugin dafür zuständig ist, und die zugehörige Methode on_shares_publish() wird mit dem
Stanza als erstem Argument aufgerufen.
Diese Informationen werden in einem Objekt der Klasse ContactShares der Models gehalten.
Diese dient als Wrapper um ein Python Dictionary und bietet einige von der Datenstruktur abstrahierte Funktionen wie get_resource(jid, resource), die für einen bestimmten User die Daten
einer bestimmten Ressource liefert. Außerdem wurden mit threading.Lock Sperren gegen den
Zugriff aus mehreren Threads zur gleichen Zeit implementiert.
Listing 5.7: Handling des Datenempfangs
@staticmethod
def on_shares_publish(msg):
""" handle incoming files """
incoming_shares = msg['pubsub_event']['items']['item']['user_
˓→shares']
logger.info('%s' % incoming_shares)
contact_shares.clear(msg['from'])
for resource in incoming_shares['resources']:
[...]
for item in resource['share_items']:
logger.info('adding share %s to resource %s' % (item['name
˓→'], resource['resource']))
contact_shares.add_share( msg['from'],
resource['resource'],
item['hash'],
item['name'],
item['size'])
for address in resource['ip_addresses']:
contact_shares.add_address( msg['from'],
resource['resource'],
address['address'],
address['port'])
publish('recheck_handles')
In der on_shares_publish() Methode werden dann zuerst alle bislang vorhandenen Daten gelöscht,
26
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
da davon ausgegangen wird, dass in dem erhaltenen Paket alle aktuellen Daten vorhanden sind. Daraufhin wird über die gesendete Liste an Ressourcen iteriert. Jede Ressource sollte “share_items”,
also Informationen über Torrents, und mindestens eine IP-Adresse mit Port haben.
Wurde das Datenpaket verarbeitet, wird eine Nachricht ohne Argumente auf Topic
“recheck_handles” geschickt. Das wiederum hat zur Folge, dass im BitTorrent Client über alle
eigenen Torrents iteriert und überprüft wird, ob neue Quellen für einen der eigenen Torrents vorliegen.
Auf diese Art können zur Laufzeit neue Quellen zu vorhandenen Torrents hinzugefügt werden.
Außerdem liegt eine durchsuchbare Datenstruktur vor, die beispielsweise von Frontends benutzt
werden kann um die empfangenen Torrentlisten anzuzeigen.
5.4.5 Versenden der Daten
Das Versenden der Daten wird in der Methode publish_shares() abgewickelt. Diese soll, wenn
aufgerufen, eine aktuelle Liste der Torrents, verpackt in die definierten Stanzas versenden.
Hier muss darauf geachtet werden, dass nicht nur eine Liste der aktuellen Torrents gesendet wird.
Es müssen außerdem die bereits empfangenen Torrents anderer Ressourcen des eigenen Accounts
mit einbezogen werden.
Dazu wird die Tatsache genutzt, dass nach dem Senden auch immer eine Liste der eigenen Torrents
empfangen wird. Das hat zur Folge, dass in derselben Datenstruktur, in der auch die Torrent Daten
anderer Nutzer gespeichert werden, die eigenen Daten vorliegen.
Es muss also nur noch der eigene Useraccount aus der Liste ausgelesen und die Daten der lokalen
Ressource aktualisiert werden.
Danach wird die bereits erläuterte Struktur aus Stanzas entsprechend der Daten erstellt und
gesendet.
5.4.6 Aufbau des Clients
Das beschriebene Plugin soll nun von einem XMPP Client genutzt werden. Hierfür wird eine neue
Klasse XmppClient aus der SleekXMPP Klasse ClientXMPP und der bereits im BitTorrent Client
genutzten Klasse Subscriber abgeleitet. (Abb. Klassendiagramm XMPP (page 28))
ClientXMPP bringt hierbei schon alle zum Verbinden benötigten Voraussetzungen mit. Initalisiert
wird das Objekt im XmppClient Konstruktor mit der JID und dem benötigten Passwort.
Listing 5.8: registrieren der benötigten Plugins
self.register_plugin('xep_0030') # service discovery
self.register_plugin('xep_0115') # entity caps
self.register_plugin('xep_0163') # pep
self.register_plugin('shares', module=share_plugin)
5.4. XMPP
27
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Fig. 5.7: Klassendiagramm XMPP
28
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Danach werden die benötigten Erweiterungen registriert, die bereits Teil von SleekXMPP sind:
Service Discovery, Entity Caps und PEP. Auch das UserShares Modul wird, wie die anderen Plugins, über register_plugin() registriert. Hier wird allerdings noch auf das vorher importierte Modul
verwiesen, da dieses nicht Teil von SleekXMPP ist.
Außerdem wird im Konstruktor das “session_start” Event mit einer Methode start() der Klasse
verknüpft. Hier wird nach dem Verbinden die eigene Präsenz gesendet und der Roster, also die
Kontaktliste, empfangen.
In dieser Grundkonfiguration wäre der Client grundsätzlich schon betriebsbereit. Allerdings fehlt
noch jegliche Art der Interaktion mit anderen Komponenten der Anwendung.
Daher wird im Konstruktor noch ein Scheduler hinzugefügt, der zyklisch die vom Subscriber
geerbte Message Queue verarbeitet. Dies erfolgt auf dieselbe Art wie schon im BitTorrent Client:
alle mit “on_” beginnenden Methoden werden automatisch als Topic abonniert und werden in der
verknüpften Methode aufgerufen, wenn die entsprechenden Nachrichten vorliegen.
Außerdem werden im Konstruktor die anderen Komponenten der Anwendung gestartet: der BitTorrent Client und eine im Kapitel Web (page 29) näher beschriebene JSON-RPC API mit einem
Web Frontend zur Übersicht über die Torrents.
Da die eigene IP Adresse Teil der zu versendenden Datenpakete ist, wird hier außerdem ein Prozess
angestoßen, der die eigene IPv4 Adresse herausfinden soll. Da diese hinter einem DSL Router im
lokalen Netz nicht bekannt ist, wurde hier das Modul ipgetter genutzt. In diesem sind eine Reihe
an Servern hinterlegt, die die IP zurück geben, von der die Anfrage kommt.
Die IPv6 Adresse kann jedoch aus dem System ausgelesen werden. Hierfür kommt das Modul
netifaces zum Einsatz, das betriebssystemunabhängig die momentanen IP Adressen auslesen kann.
Der so konstruierte Client ist somit der Hauptteil der Anwendung. Aus ihm heraus werden die
anderen Teile der Anwendung kontrolliert gestartet. Dadurch, dass wesentliche Funktionalität in
das Plugin ausgelagert wurde, ist er übersichtlich, aber um neue Funktionen erweiterbar ohne die
Funktion des Plugins zu beeinflussen.
Im folgenden Kapitel wird die Web Komponente beschrieben, die einerseits eine minimale Weboberfläche zur Übersicht darstellt, aber auch eine JSON-RPC API zur Verfügung stellt, über die
eventuelle Frontends mit der Anwendung kommunizieren können.
5.5 Web
Die Web Komponente soll nun, nachdem die Basisfunktionalität seitens der Datenübertragung
implementiert ist, eine Schnittstelle für Nutzer und Frontends zur Steuerung bieten.
Um das Programm auch auf entfernten Rechnern steuern zu können, wurde hier die Variante einer
JSON-RPC API gewählt. Außerdem wurde ein minimales Web Frontend implementiert um bereits
erhaltene Torrentlisten und eigene Torrents darzustellen. Dafür wurde das Web Framework Flask
bzw. das Flask Plugin Flask-JSONRPC genutzt.
5.5. Web
29
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Eine minimale Flask Anwendung ist dabei sehr einfach strukturiert. Erst wird ein Flask-Objekt
erzeugt, welches dann Methoden zur Verfügung stellt, die wiederum als Decorator für Funktionen
genutzt werden.
Listing 5.9: Flask Beispiel [fla] (page 53)
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
In diesem Beispiel wird ein Objekt “app” der Klasse Flask erzeugt. Daraufhin wird die Funktion
hello() mit @app.route(“/”) dekoriert, was zur Folge hat, dass wenn die Anwendung mit app.run()
lokal gestartet wird, beim Aufruf von “http://localhost:5000/” in einem Browser der String “Hello
World!” ausgegeben wird. 5000 ist hier der Standardport von Flask und kann bei Bedarf angepasst
werden.
5.5.1 Aufbau der Komponente
Fig. 5.8: Klassendiagramm Web
Da auch dieser Teil parallel zum XmppClient und dem BitTorrentClient laufen muss, soll das appObjekt in einem neuen Thread gestartet werden.
30
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Listing 5.10: Web initalization
app = Flask(__name__)
[...]
class Web(Thread):
def __init__(self, api_host='localhost', api_port=8080):
super(Web, self).__init__()
self.api_port = api_port
self.api_host = api_host
def run(self):
app.run(host=self.api_host, port=self.api_port)
Dazu wird, wie in Web initalization (page 31) zu sehen, auf Modulebene das app-Objekt erstellt
und in einer Klasse genutzt, die später wiederum zusammen mit den anderen Komponenten im
XMPP Client als Thread gestartet werden kann.
Fig. 5.9: Packages Web
Das Modul ist unterteilt in die Submodule api und gui.
Im Modul api sind die Funktionen der JSON-RPC API definiert. Dieses ist wiederum unterteilt in
“bt” und “xmpp”, um die dort definierten Routen entsprechend ihrem Zweck aufzuteilen.
Das gui Modul beinhaltet Routen und Ressourcen des Web Frontends. Dieses bietet aber nur
Funktionen um eigene Torrents und gesammelte Shares anzuzeigen. Es ist als Übersicht gedacht
und stellt keineswegs eine komplette Schnittstelle zu allen Funktionen dar.
5.5.2 Das api Modul
5.5. Web
31
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Listing 5.11:
initalisieren des
(bitween/components/web/__init__.py)
jsonrpc
Objekts
und
Import
der
Funktionen
[...]
app = Flask(__name__)
jsonrpc = JSONRPC(app, '/api', enable_web_browsable_api=enable_web_api)
from .api import versions, safe_exit, get_all_torrents
from .api.bt import [...]
from .api.xmpp import [...]
Das api Modul basiert auf der Flask Erweiterung Flask-JSONRPC. Diese wird mit dem app Objekt
und einem Prefix für die gewünschten Routen initialisiert.
Die entsprechenden Funktionen werden dann aus dem Submodul importiert.
Listing 5.12: Definition einer JSON-RPC Funktion (bitween/components/web/api/__init__.py)
from .. import jsonrpc
[...]
@jsonrpc.method('Api.versions')
def versions():
import libtorrent
import sleekxmpp
versions = {"libtorrent": '' + libtorrent.version,
"sleekxmpp": '' + sleekxmpp.__version__}
logger.debug(versions)
return versions
[...]
Das Submodul importiert dann das jsonrpc Objekt. Hier ist wichtig zu beachten, dass diese Imports
erst nach dem Erstellen des Objektes im übergeordneten Modul auszuführen sind. Die Funktion
selbst implementiert die Abfrage der verwendeten libtorrent und SleekXMPP Funktionen. Dazu
wird ein Dictionary erstellt, das als JSON String zurückgegeben und von Flask versendet werden
kann.
Aufgerufen werden die so implementierten Funktionen dann mit einem HTTP POST auf die Route
“http://ip:port/api” mit einem JSON Payload in folgendem Format:
Listing 5.13: Format des JSON Payloads
{
"jsonrpc": "2.0",
"method": "Api.versions",
"params": {},
"id": "1234"
}
32
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
In diesem Beispiel wird die oben beschriebene Methode “Api.versions” ohne Parameter
aufgerufen. Die ID ist eine zufällige Nummer, die der Antwort ebenfalls als “id” angehangen
wird, um den Aufruf zuordnen zu können.
Auf diese Art wurden folgende Funktionen eingefügt:
Aufruf
Api.versions
Api.exit
bt.get_torrents
bt.add_path
Parameter
–
–
–
path
bt.add_torrent_by_hash
hash,
save_path
bt.del_torrent
hash
xmpp.get_hashes –
xmpp.get_shares
–
Funktion
gibt die Verwendeten Versionsnummern zurück
leitet das saubere Beenden der Anwendung ein
listet die eigenen Torrents auf
generiert einen neuen Torrent aus Datei oder Verzeichnis
unter <path>
legt einen neuen Torrent anhand von <hash> an, speichert
nach <save_path>
löscht Torrent mit Hash <hash>
liefert eine Liste mit aggregierten Hashes und gefundenen
Endpunkten
liefert eine Liste aller Kontakte und deren Shares
5.5.3 Das gui Modul
Mit dem gui Modul wurde ein Interface implementiert, über das der User eine Übersicht über die
gefundenen und eigenen Torrents bekommen kann. Dies dient allerdings eher als Beispiel. Hier
wurde keine komplette Nutzerschnittstelle geschrieben, sondern lediglich genug Funktionalität um
schnell eine Übersicht bekommen zu können.
Diese Funktionen wurden gekapselt als Flask Blueprint und können somit für spätere Versionen
leicht entfernt oder weiterentwickelt werden. Hier soll deshalb nur ein kurzer Überblick über das
bisherige Vorgehen gegeben werden.
Listing 5.14: Setup des gui Blueprints (bitween/components/web/gui/__init__.py)
from flask import Blueprint
gui = Blueprint('gui', __name__, template_folder='templates', static_
˓→folder='static')
from . import views, errors
Es wird ein neues Blueprint Objekt gui erstellt. Dieses wird benötigt um im nächsten Schritt die
Routen zu importieren, da diese wiederum mit der gui.route() Funktion dekoriert werden.
Listing 5.15: Index Funktion des gui Blueprints (bitween/components/web/gui/views.py)
@gui.route('/', methods=['GET'])
def index():
5.5. Web
33
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
[...]
return render_template('gui_index.html', torrents=handles.get_
˓→shares())
Diese Beispielroute für die Index Route “/” wird nur für die GET Methode definiert. Es wird
eine neue Liste der eigenen Torrents erstellt und als “torrents” zusammen mit dem Template
“gui_index.html” (im Unterordner “templates”) an die Funktion render_template() übergeben, die
daraufhin einen String mit dem HTML Code generiert, der wiederum zurückgegeben und von
Flask ausgeliefert wird.
Listing 5.16: Registrieren des Blueprints am app Objekt
from .gui import gui as gui_blueprint
[...]
app.register_blueprint(gui_blueprint)
Registriert wird der Blueprint dann am app Objekt über die Funktion register_blueprint() mit dem
importierten Blueprint als Parameter.
5.6 Inter-Process Communication
Die Kommunikation zwischen den Threads wurde durch eine Publish-Subscribe Pattern gelöst.
Wie bei Publish-Subscribe des XMPP Protokolls können Teilnehmer (in diesem Fall Objekte der
jeweiligen Klassen) Nachrichten zu bestimmten Topics abonnieren (“subscriben”). Außerdem
steht eine “publish” Methode zur Verfügung, mit der Nachrichten auf bestimmten Topics veröffentlicht werden können.
Hierzu wurde eine Klasse “Subscriber” implementiert, die als Basisklasse für alle anderen Klassen
dient, die Nachrichten empfangen. Jedes Subscriber-Objekt besitzt eine Queue, die alle noch unverarbeiteten Nachrichten enthält, eine subscribe() Methode um Nachrichten zu Topics zu “Abonnieren” sowie eine has_messages() und get_messages() Methode um den Zustand der Queue
abzufragen und Nachrichten zu entnehmen.
Im folgenden Diagramm ist außerdem eine Klasse “AutoSub” zu sehen, die dazu dient, die PubSub
Klasse zu testen, und die gleichzeitig als einfaches Beispiel dienen soll, wie eine Klasse einige
ihrer Methoden direkt als Topics abonnieren kann. Hierauf wird am Ende dieses Kapitels genauer
eingegangen.
Im einfachsten Fall wird ein Subscriber Objekt ohne Parameter erstellt. Dann wird nur eine
Nachrichtenqueue angelegt und es können Topics mit subscribe(‘topicname’) abonniert werden.
34
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Listing 5.17: Subscriber abonniert “some_topic”
s = Subscriber()
s.subscribe('some_topic')
Wird daraufhin die Methode publish() eines Objekts der Subscriber Klasse oder die Funktion publish() des pubsub Moduls mit ‘topicname’ als erstem Argument aufgerufen, wird eine Nachricht
im Queue Objekt der entsprechenden Klasse hinterlegt.
Die Grafik concept-pubsub soll dieses Konzept verdeutlichen. Hier sind subscriber_A und subscriber_B Abonnenten des “topic_A”. Wird nun im ersten Schritt publish() mit den Argumenten
‘topic_A’, 12, ‘test’ aufgerufen. Dann wird im zweiten Schritt im Modul die die Zuordnung aus
dem topics Dictionary gelesen, das diese während der Laufzeit speichert. Hier hat “topic_A” die
Subscriber subscriber_A und subscriber_B. Das Topic und die Argumente werden daraufhin in die
Queues der beiden Objekte gelegt.
5.6.1 Automatisches Abonnieren von Topics
Listing 5.18: automatisches subscriben von Topics
1
2
3
4
5
6
class Subscriber:
def __init__(self, name='', autosubscribe=False):
[...]
if autosubscribe:
listen_to = [x for x, y in self.__class__.__dict__.items()
˓→if
(type(y) == FunctionType and x.startswith('on_
˓→'))]
5.6. Inter-Process Communication
35
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
for l in listen_to:
self.subscribe(l.split('on_')[1])
7
8
Eine interessantere Anwendung ergibt sich, wenn eine Subklasse von Subscriber erstellt und autosubscribe mit True aufgerufen wird, wie in Codebeispiel automatisches subscriben von Topics
(page 35) zu sehen. In diesem Fall wird erst eine Liste mit allen Methoden erstellt, deren Name
mit “on_” beginnt (Zeile 5 und 6). Dann wird über die Liste der gesammelten Namen iteriert: das
“on_” am Anfang wird abgeschnitten und der resultierende String wird als Topic abonniert.
Damit besteht die Möglichkeit, Methoden der Klassen direkt als Topics zu abonnieren und es
entfällt das händische Zuordnen von Topics und Funktionsaufrufen.
Als Beispiel hierzu dient die folgende Klasse AutoSub, die sich von Subscriber ableitet.
Listing 5.19: AutoSub Klasse
class AutoSub(Subscriber):
def __init__(self):
Subscriber.__init__(self, autosubscribe=True)
def process_messages(self):
if self.has_messages():
topic, args, kwargs = self.get_message()
try:
f = getattr(self, 'on_%s' % topic)
f(*args, **kwargs)
36
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
except Exception as e:
logger.error('something went wrong when calling on_%s: %s
˓→' % (topic, e))
def on_some_topic(self, some_string, some_int=1):
print('some_string is %s' % some_string)
print('some_int is %s' % some_int)
Die Subklasse mit einer Scheduling Methode wie der hier gezeigten process_messages() und der
on_some_topic() Methode würde dann also automatisch das Thema “some_topic” abonnieren, da
hier eine Methode namens “on_some_topic” definiert wurde. Wird dann eine Nachricht in diesem
Topic abgelegt, würde während des Schedulings on_some_topic() mit den Argumenten aus der
Nachricht aufgerufen.
In einer Python Shell sieht das ganze wie folgt aus:
Listing 5.20: Benutzen der AutoSub Klasse
>>> s = AutoSub()
>>> publish('some_topic', 'teststring')
True
>>> s.process_messages()
some_string is teststring
some_int is 1
Somit ist es möglich, in Subklassen von Subscriber abonnierte Topics direkt mit Methoden zu
verknüpfen, ohne dabei das Scheduling anpassen zu müssen.
Das wird von den bereits erläuterten Klassen BitTorrentClient und XmppClient genutzt, um
Nachrichten über die entsprechenden Threads hinweg zu senden und zu empfangen.
Eine Übersicht über alle Topics und deren Subscriber befindet sich im Anhang.
5.7 Abschluss der Implementierung
5.7.1 Start Skript
Nachdem nun die wesentlichen Komponenten beschrieben wurden, fehlt noch ein Skript, das
die Anwendung in der gewünschten Konfiguration startet. Hierfür wurde das Skript bitweend.py
geschrieben.
Die Basiskonfiguration der Anwendung wird in einer Json Datei abgelegt, die als “conf.json” im
Verzeichnis bitween gesucht wird, oder, falls dort nicht vorhanden, im Home Verzeichnis des
Benutzers unter dem Namen ”.bitween.json”.
Dann kann bitweend gestartet werden. Hier hat man zusätzlich die Möglichkeit mit dem Argument
5.7. Abschluss der Implementierung
37
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
“–debug” das Loglevel auf Debugausgaben zu setzen und die API mittels “–port” und “–bind” an
einen Port und IP Adresse zu binden. Dies ist sinnvoll, wenn das Programm auf einem entfernten Rechner läuft und von außerhalb bedient werden soll, da der Defaultwert für die IP Adresse
“localhost” und die API damit nur für denselben Rechner erreichbar ist, auf dem die Anwendung
läuft.
5.7.2 Cmd-Client
Außerdem wurde ein Kommandozeilenclient entworfen, um die grundlegenden Funktionen der
Anwendung zu bedienen. Diese umfassen Pfade als Torrent freigeben, gefundene Freigaben auflisten und Freigaben anhand von Hashsummen downloaden. Genutzt wird hierfür die Python
Libary Requests, um Befehle an die JSON-RPC API der Anwendung zu übermitteln.
5.7.3 setup.py
Um diese Anwendung mit den Python setuptools bzw. dem Paketmanager pip installierbar zu
machen, wurde außerdem eine Datei setup.py im Wurzelverzeichnis des Projekts angelegt.
Wie in Code Ausschnitt aus setup.py (page 38) zu sehen werden der Funktion setup() der Python
setuptools einige Informationen über das Programm übergeben.
Listing 5.21: Ausschnitt aus setup.py
[...]
install_reqs = parse_requirements("requirements.txt", session=False)
reqs = [str(ir.req) for ir in install_reqs]
[...]
setup(
name='bitween',
version='0.0.1',
description='experimental XMPP/BT Client',
long_description=readme,
author='Jan Hartmann',
url='https://github.com/puhoy/bitween',
# license=license,
packages=find_packages(exclude=('tests', 'docs')),
test_suite="tests",
entry_points={
'console_scripts': [
'bitweend=bitween.bitweend:main',
'bitweenc=bitween.bitweenc:main'
]
38
Chapter 5. Implementierung
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
},
install_requires=reqs
)
Hier werden etwa die benötigten Python Pakete aus der Datei “requirements.txt” eingelesen, Variablen wie der Name des Programms, die Version und der Autor. Außerdem werden Entrypoints
übergeben. “bitweend” kann daraufhin nach der Installation ausgeführt werden und verweist auf
die Funktion main() im bitweend Skript. Analog dazu wird ein Entrypoint für “bitweenc” angelegt.
Die Installation kann dann mit dem Aufruf von “pip install -e pfad/zum/projekt” erfolgen.
5.7.4 Dokumentation
Für die gesamte Anwendung wurde Dokumentation in Form von Docstrings an allen Funktionen,
Methoden, Modulen und Klassen verfasst. Diese sind im reStructuredText Format gehalten.
Um diese Dokumentation übersichtlich dar zu stellen, kann aus den Docstrings mit dem Dokumentationsgenerator Sphinx (www.sphinx-doc.org [Ove] (page 53)) eine Dokumentation in anderen
Formaten wie HTML oder PDF erstellt werden.
Die Konfiguration von Sphinx geschieht dabei über die Datei conf.py im Verzeichnis docs. Mit
dem Skript build_docs.sh im Wurzelverzeichnis des Projekts kann dann das automatisierte Erstellen der benötigten Dokumentationsdateien angestoßen werden. Diese sind untereinander logisch verkettet und können so in späteren Formaten wie HTML verlinkt werden.
Außerdem wurde eine Datei index.rst geschrieben, die als Einsprungpunkt in die automatisch
generierte Dateistruktur dient.
5.7.5 Integration in andere Dienste
Dadurch, dass dieses Projekt in Git versioniert und auf GitHub, einem Git Hostingdienst, entwickelt wurde, war es naheliegend, darauf basierende weiterführende Dienste zu benutzen. So wurden
drei externe Dienste in dieses Projekt integriert:
ReadTheDocs (readthedocs.io [Wel] (page 53)) um automatisch Dokumentationen in HTML
aus den Docstrings des Programms zu erstellen und zu hosten. Dabei wird nach jedem
“git push” auf den Server ein Webhook ausgelöst, der das Erstellen einer neuen Version
der Dokumentation antriggert. Zu finden ist diese Dokumentation unter http://bitween.
readthedocs.io/en/develop/ und auf der beiliegenden CD.
Travis-CI (travis-ci.org [puha] (page 54)) für automatisierte Unittests. Diese werden ebenfalls
per Webhook vom Server ausgelöst. So wird jeder Commit automatisch getestet. Außerdem
wird eine History über vergangene Tests geführt.
Coveralls (coveralls.io [puhb] (page 54)) das die prozentuale Abdeckung des Codes durch die
Testfälle darstellt. Dieser erhält die Testabdeckung von Travis-CI nach jedem Test. Auch
5.7. Abschluss der Implementierung
39
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
hier wird eine History bis auf die Ebene einzelner Dateien erstellt. Außerdem wird grafisch
dargestellt, welche Zeilen einer Datei ausgeführt wurden.
40
Chapter 5. Implementierung
CHAPTER 6
Beurteilung der Ergebnisse
Während der Implementierung traten eine ganze Reihe an Problemen größerer und kleinerer Natur
auf, die so nicht erwartet wurden.
6.1 Vor- und Nachteile der serverlosen Dateiübertragung
Durch die serverlose Dateiübertragung per BitTorrent umgeht man zwar potentiell langsame
Server, verliert aber auch einen “Mittelsmann” für die Übertragung. Befinden sich beispielsweise
beide Teilnehmer hinter einem DSL Router, müssen beide Techniken zum Port öffnen unterstützen
(oder manuell Ports öffnen) um eine Kommunikation in beide Richtungen zu ermöglichen. Außerdem müssen nätürlich beide Parteien dasselbe Protokoll sprechen. Hat ein Teilnehmer eine IPv4
Adresse und ein anderer eine IPv6 Adresse, werden diese zwar gegenseitig ihre Torrentlisten erhalten. Allerdings wird nie eine Datenübertragung zustande kommen, da diese vom XMPP Server
übermittelt wird. Zum Teil werden diese Probleme aufgefangen, wenn sich die Teilnehmerzahl
erhöht, aber trotzdem werden die Übertragungen aufgrund der Beschränkung auf die bekannten
Kontakte nie so reibungsfrei laufen wie “echte” BitTorrent Dateiübertragungen, bei denen ein
Tracker oder das Torrent Netz selbst andere Teilnehmer vermittelt und daher viel mehr Endpunkte
vorhanden sind.
Aus diesem Grund ist der Erfolg dieser Art der Datenübertragung zu einem gewissen Grad von der
Homogenität und Funktionalität des genutzen Netzwerks der Teilnehmer abhängig.
Ebenfalls entfällt mit einem Server eine Instanz, bei der IP Adressen erfragt werden können. Als
Ersatz kam hier ipgetter zum Einsatz, das lediglich aus einer Reihe hinterlegter Server einen zufälligen auswählt und die IP erfragt. Ist dieser Server nicht erreichbar, kommt es hier zu Wartezeiten
beim Starten des Programms.
41
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
6.2 libtorrent
Die libtorrent Libary, die sich selbst als “feature complete” [wwwc] (page 54) bezeichnet, ist vor
allem zu Beginn sehr unübersichtlich. Die Dokumentation bezieht sich auf die C++ Schnittstelle
und verweist auch bezüglich der Python Bindings auf diese Dokumentation, da alle Elemente
dieselben Bezeichnungen haben und ähnlich funktionieren. Trotzdem wurde während der Implementierung zumindest eine Übersicht der zu erwarteten Python Datentypen vermisst.
Dazu kommt eine asynchrone Arbeitsweise, bei der viele Funktionen nur Alerts auslösen, die dann
das Ergebnis enthalten und die das Debugging und Tests erheblich verkomplizieren.
Außerdem existieren Inkompatibilitäten zwischen den Versionen, die in den Changelogs nicht gefunden wurden. So ändert sich beispielsweise die Codecerkennung bei Magnet Links zwischen
Version 0.16.13 (in den Ubuntu 14.04 Paketquellen) und Version 1.1.0 (zu diesem Zeitpunkt aktuell). Da hier keine Warnung gegeben wird, sondern nur ein Torrent mit invalidem Hash angelegt wird, war die Fehlersuche sehr zeitaufwändig. Zur Lösung wurden zwei Funktionen zum
Umwandeln nach UTF-8 aus dem ebenfalls auf libtorrent aufbauenden BitTorrent Client Deluge
übernommen. (siehe bitween/components/bt/helpers.py)
Außerdem exisiert für die libtorrent Installation kein Python Wheel, das die vorkompilierte Libary
enthält. Der Nutzer ist hier darauf angewiesen, entweder selbst zu kompilieren oder möglicherweise alte Versionen zu nutzen, die das Betriebssystem bereitstellt. Auch das ist negativ zu werten,
da es eine Hürde für unerfahrene Nutzer darstellt und somit die Verbreitung einschränkt.
6.3 XMPP Ansätze
Auch die Komplexität vom XMPP und seinen Erweiterungen ist nicht zu unterschätzen. Es wurde
auf 2 Bücher zurück gegriffen, die beide einen Einstieg in XMPP geben und von denen eines
auch ein Codebeispiel für SleekXMPP verfolgt, jedoch wurde hier PEP nicht näher beleuchtet.
Daher bezog sich die genauere Recherche in den meisten Fällen auf die häufig sehr umfassenden
Protokollspezifikationen.
6.4 Threading
Während des Testens war es auffällig, das sich die Anwendung in einigen nicht reproduzierbaren
Fällen nicht komplett herunterfahren ließ. Hier wurden die Threads des BitTorrent Client und der
API Schnittstelle beendet, jedoch lief der XMPP Client weiter. Der Prozess musste in diesen Fällen
von Hand beendet werden. Da die BitTorrent Komponente immer kontrolliert herunter gefahren
wurde, wurden dabei aber alle zu speichernden Daten in die zugehörige Datenbank geschrieben,
sodass kein Datenverlust auftrat.
42
Chapter 6. Beurteilung der Ergebnisse
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
6.4.1 Tests
Aufgrund der Tatsache, dass hier ein Prototyp entwickelt wurde, dessen Aufbau und Konzept sich
unter Umständen noch häufig ändern, wurden ausgiebige Unittests nur für die Datenmodelle und
die Inter-Process Communication implementiert. Diese stellen eher statische Elemente dar, die
sich auch bei neuen Funktionen wenig ändern.
Hierfür wurde der Dienst Travis-CI [puha] (page 54) in das Git Repository des Projektes auf
GitHub (https://github.com/puhoy/bitween [puhc] (page 54)) integriert. Dieser führt bei jedem
neuen Commit des Codes mittels eines Webhooks automatische Unittests aus.
Der Rest der Anwendung wurde manuell getestet. Hierfür wurden zwei Clients gestartet: auf
einem zur Verfügung stehenden Server mit installiertem Debian 8 und auf einem Ubuntu 14.04
bzw. 16.04 System hinter einem DSL Router bei aktiviertem UPNP.
In den Tests wurde auf jeder Instanz eine Datei freigegeben und auf die jeweils andere Instanz
übertragen. Die Tests beschränkten sich in diesem Fall auf das IPv4, IPv6 konnte nicht getestet
werden.
6.4. Threading
43
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
44
Chapter 6. Beurteilung der Ergebnisse
CHAPTER 7
Ausblick
Diese erste Version der Anwendung schöpft bei weitem noch nicht das volle Potential der
Möglichkeiten dieser Technik aus. Es sind sowohl noch Probleme zu lösen, als auch das Programm zu erweitern.
So fehlt zur Zeit die Funktionalität, um mögliche Fehler bei Übertragungen zu erkennen.
Es muss etwa überprüft werden ob mindestens zwei Teilnehmer dieselbe IP Version
verwenden oder ob der Client Probleme hatte Ports am Router zu öffnen. In diesen Fällen
sollten an den Shares Hinweise verteilt werden, sodass ein Client entscheiden kann, welche
Ergebnisse überhaupt angezeigt oder mit Warnungen versehen werden. Genauso sollten
“bevorzugte” Verbindungen implementiert werden. Nutzen beide Teilnehmer einen vollen
IPv4 und IPv6 Stack, könnte man Verbindungen standardmäßig auf IPv6 starten, um IPv4
NAT zu umgehen.
Außerdem werden die IPv4 Adressen in dieser Version ausschließlich über andere Server herausgefunden, die die eigene öffentliche IP Adresse zurückliefern. Ist ein Server aus dieser Liste
nicht erreichbar, wird lange auf ein Timeout der Verbindung gewartet bevor eine nächste Anfrage
gestellt wird. Hier sollte man zusätzlich auf andere Techniken zurückgreifen: BitTorrent nutzt
beispielsweise eine Technik, um bei anderen Peers die IP Adresse zu erfragen. Hierfür sind natürlich andere Peers nötig. Der erste Kontakt in einer Nutzergruppe müsste also weiterhin andere
Techniken nutzen.
Andere mögliche Erweiterungen wären:
• grafischer Client mit Statistiken über Up-/Downloads
• Kontrolllisten für Torrents: nicht jeder Kontakt sollte alle Shares bekommen
• “Backup-Mode”: alle Freigaben anderer Ressourcen des eigenen Accounts automatisch
downloaden
• “Wanted” Listen: Kontakte können gesuchte Hashes als “Wanted” publishen. Werden diese
von anderen Kontakten gefunden, werden diese downloaden und dem ursprünglich Suchenden zur Verfügung stellen
• Usermanagement/passwortgeschützter Login für die API
45
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
• Implementierung einer nativen Python BitTorrent Bibliothek, um für eine einfache Installation nicht auf das vorkompilierte libtorrent angewiesen zu sein.
46
Chapter 7. Ausblick
CHAPTER 8
Zusammenfassung
In der Thesis wurde untersucht, ob es sinnvoll ist, Dateiübertragungen des XMPP Protokolls OutOf-Band über das BitTorrent Protokoll abzuwickeln. Dazu wurde ein XMPP und BitTorrent Client
entworfen und implementiert.
Daraus zeigten sich, neben einigen “Kinderkrankheiten” dieser frühen Version der Anwendung,
auch generelle Probleme dieser Art der Datenübertragung. Durch die serverlose Datenübertragung
fehlt hier eine Instanz, die als Bindeglied zwischen den Clients dient. Das hat zur Folge, dass die
Clients sehr genau konfiguriert sein müssen: alle Teilnehmer müssen dasselbe Internet Protocol
sprechen, sowie gegebenenfalls die Ports am Router konfiguriert und Firewalls eingestellt werden.
Ein Server hingegen könnte als Brücke zwischen IPv4 und IPv6 dienen und über holepunching
Methoden Ports öffnen.
Der entfallende Server ist somit Vor- und Nachteil zugleich: einerseits entfällt hier zentrale Infrastruktur, was das Netzwerk im Ganzen ausfallsicherer und schneller machen kann, andererseits entfällt auch ein “Ansprechpartner” der Verbindungen vermittelt oder als Proxyserver dienen
kann. Demzufolge stellt die Datenübertragung per BitTorrent in gut konfigurierter Umgebung eine
Verbesserung gegenüber der Übertragung über Server dar, für den Endanwender allerdings müsste
das Programm noch sehr viel mehr Funktionalität zur Fehlererkennung mitbringen um mögliche
Verbindungsfehler aufzufangen.
47
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
48
Chapter 8. Zusammenfassung
CHAPTER 9
Anhänge
9.1 Übersicht der IPC Topics
9.2 Inhaltsverzeichnis der CD
• website_snapshots: Kopien der genutzten Webseiten
• thesis: Quellcode, PDF-Version und HTML-Version der Thesis
• bitween: Quellcode und generierte API des Programms
• pubsub_overview.png: Übersicht der genutzten Topics und deren Abonnenten
Todo
• Quellcode der Thesis
• Thesis als PDF
• Thesis als HTML
• Quellcode des Programms
• generierte API Docs HTML
• Übersicht der Topics und Abonnenten
•
49
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
Fig. 9.1: Übersicht der Publisher, Topics und Subscriber
50
Chapter 9. Anhänge
CHAPTER 10
Literaturverzeichnis
References
10.1 Bücher
10.2 URLs
51
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
52
Chapter 10. Literaturverzeichnis
Bibliography
[XMP16] Ref.~\citenum XMPPTheDefinitiveGuide, p.16.
[XMP.8] Ref.~\citenum XMPPTheDefinitiveGuide, p.8.
[pro35] Ref.~\citenum professionalxmpp, p.35.
[Mof10] Jack Moffitt. Professional XMPP Programming with JavaScript and jQuery. Wrox, 2010.
ISBN 0470540710.
[SAST09] Peter Saint-Andre, Kevin Smith, and Remko Tronçon. XMPP: The Definitive Guide:
Building Real-Time Applications with Jabber Technologies. O’Reilly Media, 2009. ISBN
059652126X.
[Ext] Extensible
messaging
and
presence
protocol
(xmpp):
https://xmpp.org/rfcs/rfc3920.html#bind. (Accessed on 07/21/2016).
core.
[imo] Im observatory. https://xmpp.net/directory.php. (Accessed on 07/07/2016).
[jab] Jabber/xmpp server list. http://www.jabberes.org/servers/. (Accessed on 07/07/2016).
[Ove] Overview — sphinx 1.4.5 documentation. http://www.sphinx-doc.org/en/stable/. (Accessed
on 08/18/2016).
[Sch] Scheduler — sleekxmpp. http://sleekxmpp.com/api/xmlstream/scheduler.html. (Accessed
on 07/18/2016).
[hit] Structuring your project — the hitchhiker’s guide to python. http://docs.pythonguide.org/en/latest/writing/structure/. (Accessed on 07/17/2016).
[Wel] Welcome to bitween’s documentation!
— bitween
http://bitween.readthedocs.io/en/latest/. (Accessed on 08/18/2016).
documentation.
[fla] Welcome | flask (a python microframework). http://flask.pocoo.org/. (Accessed on
08/11/2016).
53
Ein File-Sharing-Client auf Basis von XMPP und BitTorrent, Release Bachelorarbeit
[XEPa] Xep-0115: entity capabilities. http://xmpp.org/extensions/xep-0115.html. (Accessed on
07/26/2016).
[XEPb] Xep-0118:
07/26/2016).
user tune. http://xmpp.org/extensions/xep-0118.html. (Accessed on
[XEPc] Xep-0163: personal eventing protocol. http://xmpp.org/extensions/xep-0163.html. (Accessed on 07/18/2016).
[XMPa] Xmpp | history of xmpp. http://xmpp.org/about/history.html. (Accessed on 07/07/2016).
[XMPb] Xmpp | specifications. http://xmpp.org/extensions. (Accessed on 07/26/2016).
[al4] Al45tair / netifaces / pull request #5: add support for retrieving ipv6 address flags on
bsd/mac-os — bitbucket. https://bitbucket.org/al45tair/netifaces/pull-requests/5/add-supportfor-retrieving-ipv6-address/diff. (Accessed on 07/18/2016).
[puha] Puhoy/bitween - travis ci. https://travis-ci.org/puhoy/bitween. (Accessed on 08/17/2016).
[puhb] Puhoy/bitween
|
coveralls
test
coverage
history
https://coveralls.io/github/puhoy/bitween. (Accessed on 08/18/2016).
[puhc] Puhoy/bitween:
a
somewhat
experimental
https://github.com/puhoy/bitween. (Accessed on 08/18/2016).
&
xmpp/bittorrent
statistics.
client.
[wwwa] Www.bittorrent.org/beps/bep_0003.html. http://www.bittorrent.org/beps/bep_0003.html.
(Accessed on 07/25/2016).
[wwwb] Www.bittorrent.org/beps/bep_0009.html. http://www.bittorrent.org/beps/bep_0009.html.
(Accessed on 07/27/2016).
[wwwc] Www.libtorrent.org. http://www.libtorrent.org/. (Accessed on 08/15/2016).
54
Bibliography