www.eprace.edu.pl » sieci-p2p » Technologia programów P2P w praktyce » Programy oparte o protokół Gnutella

Programy oparte o protokół Gnutella

Istnieje wiele programów służących wymianie plików w Internecie, wyróżniających się interfejsem, rozbudowanym sposobem wyszukiwania plików czy obsługiwanym systemem operacyjnym. Do tej grupy możemy zaliczyć programy oparte o protokół Gnutella.

Historia Gnutelli rozpoczyna się 14 marca 2000 roku, gdy program został umieszczony na stronie firmy Nullsoft (znanej jako producent popularnego odtwarzacza audio/video Winamp). W kilka godzin program został pobrany przez tysiące użytkowników. W obawie o względy prawne jego pobieranie zostało wkrótce zablokowane. Jednak program szybko znalazł uznanie użytkowników, którzy odtworzyli kod źródłowy programu i został on opublikowany na licencji GNU General Public License2 (GPL), otwierając drogę do powstania programów opensource, bazujących na tym protokole.

Gnutella jest jednym z niewielu rozwiązań P2P, będącym całkowicie zdecentralizowanym, co oznacza, że w sieci nie występuje żaden centralny serwer i nie wymaga do swojego działania stałego elementu w sieci. Słowo Gnutella odnosi się zarówno do protokołu, jak i sieci go wykorzystującej. Często używa się terminu GnutellaNet lub GNet do określenia sieci i w niniejszej pracy ten termin również będzie obowiązywał.

Podstawą protokołu Gnutella jest specyfikacja w wersji 0.4 [2], Jednakże większość obecnie działających programów opiera się o ciągle rozwijaną i niemającą statusu stabilnego wersję 0.6 [3].

Przy projektowaniu Gnutelli zrezygnowano z dotychczasowego modelu komunikacji internetowej opartej o serwer. Każdy klient sieci jest zarazem serwerem. Ten nowy model określono mianem serwentu (ang. servent od słów server/client). To założenie miało za zadanie uniemożliwić niedostępność sieci bądź jej wyłączenie przez pozbycie się newralgicznych punktów, jakimi do tej pory były centralne serwery.

Terminologia

Poniżej wymieniona została terminologia występująca w opisie protokołu i sieci Gnutella:

Struktura sieci

Bazowa wersja protokołu zakładała równorzędność wszystkich węzłów sieci. Każdy węzeł – serwent był jednocześnie klientem, jak i serwerem w sieci i żaden nie był wyróżniony spośród innych. Sieć skonstruowana w ten sposób posiada bardzo prostą strukturę. Każdy serwent łączy się z kilkoma sąsiednimi serwentami w sieci (rys. 7).

Rysunek 7 Podstawowa struktura sieci zbudowana ze zwykłych węzłów

To rozwiązanie wprowadzało niestety sporo ograniczeń i problemów takich jak:

Kolejne wersje protokołu wprowadziły jednak podział węzłów, aczkolwiek obecność węzłów sieci spełniających wcześniejsze założenia (zwanych dalej zwykłymi węzłami) w obecnej wersji protokołu jest dozwolona.

Podział hierarchiczny węzłów sieci został dokonany ze względu na opisane powyżej problemy ze skalowalnością i uczestnictwem wszystkich węzłów w wymianie komunikatów, co powodowało duże obciążenie sieci.

Specyfikacja 0.6 protokołu zakłada wyróżnienie dwóch rodzajów węzłów:

UP utrzymują połączenia miedzy sobą, jak i z węzłami Leaf połączonymi z nimi. Każdy UP może mieć podłączonych od 10 a 100 węzłów Leaf i utrzymuje połączenia z kilkoma węzłami typu UP bądź zwykłymi węzłami. Dodatkowo UP chronią Leaf przed nadmierną ilością zapytań, jak i przyjmują od nich zapytania.

Leaf jest węzłem o mniejszej funkcjonalności. Utrzymuje on tylko kilka aktywnych połączeń i to tylko z węzłami UP, nie są do niego również przekazywane wszystkie komunikaty.

Struktura sieci z węzłami UP i Leaf przedstawia się nieco inaczej (porównaj rys. 7), co obrazuje rysunek 8. Na rysunku znajdują się również zwykłe węzły. Są to węzły używające poprzedniej wersji protokołu, gdzie wszystkie węzły były równorzędne (określane jako serwent). Umieszczenie tych węzłów na rysunku podkreśla kompatybilność obecnej wersji protokołu z wcześniejszymi.

Rysunek 8 Struktura sieci z węzłami typu UP, Leaf i węzłami zwykłymi

Węzeł typu Ultrapeer spełnia następujące założenia:

Powyższe parametry nie gwarantują serwentowi, że będzie on typu UP. Spełniając powyższe wymagania jest on kandydatem do bycia UP w momencie, gdyby w sieci zaistniała taka konieczność.

Numery portów

Standardowym portem, na którym serwent przyjmuje połączenia jest 6346, aczkolwiek inny port może być również wykorzystywany; wiadomość o numerze portu przekazywana jest poprzez komunikaty Gnutelli.

Połączenie się z siecią GNet

By połączyć się do sieci serwentowi potrzebna jest znajomość przynajmniej jednego adresu IP innego serwentu. Adresy IP innych serwentów mogą być dostarczone różnymi drogami. W starszych implementacjach wyróżniamy następujące sposoby wyszukiwania węzłów sieci:

W obecnych rozwiązaniach wyróżnia się 4 sposoby pozyskiwania adresów innych serwentów i są to:

Najbardziej podstawowym i rozwijanym sposobem uzyskiwania adresów jest GwebCache (Gnutella Web Caching System). Jest to aplikacja zainstalowana na serwerze WWW hosta sieci GNet, zbierająca i udostępniająca adresy aktywnych węzłów.

Inicjowanie połączenia

W czasie, gdy dany serwent ma już listę innych serwentów, z którymi może się połączyć, następuje procedura inicjowania połączenia - handshaking. Handshaking odbywa się z wykorzystaniem protokołu TCP/IP. Jeżeli przyjmiemy, że host inicjujący połączenie to klient, a odbierający serwer, procedura przebiega w następujący sposób:

  1. Klient nawiązuje połączenie TCP

  2. Klient wysyła komunikat GNUTELLA CONNECT/0.6<cr><lf>

  3. Klient wysyła wszystkie swoje nagłówki kończąc każdy poprzez <cr><lf>

  4. Serwer powinien odpowiedzieć komunikatem GNUTELLA/0.6 200 <string><cr><lf>

  5. Serwer wysyła wszystkie swoje nagłówki w schemacie opisanym w podpunkcie c.

  6. Klient powinien odpowiedzieć GNUTELLA/0.6 200 OK<cr><lf>

  7. Klient powinien wysłać inne swoje nagłówki, jeżeli zachodzi taka potrzeba w formacie jak w podpunkcie c.

  8. Klient i serwer wysyłają między sobą komunikaty binarne, używając informacji z podpunktów c. i e.

Oto przykładowa wymiana komunikatów między klientem i serwerem podczas inicjowania połączenia:

Rysunek 9 Inicjowanie połączenie między zwykłymi węzłami4

Jeżeli klient podczas inicjowania połączenia wyśle inny komunikat niż opisany w punkcie c, serwer powinien go odrzucić. Serwer może również odrzucić połączenia z innych przyczyn, np. braku wsparcia wersji protokołu, którego używa klient.

Powyższy opis odnosi się do sytuacji, gdy mamy do czynienia ze zwykłymi węzłami sieci. Jeżeli mamy do czynienia z węzłami typu UP i Leaf komunikaty wzbogacone są o dodatkowe zmienne:

Poniżej zostaną przedstawione typowe schematy połączeń w sieci między węzłami różnego typu z wykorzystaniem powyższych zmiennych.

2.1.5.1. Połączenie UP – Leaf

W tym przypadku Leaf nawiązuje połączenie z UP, jeżeli połączenie zakończy się sukcesem, Leaf odrzuca wszystkie połączenia z węzłami innego typu niż UP (o ile je posiadał) i wysyła do UP swoją tablicę QRP.

Rysunek 10 Połączenia typu UP - Leaf

2.1.5.2. Połączenie Leaf – Leaf

Próba połączenia węzła typu Leaf z innym węzłem typu Leaf powinna zakończyć się rozłączeniem tak, jak na poniższym rysunku.

Rysunek 11 Połączenie typu Leaf – Leaf

2.1.5.3. Połączenia UP - UP

Połączenia UP – UP mogą przebiegać w dwojaki sposób. Pierwszy (rys. 12) zakłada połączenie dwóch UP, mających podłączone do siebie inne węzły typu Leaf.

Rysunek 12 Połączenie UP – UP

Może jednak zdarzyć się przypadek, w którym do danego węzeł typu UP nie jest połączony żaden węzeł typu Leaf, a dodatkowo w sieci jest już wystarczająca liczba węzłów UP. Wtedy status jednego z węzłów może zostaje zmieniony na Leaf, co przedstawia poniższy rysunek.

Rysunek 13 Połączenie UP – UP zakończone zmianą statusu jednego z węzłów.

Komunikaty protokołu Gnutella

Gdy połączenie do sieci GNet zostało już nawiązane, komunikacja między serwentami odbywa się poprzez kilka zdefiniowanych komunikatów. Każdy komunikat poprzedzony jest nagłówkiem komunikatu o ustalonej strukturze. W jednym pakiecie IP może znajdować się kilka komunikatów Gnutelli, jak i dozwolona jest sytuacja, gdy jeden komunikat Gnutelli zajmuje kilka pakietów IP.

2.1.6.1. Nagłówek komunikatów Gnutelli

Struktura nagłówka komunikatów Gnutelli przedstawiona jest na rysunku 14. Każdy nagłówek zawiera 23 bajty.

Fields GUID Payload Descriptor TTL HOPS Payload Length
Byte offset 0...15 16 17 18 19..22

Rysunek 14 Nagłówek komunikatów Gnutelli

Poszczególne pola nagłówka komunikatu mają następujące znaczenie:

2.1.6.1. Rodzaje komunikatów

Protokół Gnutella określa kilka podstawowych komunikatów, aczkolwiek dozwolone jest używanie niestandardowych komunikatów zdefiniowanych, np. dla konkretnego programu. Informacje o dodatkowych rodzajach komunikatów wymieniane są miedzy serwentami w czasie inicjowania połączenia. Rodzaje poszczególnych komunikatów opisane są w poniższych podrozdziałach.

2.1.6.1.1. Ping

Komunikat wysyłany w celu aktywowania połączenia z innym serwentem. Serwent odbierający przekazuje go dalej do innych serwentów, może też sam odpowiedzieć komunikatem Pong, jeżeli chce nawiązać połączenie z nadawcą komunikatu Ping. Komunikat ten nie zawiera standardowo danych, aczkolwiek istnieje możliwość umieszczenia w tym komunikacie bloku GGEP (Gnutella Generic Extension Protocol, patrz rozdział 2.1.6.2)

2.1.6.1.2. Pong

Komunikat wysyłany w odpowiedzi na Ping. Powinien zawierać GUID komunikatu Ping, na który odpowiada. Zawiera następujące dane:

2.1.6.1.3. Query

Komunikat Query (zapytanie) jest używany w celu wyszukania danego pliku.. Długość zapytania nie powinna przekraczać 256 bajtów i nie może przekraczać 4 kB, Zapytanie przekraczające 4kB będzie odrzucone.

Komunikat Query zawiera następujące pola:

2.1.6.1.4. QueryHits

Jest komunikatem wysyłanym w odpowiedzi na komunikat Query. Zawiera następujące pola:

2.1.6.1.5. Push

Ten komunikat jest wysyłany przez serwent wyszukujący plik w momencie otrzymania komunikatu QueryHits, z informacją o niemożliwości przyjmowania połączeń przychodzących.

Komunikat Push zawiera następujące informacje:

2.1.6.1.6. Bye

Bye jest komunikatem opcjonalnym i używanym do powiadomienia serwentów, z którymi dany serwent jest połączony, o wyłączeniu się z sieci wraz z podaniem przyczyny. Komunikat wysyłany jest z TTL=1, by nie był wysyłany dalej przez serwent, który go otrzymał.

2.1.6.2. GGEP – rozszerzenie protokołu Gnutella

GGEP - Gnutella Generic Extension Protocol jest protokołem rozszerzającym możliwości protokołu Gnutella, pozwalającym na zwiększenie funkcjonalności jego komunikatów. Serwenty używające GGEP muszą powiadomić o tym fakcie inne serwenty w czasie inicjacji połączenia, dołączając tę wiadomość do nagłówka. Jeżeli serwent używający GGEP wysyła komunikaty do serwentów nieobsługujących to rozszerzenie, to powinien usunąć ten blok ze swoich komunikatów.

Wyszukiwanie węzłów w sieci

Jeżeli węzeł został już podłączony do innego węzła wymienia z nim listę aktywnych węzłów i próbuje się z nimi połączyć. Dany serwent buduje w ten sposób listę aktywnych węzłów. Teoretycznie wymiana informacji z kolejnymi węzłami może trwać aż do wyszukania wszystkich aktywnych węzłów, jednak praktycznie ilość węzłów, z którymi nawiązuje się połączenie, jest definiowalna. Zazwyczaj liczba węzłów, z którymi nawiązano połączenie wynosi około 5.

Serwent utrzymuje również listę innych węzłów, z którymi nie nawiązał jeszcze połączenia (jako alternatywne) oraz odrzuca te z którymi połączyć mu się nie udało.

Informacje na temat aktywnych węzłów sieci, z którymi można się połączyć zdobywana jest za pośrednictwem komunikatów Ping i Pong.

W bazowej wersji protokołu serwent wysyła komunikat Ping do węzłów, z którymi jest połączony, a te przekazują ten komunikat dalej do swoich sąsiadów (czyli węzłów, z którymi mają aktywne połączenie), zmniejszając o 1 wartość TTL i zwiększając o 1 wartość Hops. Oczywiście każdy Ping opatrzony jest odpowiednią wartością TTL, by wiadomość nie krążyła w sieci bez końca. Zazwyczaj wartość ta ustawiona jest na 7. Serwent odbierający, o ile może nawiązać połączenie, odsyła komunikat Pong. Serwent ma też za zadanie nie przekazywać dalej komunikatów Pong z tym samym GUID i Payload Descriptor, które odebrał wcześniej. Przykładowy ruting komunikatów Ping i Pong przedstawia rysunek 15.

Rysunek 15 Ruting komunikatów Ping i Pong6

Komunikat Pong wraca do nadawcy tą samą drogą, którą przebył komunikat Ping. Takie rozwiązanie niesie za sobą bardzo duże natężenie ruchu w sieci, więc w nowszych implementacjach protokółu, wprowadzono mechanizm zwany Pong-cache.

2.1.7.2. Mechanizm Pong-cache

Pong-cache jest mechanizmem pozwalającym znacznie ograniczyć ilość komunikatów Pong w sieci. Każdy serwent tworzy bazę danych komunikatów Pong dla każdego z połączeń, które aktualnie obsługuje. W bazie tej zapisuje odebrane wiadomości Pong. Liczba rekordów w bazie jest ograniczona (zazwyczaj do kilkunastu). Gdy serwent otrzymuje nowy komunikat Pong, zapisuje go w bazie, nadpisując najstarszą zanotowaną wartość. W momencie, gdy serwent otrzymuje komunikat Ping, odsyła od razu kilka komunikatów Pong (zazwyczaj 10) ze swojej bazy danych i przesyła Ping dalej, jak miało to miejsce wcześniej. Serwent wysyłając komunikaty Pong zmienia GUID na GUID adresata Ping. Dla ciągłego odświeżania bazy Pong-cache serwent wysyła co 3 sekundy komunikat Ping z TTL=7 i Hops=0. Oczywiście serwenty podczas nawiązywania połączenie powinny wymienić ze sobą informacje o wersji Pong-caching, jaki obsługują np.: "Pong-Caching: 0.1".

Wyszukiwanie plików

Wyszukiwanie plików jak i wyszukiwanie hostów również można podzielić na dwie grupy. Pierwsza to standardowe wyszukiwanie zgodne z podstawową wersją protokołu Gnutelli, drugie pojawiło się wraz z rozszerzeniem protokołu i wprowadzeniem podziału na hosty UP i Leaf. Obie wersje mogą ze sobą współpracować, a szczegółowy opis każdej z nich zamieszczony jest w kolejnych podrozdziałach.

2.1.8.1. Wyszukiwanie plików w wersji podstawowej

Wyszukanie pliku polega na wysłaniu komunikatu Query do innych węzłów sieci, z którymi wcześniej nawiązano połączenie. Tak jak w przypadku komunikatów Ping i Pong, Query przekazywane jest dalej zgodnie z wartością TTL zawartą w komunikacie. Każdy węzeł, który otrzymał komunikat Query, sprawdza kryteria wyszukiwania i dopasowuje je do listy swoich zbiorów. Jeżeli spełnia te kryteria (posiada dany plik), odsyła tą samą drogą komunikat QueryHits. W przypadku, gdy nie może przyjmować połączeń przychodzących, informuje o tym nadawcę, ustawiając bit flagPush w odpowiednią wartość. Odbiorca po otrzymaniu takiej informacji może użyć komunikatu Push w celu pobrania interesującego go pliku (rozdział 2.1.9.1). GUID QuueryHit powinien być identyczny z identyfikatorem komunikatu Query, na który dany serwent odpowiada. Nadawca Query po otrzymaniu odpowiedzi w postaci QueryHits nawiązuje bezpośrednie połączeni z nim. Jeżeli węzeł otrzyma odpowiedź od więcej niż jednego węzła, może wykonać tzw. swarm download - ściągnięcie kawałków pliku z różnych komputerów, przez co skraca się czas pobierania pliku. Plik pobierany jest z wykorzystaniem protokołu HTTP, a nie Gnutella (rozdział 2.1.9). Rysunek 16 przedstawia ruting komunikatu Query.

Rysunek 16 Routing komunikatów Query i QueryHit

2.1.8.2. Wyszukiwanie plików w wersji rozszerzonej

W przypadku, gdy mamy węzły typu UP i Leaf, wyszukiwanie pliku odbywa się w nieco inny, rozszerzony sposób. Jak już wcześniej wspomniano, podział węzłów sieci został dokonany ze względu na ograniczenie ruchu sieciowego i zwiększenie skalowalności sieci. Dodatkowo hosty z łączami o słabszych parametrach odciążone są przez węzły UP poprzez zastosowanie protokołu QRP - Query Routing Protocol.

Zasada działania protokołu QRP przedstawia się następująco. Każdy węzeł typu Leaf buduje bazę nazwaną boolean vector, wyrazów z nazw plików, które udostępnia, a następnie przesyła ją do UP, z którym jest połączony. Węzeł UP zapisuje wszystkie boolean vector, które otrzymał od swoich węzłów typu Leaf. W momencie, gdy do UP przychodzi komunikat Query, ten przed wysłaniem go do węzłów Leaf porównuje warunki zapytania z bazami boolean vector i jeżeli jakieś zapytanie pasuje, przekazuje komunikat Query danemu Leaf. W przeciwnym wypadku, komunikaty Query nie są przekazywane. Węzeł UP przekazuje Query tylko innym węzłom UP, a także serwentom niewspierającym tej technologii (zwykłym węzłom). Także węzeł typu Leaf będzie otrzymywał wszystkie komunikaty Query, o ile nie wyśle do swojego UP tablicy boolean vector.

Pobieranie plików

Po otrzymaniu komunikatu QueryHit serwent może zainicjować procedurę pobierania pliku. Pobieranie pliku odbywa się poza siecią GNet z wykorzystaniem protokołu HTTP. Rekomendowaną wersją protokołu HTTP jest 1.1.

Serwent nawiązuje bezpośrednie połączenie z serwentem udostępniającym plik, wysyłając komunikat w następującej formie:

GET /get/<File Index>/<File Name> HTTP/1.1<cr><lf>

User-Agent: Gnutella<cr><lf>

Host: 123.123.123.123:6346<cr><lf>

Connection: Keep-Alive<cr><lf>

Range: bytes=0-<cr><lf>

<cr><lf>

gdzie:

Jak widać w powyższym przykładzie Gnutella wprowadziła wsparcie dla parametru Range protokołu HTTP, co pozwala na wznowienie pobierania pliku w momencie przerwania połączenia. Przykładowe inicjowanie pobierania pliku przedstawia rysunek 17.

Rysunek 17 Pobieranie pliku z wykorzystaniem protokołu HTTP

2.1.9.1. Pobieranie pliku zza zapory sieciowej

Jak wspomniano w rozdziale 2.1.8.1, może zdarzyć się sytuacja, w której serwent odpowiadający na komunikat Query jest za zaporą sieciową i nie może przyjmować połączeń przychodzących. W takim wypadku serwent chcący pobrać plik wysyła komunikat Push z informacją o swoim adresie IP i porcie, na którym przyjmie połączenie (rys. 18). Adresat komunikatu Push może zainicjować połączenie i w ten sposób pobieranie pliku może się odbyć. Inicjowanie połączenia odbywa się komunikatem:

GIV <File Index>:<Servent GUID>/<File Name>\n\n

Serwent obierający komunikat GIV odpowiada komunikatem:

GET /get/<File Index>/<File Name>/ HTTP/1.0\r\n

User-Agent: Gnutella/0.4\r\n

Range: bytes=0-\r\n

Connection: Keep-Alive\r\n

Dalszy przebieg komunikacji jest taki sam, jak w przypadku opisanym w rozdziale 2.1.9.

Rysunek 18 Ruting komunikatów Push.

Podsumowanie

Opisana powyżej sieć GNet i związany z nią protokół Gnutella jest podstawą wielu programów P2P dostępnych obecnie w świecie komputerowym. Do najpopularniejszych należą takie programy jak:

Programy korzystające z sieci GNet ciągle się doskonalą, a ich twórcy wprowadzają nowe rozwiązania, wpływające nie tylko na coraz większą łatwość wyszukiwania plików, ale także na zmniejszenie ruchu w sieci i jej skalowalność. Warto zauważyć, że ogólna zasada działania sieci nie jest skomplikowana, a ilość komunikatów wyspecyfikowanych przez protokół niewielka. Brak też systemu logowania użytkownika do sieci opartego o system autentykacji i autoryzacji. Zauważyć można również zasadnicze zmiany podstawowych założeń systemu, które pierwotnie nie wyróżniały żadnego z węzłów sieci. Zmiana ta została podyktowana przez rzeczywiste warunki, w których każdy z węzłów dysponuje różnymi parametrami łącza, rodzajem adresu czy sprzętem, w jaki jest wyposażony.



komentarze

Copyright © 2008-2010 EPrace oraz autorzy prac.