Współbieżność, tunelowanie i kapsułkowanie, XDR i RPC 1. Serwery wielousługowe i wieloprotokołowe. 2. Sterowanie współbieżnością w serwerze współbieżność sterowana zapotrzebowaniem, alokacja wstępna procesów podporządkowanych. 3. Współbieżność w programach klienckich. 4. Transmisja tunelowa i kapsułkowanie serwery proxy. 5. Zewnętrzna reprezentacja danych - XDR. 6. Koncepcja programowania rozproszonego. model proceduralny. zdalne wywoływanie procedur. wprowadzenie do Sun RPC. 1
Serwery wieloprotokołowe (TCP i UDP) Serwery wieloprotokołowe udostępniają jedną usługę za pośrednictwem kilku protokołów warstwy transportowej. Podstawową zaletą tego rozwiązania jest łatwiejsze zarządzanie kodem realizującym usługę, np. przy zmianie wersji oprogramowania czy systemu operacyjnego, niezależnie od obsługiwanego protokołu (TCP lub UDP). Dodatkowo, serwer wieloprotokołowy zwykle potrzebuje mniej zasobów systemowych niż kilka serwerów jednoprotokołowych. 2
Serwery wieloprotokołowe Schemat struktury serwera wieloprotokołowego (TCP i UDP). proces główny obsługa UDP oczekiwanie na połączenia TCP obsługa połączenia TCP Proces główny przyjmuje zgłoszenia połączeń TCP; do obsługi połączenia wykorzystywane jest osobne gniazdo. Niezależnie obsługiwane są zgłoszenia UDP. 3
Serwery wieloprotokołowe Poniższy przykład nie zawiera obsługi błędów zwracanych przez funkcje wykorzystywane do transmisji TCP/IP. svr_echo_tcpudp(){ int stcp, sudp, maxs, s; struct sockaddr_in sin; char buf[linelen]; fd_set afds, //zbiór aktywnych deskryptorów stcp = passivesock(testport, "tcp", 10); sudp = passivesock(testport, "udp", 0); maxs = MAX(sTCP, sudp); FD_ZERO(&afds); while(1){ FD_SET(sTCP, &afds); FD_SET(sUDP, &afds); 4
Serwery wieloprotokołowe } select(maxs+1, &afds, NULL, NULL, 0); alen = sizeof(sin); if(fd_isset(stcp, &afds)){ // obsługa TCP s = accept(stcp, struct sockaddr*)&sin, &alen); daytimed(buf); write(s, buf, strlen(buf)); close(s); } if(fd_isset(sudp, &afds)){ // obsługa UDP recvfrom(sudp, buf, sizeof(buf), 0, (struct sockaddr*)&sin &alen); daytimed(buf); sendto(sudp, buf, strlen(buf), 0, (struct sockaddr*)&sin, sizeof(sin)); } }//while 5
Serwery wieloprotokołowe (TCP i UDP) Serwer wieloprotokołowy stanowi rozwiązanie pozwalające skupić cały kod, realizujący określoną usługę, w jednym programie. Dzięki temu nie ma problemów z koordynacją zmian w przeciwieństwie do sytuacji gdy kod ten jest powielony w różnych programach. Serwer tego typu jest realizowany przez jeden proces, który tworzy główne gniazdo dla każdego z protokołów (TCP, UDP) po czym wywołuje funkcje select() w oczekiwaniu na gotowość jednego lub obydwu gniazd. Gdy gotowe jest gniazdo TCP, serwer nawiązuje połączenie z klientem i za jego pośrednictwem odpowiada na nadsyłane zapytania. Gdy gotowość zgłosi gniazdo UDP, serwer odczytuje zapytanie i wysyła odpowiedź 6
Serwery wielousługowe Dla rodziny protokołów TCP/IP zdefiniowano duży zbiór tzw. prostych usług, pomocnych w administrowaniu, testowaniu i wykrywaniu błędów (np. DAYTIME, ECHO, TIME). W systemie, w którym dla każdej standartowej usługi istnieje oddzielny serwer, będą działać dziesiątki procesów, mimo że większość z nich zapewne nigdy nie otrzyma zgłoszenia. Dlatego połączenie wielu serwerów dla wielu różnych usług w jeden proces może radykalnie zmniejszyć liczbę aktywnych procesów. UWAGA: system operacyjny może ograniczać maksymalną liczbę gniazd używanych w jednym procesie. 7
Budowa wielousługowego serwera połączeniowego Schemat struktury iteracyjnego, połączeniowego serwera wielousługowego. serwer gniazdo Gniazdo obsługujące określone połączenie. gniazdo gniazdo gniazdo Gniazda dla poszczególnych usług realizowanych przez serwer. Serwer ma otwarte gniazdo dla każdej z usług i co najwyżej jedno do obsługi określonego połączenia. 8
Budowa wielousługowego serwera bezpołączeniowego Schemat struktury bezpołączeniowego serwera wielousługowego. serwer gniazdo gniazdo... gniazdo Gniazda dla poszczególnych usług realizowanych przez serwer. Serwer czeka na nadejście datagramu do któregokolwiek z gniazd (select()). Każde gniazdo jest przypisane określonej usłudze. 9
Wspólbieżny połączeniowy serwer wielousługowy Schemat struktury współbieżnego, połączeniowego serwera wielousługowego. Proces główny Proces potomny Proces potomny gniazdo gniazdo gniazdo gniazdo gniazdo Gniazda dla poszczególnych usług realizowanych przez serwer. Gniazda dla poszczególnych połączeń obsługiwanych przez procesy potomne. 10
Serwery wielousługowe Jedną z głównych wad dotychczas omówionych rozwiązań jest trudność wprowadzania zmian. Każda modyfikacja kodu realizującego jedną usługę wymaga ponownej kompilacji całego serwera, zatrzymania jego działania i uruchomienia nowej wersji. W serwerze wielousługowym można oddzielić części kodu realizujące poszczególne usługi od kodu obsługującego wstępne zgłoszenia połączeń. Zabieg taki jest możliwy dzięki funkcji systemowej execve(). 11
Serwery wielousługowe int execve(const char *filename, const char *argv[], const char *env[]); filename nazwa pliku z programem lub skryptem, który zostanie uruchomiony w miejsce istniejącego procesu, argv tablica argumentów, env zmienne środowiska Wartość zwracana: w przypadku sukcesu nie ma powrotu, w przypadku błędu -1 (errno). 12
Wspólbieżny połączeniowy serwer wielousługowy Schemat struktury połączeniowego, który korzysta z funkcji execve(), aby wywołać odzielny program do obsługi każdego połączenia. Proces główny Proces potomny Proces potomny program program gniazdo gniazdo gniazdo Gniazda dla poszczególnych usług realizowanych przez serwer. gniazdo gniazdo Gniazda dla poszczególnych połączeń obsługiwanych przez procesy potomne. 13
Wybór między rozwiązaniem iteracyjnym i współbieżnym Wybór typu serwera iteracyjnego albo współbieżnego może być sprawą trudną, zważywszy jak szybki jest zarówno wzrost popytu użytkowników na usługi, jak i rozwój możliwości komunikacyjnych oraz wzrost szybkości przetwarzania danych. Projektanci przeważnie podejmują odpowiednie decyzje na podstawie ekstrapolacji dotychczasowych tendencji rozwojowych. 14
Poziom współbieżności Poziom współbieżności działania serwera definiujemy jako liczbę procesów serwera działających w danej chwili. Współbieżny serwer połączeniowy zazwyczaj tworzy nowy proces dla każdego nawiązywanego połączenia z klientem. W praktyce liczba tych połączeń nie może być dowolnie duża. Każda implementacja protokołu TCP narzuca ograniczenie liczby jednocześnie aktywnych połączeń. Każdy system operacyjny ogranicza liczbę działających procesów. Gdy serwer wyczerpie jeden lub drugi limit, system będzie odmawiał tworzenia nowych procesów. 15
Współbieżność sterowana zapotrzebowaniem Współbieżność sterowana zapotrzebowaniem (demand driven concurency) to technika polegająca na tworzeniu nowych procesów przy wzroście liczby jednocześnie obsługiwanych połączeń. Serwer zajmuje zasoby systemu tylko wtedy, gdy ich rzeczywiście używa. Jednocześnie takie serwery zapewniają krótki obserwowany czas odpowiedzi, dlatego że kolejne zgłoszenia nie muszą czekać na zakończenie obsługi zgłoszeń wcześniejszych. 16
Narzut czasowy operacji systemowych Zarówno operacja odebrania zgłoszenia z sieci, jak i utworzenie nowego procesu zajmują zauważalną ilość czasu. Powoduje to opóźnienie rozpoczęcia obsługi zgłoszenia. Jest to szczególnie odczuwalne, gdy czas obsługi zgłoszenia jest krótszy niż czas utworzenia procesu potomnego. serwer współbieżny utworzenie procesu podporządkowanego 1 obsługa zgłoszenia 1 utworzenie procesu podporządkowanego 2 obsługa zgłoszenia 2 serwer iteracyjny obsługa zgłoszenia 1 obsługa zgłoszenia 2 17
Narzut czasowy operacji systemowych W praktyce obciążenie serwerów zgłoszeniami rzadko osiąga poziom bliski ich maksymalnej przepustowości. Ponadto niewielu projektantów decyduje się na współbieżną realizację serwera w sytuacji, gdy koszt tworzenia nowego procesu przewyższa czas przetwarzania zgłoszenia. Dlatego przypadki nadmiernych opóźnień obsługi lub odrzucania zgłoszeń nie zdarzają się często. Jednak projektując serwer, któremu stawia się wymaganie możliwie krótkiego czasu odpowiedzi przy dużym obciążeniu, trzeba rozważyć rozwiązania inne niż współbieżność sterowana zapotrzebowaniem. 18
Alokacja wstępna procesów podporządkowanych Wstępna alokacja procesów polega na tworzeniu współbieżnych procesów przy rozpoczęciu pracy przez proces główny serwera. Każdy z tych procesów czeka na nadejście zgłoszenia wywołując odpowiednią funkcję systemową. Po jego nadejściu jeden z procesów potomnych rozpoczyna obsługę klienta. Po zakończeniu obsługi proces potomny oczekuje na kolejne zgłoszenie. Dzięki temu skraca się czas obsługi zgłoszeń ponieważ serwer nie traci go na tworzenie dodatkowych procesów potomnych. Obsługa zgłoszenia może też przebiegać jednocześnie z operacją wejścia wyjścia związaną z innym zgłoszeniem. 19
Alokacja wstępna procesów podporządkowanych Schemat serwera połączeniowego korzystającego z wstępnej alokacji procesów. proces główny proces potomny proces potomny proces potomny gniazdo gniazdo gniazdo gniazdo dla zgłoszeń połączeń gniazda używane do obsługi poszczególnych połączeń. 20
Alokacja wstępna procesów podporządkowanych Schemat serwera bezpołączeniowego korzystającego z wstępnej alokacji procesów. proces główny proces potomny proces potomny proces potomny gniazdo gniazdo związane z portem o powszechnie znanym numerze 21
Systemy wieloprocesorowe W systemach wieloprocesorowych alokacja wstępna pozwala powiązać poziom współbieżności z możliwościami serwera. W komputerze wieloprocesorowym system operacyjny przydziela każdemu procesowi oddzielny procesor. Dzięki temu, podczas nasilonego napływu zgłoszeń, każde z nich będzie obsługiwane przez inny procesor. W ten sposób osiągnięta zostanie maksymalna możliwa szybkość obsługi. 22
Odraczanie alokacji procesów Koszty alokacji wstępnej nie ograniczają się jedynie do utworzenia procesu. Każdy nowy proces działający w systemie zwiększa ilość czasu procesora zużywaną przez podsystem zarządzający procesami. Oprogramowanie sieciowe także może zużywać więcej czasu w sytuacji, gdy obsługuje wiele alokowanych wstępnie procesów usiłujących odbierać zgłoszenia napływające z sieci. Te dodatkowe koszty są uzasadnione tylko wtedy, gdy dzięki alokacji wstępnej procesów można zwiększyć całkowitą przepustowość serwera lub skrócić czas oczekiwania na obsługę. W przeciwnym razie należy rozważyć wykorzystanie techniki tzw. Odroczonej alokacji procesów (delayed process allocation). 23
Odraczanie alokacji procesów Serwer działający według metody odroczonej alokacji procesów rozpoczyna przetwarzanie każdego zgłoszenia w trybie iteracyjnym. Oddzielny, współbieżnie działający proces przeznaczony tylko do obsługi tego połączenia jest tworzony wtedy, gdy okazuje się, że jego przetwarzanie zajmie (zajmuje) zbyt dużo czasu. Proces główny może więc przykładowo sprawdzić poprawność zgłoszenia, a jeśli przetwarzane zgłoszenie jest mało czasochłonne także je obsłużyć, bez tworzenia w tym celu nowego procesu i przełączania kontekstu. W celu realizacji tej techniki w środowisku UNIX można wykorzystać systemową funkcję alarm(). 24
Odraczanie alokacji procesów Funkcja alarm() powoduje wysłanie do procesu, w którym została użyta, sygnału SIGALARM po upływie określonego czasu. Użycie funkcji alarm() kasuje wszystkie wcześniej ustawione alarmy. unsigned int alarm(unsigned int seconds); seconds liczba sekund, po której zostanie wysłany sygnał. W przypadku gdy seconds zostanie ustawione na zero, nie zostanie wysłany sygnał. Wartość zwracana: liczba sekund pozostała do wywołania wcześniej zdefiniowanego alarmu lub zero. 25
Jednoczesne stosowanie kilku technik alokacji Techniki wstępnej i odroczonej alokacji są oparte na tej samej zasadzie uniezależnienia poziomu współbieżności działania serwera w danej chwili od liczby obsługiwanych zgłoszeń. Przyjęcie tej zasady umożliwia elastyczne dostosowanie poziomu współbieżności do potrzeb i w konsekwencji pozwala osiągnąć lepszą wydajność serwera. 26
Jednoczesne stosowanie kilku technik alokacji Omówione techniki można stosować łącznie. Serwer na początku działa zgodnie z metodą odroczonej alokacji (nie tworzy nowych procesów). Gdy jednak już powstanie taki proces to po zakończeniu obsługi klienta może on istnieć nadal i czekać na następne zgłoszenia. Największym problemem w tym wypadku jest stworzenie efektywnego mechanizmu ograniczenia poziomu współbieżności. Nie jest łatwo ocenić, kiedy istniejący proces potomny powinien zakończyć działanie i zwolnić zasoby. Jedna z metod polega na kończeniu procesów jeśli przez określony czas są one bezczynne np. nie otrzymały zgłoszenia połączenia. 27
Współbieżność w programach klienckich Współbieżna realizacja programów klienckich ma następujące zalety: łatwość implementacji poszczególne funkcje mogą być realizowane przez odrębne części programu, ułatwiona konserwacja i rozbudowa programu odrębne moduły mogą być rozwijane niezależnie, możliwość połączenia z wieloma serwerami jednocześnie może znacznie skrócić czas oczekiwania na uzyskanie kompletnej obsługi, dynamiczne sterowanie przetwarzaniem danych zmiana parametrów działania programu, uzyskanie informacji o stanie połączeń itp. Zasadniczą zaletą współbieżnej realizacji programów klienckich jest asynchronizm. Program może jednocześnie wykonywać wiele zadań. Kolejność ich wykonywania nie jest przez program zdeterminowana. 28
Współbieżność w programach klienckich - przykłady Schemat jednej z możliwych struktur wieloprocesowego, połączeniowego programu klienta. proces sterujący proces obsługi wejścia proces obsługi wyjścia polecenia gniazdo sterujące wejście wyjście TCP Jeden z procesów obsługuje wejście i wysyła zgłoszenia do serwera, a drugi odbiera odpowiedzi i obsługuje wyjście. 29
Współbieżność w programach klienckich - przykłady Schemat jednej z możliwych struktur jednoprocesowego, połączeniowego programu klienta. polecenia sterujące proces klienta wejście wyjście gniazdo TCP gniazdo TCP gniazdo TCP Jeden proces za pomocą funkcji select() obsługuje wiele połączeń TCP oraz wejście danych sterujących. 30
Współbieżność w programach klienckich - przykłady int cli_con_echo_tcp(int argc, char **argv){ long t; int ccount, scount, i, j; char *buf; scount = 0; ccount = 1024; for(i=0; i<argc; i++){ if(strcmp(argv[i], "-c")==0){ if (++i<argc && (ccount = atoi(argv[i]))){ continue; }else{ printf("nieprawidlowe wywolanie programu\n"); return -1; } }else{ 31
Współbieżność w programach klienckich - przykłady switch(fork()){ case 0: // proces potomny if ((buf=(char *)calloc(ccount+1, sizeof(char)))==null){ fprintf(stderr, "calloc: %s\n", strerror(errno)); exit(0); } for(j=0; j<ccount; j++) buf[j] = 'a'; buf[j]='\0'; t = cli_echo_tcp(argv[i], TESTPORT, buf, 0); printf("%s\t%.6f\n", argv[i], (double)t/1000000.0); free(buf); exit(1); 32
Współbieżność w programach klienckich - przykłady } default: scount++; continue; case -1: fprintf(stderr, "fork: %s\n", strerror(errno)); return -1; } } } for(i=0; i<scount; i++) waitpid(-1, NULL, 0); return 1; 33 33
Transmisja tunelowa i kapsułkowanie Sieci komputerowe rozwijały się stopniowo przez wiele lat. Poszczególni producenci promowali własne systemy sieciowe, a protokoły TCP/IP nie zawsze były dostępne. Co więcej technologie przesyłu danych w sieciach rozległych zwykle wykorzystują odmienne protokoły transmisji niż te, które stosuje się w sieciach lokalnych. W związku z tym często zachodzi potrzeba przekazywania danych jednego protokołu poprzez połączenie obsługiwane przez inny protokół. W tym celu wykorzystuje się tzw. kapsułkowanie (encapsulation) lub tunelowanie (tunnelling). 34
Transmisja tunelowa i kapsułkowanie sieć rozległa frame relay, ATM, X25, GPRS, HSPA, 3G sieć lokalna TCP/IP sieć lokalna TCP/IP 35
Transmisja tunelowa i kapsułkowanie O transmisji kapsułkowanej datagramów IP mówimy wtedy, gdy protokół IP korzysta bezpośrednio z transmisji realizowanych przez sprzęt sieciowy, tzn. każdy datagram przeznaczony do wysłania jest umieszczany (kapsułkowany) w tzw. ramce (frame) lub pakiecie sieciowym. O transmisji tunelowej mówimy zaś wtedy, gdy protokół IP przesyła datagramy korzystając z usług protokołu wysokiego poziomu, np. korzysta z warstwy transportowej innego protokołu. 36
Transmisja tunelowa na poziomie aplikacyjnym program pocztowy z szyfrowaniem SSL oprogramowanie po stronie klienta transmisja niekodowana transmisja szyfrowana SSL Serwer POP3/IMAP stunnel oprogramowanie po stronie serwera Program stunnel (http://www.stunnel.org) typowo odbiera zgłoszenia na portach 995 (POP3) i 993 (IMAP). Po rozszyfrowaniu odebrane dane są przekazywane do serwera poczty (110, 143). Odpowiedz serwera jest szyfrowana i wysyłana do klienta. 37
Serwer proxy klient serwer serwer proxy Serwer proxy pośredniczy w transmisji pomiędzy klientem a serwerem. Serwerów proxy zwykle używa się aby: zabezpieczyć komputery w sieci lokalnej, przyspieszyć otrzymywanie odpowiedzi na powtarzające się zapytania (np. strony www squid), umożliwić wielu komputerom korzystanie z jednego publicznego adresu IP (Network Address Translation). Inne zastosowania serwerów pośredniczących: VPN, VOIP, GSM/GPRS,... 38
Podsumowanie Współbieżna organizacja programów to bardzo mocne narzędzie, użyteczne zarówno w odniesieniu do serwerów, jak i klientów. Współbieżność w programach klienckich umożliwia szybsze dostarczenie odpowiedzi użytkownikowi, pozwala uniknąć ryzyka zakleszczenia a także ułatwia oddzielenie operacji sterujących działaniem klienta od operacji przesyłania danych. 39
Zewnętrzna reprezentacja danych Dana jest liczba 32 bitowa 0 0 2 8 W zależności od komputera ten ciąg bajtów może być różnie interpretowany. Np. 2 2 8 + 8 2 0 = 520 lub 8 2 24 + 2 2 16 = 134217728 + 131072 = 134348800 Dla programistów konstruujących oprogramowanie typu klient serwer wybór sposobu reprezentacji danych przesyłanych między partnerami jest istotnym problemem. Jeśli komputery różnią się reprezentacją przesyłanych danych to muszą one zostać poddane konwersji. 40
Asymetryczna konwersja danych - jedna ze stron (klient lub serwer) dokonują odpowiedniej konwersji danych. W tej technice programista musi uwzględnić konwersję dla każdej pary różnych typów architektury komputerów, na których system może być używany. Zewnętrzna reprezentacja danych Jeśli w realizacji programów klient serwer zakłada się asymetryczną konwersję danych między rodzimą reprezentacją klienta a rodzimą reprezentacją serwera to liczba potrzebnych wersji takiej pary programów wzrasta proporcjonalnie do kwadratu liczby różnych typów architektury (problem N 2 dla konwersji danych). 41 41
Symetryczna konwersja danych obie strony wykonują konwersję danych z postaci rodzimej do standardowej reprezentacji danych przesyłanych przez sieć zewnętrznej reprezentacji danych (external data representation). Zalety: brak troski o architekturę komputera, na którym działa partner komunikacji, łatwiejsza implementacja i konserwacja programu Wady: dodatkowy koszt związany z konwersją danych, w przypadku gdy nie byłaby ona potrzebna, przesyłanie dodatkowych informacji przez sieć wymaganych przez specyfikację. Standardowy sieciowy porządek bajtów 42
XDR najpopularniejszy standard sieciowej reprezentacji danych opracowany przez firmę SUN [RFC 1014]. Podstawowy blok danych 4 bajty. Standardowy sieciowy porządek bajtów int [-2147483648, 2147483647] 4 bajty w formacie big endian (bajt najbardziej znaczący pod najniższym adresem): 0 0 2 8 = 520 Liczby ujemne notacja uzupełnieniowa do dwóch. Zapis bitów w ramach jednego bajta definiowany przez inne standardy. Zwykle little endian. unsigned int [0, 4294967295] 4 bajty, big endian. enum równoważnie jak zbiór int'ów, bool równoważne do enum { FALSE = 0, TRUE = 1 }. 43
Standardowy sieciowy porządek bajtów hyper (unsigned hyper) 8 bajtowy int (unsigned int), format big endian. float 4 bajtowa liczba zmiennoprzecinkowa: 1 bajt 2 bajt 3 bajt 4 bajt S E (8 bitów) F (23 bity) liczba = (-1) S 1.F 2 E-B. (B=127) Przykład: (5.78125) 10 = (4 + 1 + 1/2 + 1/4 + 1/32) 10 = (101.11001) 2 = (-1) 0 (1.0111001) 2 2 2 = = 0 10000001 01110010000000000000000 Pierwszy bit najbardziej znaczący. Stałe typu NaN (not a number) nie powinny być przesyłane przez sieć, 44
Standardowy sieciowy porządek bajtów double 8 bajtowa liczba zmiennoprzecinkowa: E 11 bitów, F 52 bity, B = 1023, 1 bajt 2 bajt 3 bajt S E (11 bitów) 4 bajt 5 bajt 6 bajt 7 bajt 8 bajt F (52 bity) liczba = (-1) S 1.F 2 E-B. (B=1023) void 0 bajtów, 45
Standardowy sieciowy porządek bajtów opaque identyfikator[n] dane nie poddane konwersji (n bajtów). Ewentualnie uzupełniane na końcu zerowymi bajtami (tak aby długość ciągu n+r była wielokrotnością 4). bajt 0 bajt 1... bajt n-1... bajt n+r-1 opaque identyfikator<n> - ciąg danych nie poddanych konwersji o nieustalonej długości (maksymalnie n = 2 32-1 bajtów). Długość kodowana jako unsigned int, długość (4 bajty) kolejne bajty uzupełnione zerami na końcu (jak w opaque) 46
Standardowy sieciowy porządek bajtów string identyfikator<n> - tekst kodowany jest podobnie do opaque o nieustalonej długości. Znaki tekstu są kolejno umieszczane w obszarze danych, tablica o ustalonej długości kolejno zapisane elementy tablicy (każdy element może mieć inną długość string), element 0 element 1... element n-1 tablica o nieustalonej długości (max n = 2 32-1 kodowane jako unsigned int) n element 0 element 1... element n-1 47
Standardowy sieciowy porządek bajtów struktury - struct { komponent A; komponent B;... }; komponent A komponent B... unie union{ komponent A; komponent B; }; numer komponentu (4 bajty) komponent 48
Procedury konwersji XDR Do konwersji można wykorzystać funkcje biblioteczne <rpc/xdr.h> Najpierw trzeba utworzyć obiekt XDR. W przypadku konwersji strumieniowej należy użyć funkcji: void xdrmem_create(xdr *xdrs, char *buf, int blen, int op); xdrs wskaźnik do utworzonej struktury zarządzającej konwersją strumieniową, buf wskaźnik do bufora dla konwertowanego strumienia danych, blen rozmiar bufora, op tryb pracy konwertera: XDR_ENCODE, XDR_DECODE lub XDR_FREE. 49
Procedury konwersji XDR Poszczególne procedury konwersji XDR mogą wykonywać konwersję w obydwu kierunkach. Wywołana procedura stwierdza w którym kierunku należy przeprowadzić konwersję, badając odpowiednią informację w strumieniu XDR, na którym działa. Jeśli *xdrs został utworzony z parametrem XDR_DECODE, odczytana zmienna zostanie wpisana w komórkę wskazaną przez pi. 50
Procedury konwersji XDR Jeśli (*xdrs) został utworzony w trybie XDR_ENCODE do konwersji liczby należy użyć jednej z funkcji konwertujących np: int xdr_int(xdr *xdrs, int *pi); xdrs wskaźnik do struktury zarządzającej konwersją strumieniową, pi wskaźnik do konwertowanej liczby; nagłówek strumienia 0 1 2 3 4 5 nagłówek strumienia 0 1 2 3 4 5 0 0 2 8 51
Procedury konwersji XDR Program może zażądać, aby procedury XDR po przekształceniu każdego elementu danych do postaci zewnętrznej automatycznie wysyłały ten element przez połączenie TCP. W tym celu należy użyć funkcji fdopen() i xdrstdio_create(): FILE * fdopen(int fd, char* mode); fd deskryptor istniejącego pliku (gniazda), mode typ dostępu do pliku (gniazda): r, r+, w, w+, a, a+ Wartość zwracana: wskaźnik do strumienia związanego z plikiem (gniazdem) lub NULL w przypadku błędu (errno). Funkcja fdopen() pozwala związać istniejące gniazdo TCP ze strumieniem FILE. 52
Procedury konwersji XDR Do powiązania konwertera XDR z dowolnym strumieniem danych służy funkcja xdrstdio_create(). void xdrstdio_create(xdr *xdrs, FILE *file, int op); xdrs - wskaźnik do utworzonej struktury zarządzającej konwersją strumieniową, FILE wskaźnik do struktury opisującej strumień danych, op tryb pracy konwertera: XDR_ENCODE, XDR_DECODE lub XDR_FREE. 53
Aby funkcje XDR mogły współpracować z oprogramowaniem używającym transmisji datagramowej opracowano alternatywny interfejs. Procedury konwersji XDR void xdrrec_create(xdr *xdrs, u_int sendsize, u_int recvsize, char *handle, int (*readit)(), int (*writeit)(); xdrs - wskaźnik do utworzonej struktury zarządzającej konwersją strumieniową, sendsize, recvsize rozmiary buforów: wyjściowego i wejściowego, readit, writeit funkcje wywoływane po opróżnieniu / zapełnieniu bufora w celu pobrania/wysłania danych. Posiadają trzy argumenty: void *handle, void *buffer, void *len. 54
Procedury konwersji XDR Użycie rekordów umożliwia wykorzystywanie dodatkowych funkcji: bool_t xdrrec_endofrecord(xdr *xdrs, bool_t flushnow) - wstawia znacznik końca rekordu bool_t xdrrec_skiprecord(xdr *xdrs) - pozwala pominąć rekord bool_t xdrrec_eof(xdr *xdrs) - zwraca true jeśli osiągnięto koniec pliku w powiązanym ze strukturą *xdrs strumieniu. 55
Programowanie rozproszone Przystępując do tworzenia aplikacji rozproszonej projektant ma do wyboru dwa różne podejścia: projektowanie z punktu widzenia komunikacji między procesami (communication-oriented design) główny nacisk kładzie na protokół komunikacyjny. W drugiej kolejności tworzone są programy klienta i serwera reagujące na zgłoszenia i wysyłające dane zgodnie z protokołem. projektowanie z punktu widzenia aplikacji (applicationoriented design) rozpoczyna się od stworzenia aplikacji rozwiązującej problem. Po przetestowaniu jest ona dzielona na dwie lub więcej części. Na tym etapie dobudowuje się protokoły komunikacyjne. 56
Model zdalnego wywołania procedury Aby ułatwić projektowanie z punktu widzenia aplikacji opracowano model pojęciowy nazwany modelem wywołania procedury RPC (Remote Procedure Call) Model RPC umożliwia projektantowi (programiście) skupienie uwagi na samej aplikacji. Projektant może opracować zwykły, tradycyjny program rozwiązujący zadany problem, a dopiero potem podjąć próbę rozdzielenia go na części wykonywane na różnych komputerach. 57
Model wywołań procedur main() proc1() proc2() proc3() proc4() proc5() proc6() proc7() Program składa się z jednej lub wielu procedur, zazwyczaj zorganizowanych w hierarchię wywołań. Strzałka od procedury n do procedury m oznacza wywołanie m z wnętrza procedury n. 58
Rozszerzenie modelu proceduralnego Komputer 1 Komputer 2 main() proc1() proc2() proc3() proc4() proc5() proc6() proc7() Program rozproszony wykorzystujący model zdalnego wywołania procedury. Linia podziału przebiega pomiędzy programem głównym a procedurą nr 3. Implementacja zdalnego wywołania wymaga użycia protokołu komunikacyjnego. 59
Przebieg wykonania procedury kod programu głównego początek procedura A procedura B wywołanie A wywołanie B koniec powrót powrót Przepływ sterowania w sytuacji wywołania procedury i powrotu. Wykonanie programu biegnie przez program główny, następnie przez procedury A oraz B, po czym wraca do programu głównego. 60
Przebieg wykonania zdalnej procedury kod programu głównego klient początek procedura A serwer procedura B serwer wywołanie A wywołanie B koniec powrót powrót Przepływ sterowania w sytuacji wywołania zdalnej procedury. Linie przerywane pokazują przepływ sterowania między klientem i serwerem przy wywołaniu zdalnej procedury i powrocie z niej. 61
Model klient-serwer a RPC Wywołanie odległej procedury odpowiada wysłaniu zapytania do serwera. Odpowiedź serwera jest analogiem powrotu z procedury. W warunkach idealnych przebieg zdalnego wywołania procedury byłby identyczny jak wywołania lokalnego. Są jednak przypadki, które to ograniczają: duża czasochłonność wywołania zdalnego, brak możliwości przekazywania wskaźników, brak wspólnego środowiska pracy np. deskryptory wejścia wyjścia, operacje systemu operacyjnego. 62
Mechanizm Sun RPC Sun RPC [RFC 1057] definiuje mechanizm realizujący model zdalnego wywołania procedury. Specyfikacja określa: format komunikatów wysyłanych przez program wywołujący (klienta), format argumentów wywołania oraz format zwracanych wyników, protokół warstwy transportowej TCP lub UDP, kodowanie przesyłanych informacji XDR, Dla Sun RPC zdefiniowano również system kompilacji, który umożliwia automatyzację konstruowania programów rozproszonych. 63
Zdalnie wywoływane programy i procedury Program odległy jest odpowiednikiem serwera i zawiera jedną lub więcej procedur odległych oraz dane globalne. Do obszaru danych globalnych mają dostęp wszystkie procedury działające w ramach jednego programu. Jeden zdalnie wywoływany program proc 1 proc 2 proc 3 wspólne dane globalne 64
Identyfikacja zdalnie wywoływanych procedur Standard Sun RPC wymaga przypisania każdemu programowi odległemu jednoznacznego 32-bitowego identyfikatora. Ponadto każdej procedurze należącej do danego programu przydziela się liczbę całkowitą. Procedury są numerowane sekwencyjnie: 1, 2,..., N. Tak więc każdą procedurę odległą można zidentyfikować podając parę liczb. Specyfikacja przewiduje również możliwość rozróżnienia różnych wersji wywoływanego programu zdalnego. 65
Przekazywanie argumentów W większości języków programowania argumenty wywołania procedur podaje się w notacji pozycyjnej. Duża liczba argumentów powoduje zmniejszenie czytelności programu. Tę niedogodność można usunąć zbierając wiele argumentów w jedną strukturę Po powrocie do procedury wywołującej z tej struktury można wyodrębnić też wartości wynikowe zwrócone przez procedurę wywoływaną. 66
Wzajemne wykluczanie procedur Mechanizm Sun RPC nie pozwala na jednoczesne wywołanie więcej niż jednej procedury w ramach jednego, zdalnie wywoływanego programu. Dopóki trwa wykonanie jednej z nich, system blokuje następne wywołania. Jest to szczególnie istotne w programach wykorzystujących wspólne dane dla kilku różnych procedur. 67 67
Semantyka wywołań a protokół komunikacyjny Standard Sun RPC nie gwarantuje niezawodności zdalnego wywołania w przypadku, gdy używanym protokołem warstwy transportowej jest UDP. Jeśli program wywołujący procedurę nie otrzyma odpowiedzi oznaczającej powrót po jej wykonaniu to procedura wykonała się zero lub więcej razy. Nadejście odpowiedzi oznacza natomiast, że procedura została wykonana co najmniej jeden raz. Jeśli aplikacja korzystająca z mechanizmu Sun RPC używa protokołu UDP to trzeba ją zbudować tak, aby była odporna na konsekwencje tych własności. Przykładowo, wielokrotne i jednokrotne wykonanie zdalnej procedury powinno dać takie same wyniki - warunek idempotencji. 68
Numer programu a numer portu program RPC (serwer) zarządca odwzorowań RPC (serwer) program RPC (serwer) gniazdo gniazdo 111 gniazdo Każdy program RPC rejestruje swój numer i numer używanego portu u zarządcy odwzorowań RPC. Program klienta kontaktuje się z programem zarządcy działającym na porcie 111, aby otrzymać numer portu określonego programu zdalnego. 69
Algorytm programu zarządcy 1. Utwórz gniazdo bierne, związane z portem 111. 2. Przyjmuj kolejne żądania zarejestrowania numeru portu programu RPC lub odszukania numeru portu na podstawie podanego numeru programu RPC. Żądanie rejestracji jest zgłaszane przez programy działające lokalnie. Każde zgłoszenie ma postać pary składającej się z numeru programu RPC i numeru portu. Po otrzymaniu żądania zarządca dopisuje otrzymaną parę do bazy odwzorowań. Żądania odszukania numeru portu mogą być nadsyłane z dowolnego komputera. Każde zgłoszenie zawiera numer programu RPC na podstawie którego zarządca wyszukuje w bazie odwzorowań numer portu. 70
Podsumowanie Poza klasycznymi zastosowaniami, model klient serwer może być wykorzystany także do innych celów. Przedstawione przykłady odnoszą się do przekazywania i konwersji pakietów. We wszystkich zastosowaniach należy pamiętać o konwersji danych pomiędzy różnymi architekturami sprzętowymi klienta i serwera. Najpowszechniej używany jest tu standard XDR zakładający symetryczną konwersję danych. 71