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="alice@wonderland.lit"/> <item jid="madhatter@wonderland.lit"/> <item jid="whiterabbit@wonderland.lit"/> </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="queen@wonderland.lit" to="madhatter@wonderland.lit"> <body>Off with his head!</body> </message> S: <message from="king@wonderland.lit" to="party@conference.wonderland.lit"> <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='stpeter@jabber.org/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 ‘stpeter@jabber.org‘ 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