Systemy rozproszone
Dr inż. L. Miękina
Department of Robotics and Mechatronics
AGH University of Science and Technology
Marzec, 2013
1/1
Program wykładu
Podstawowe charakterystyki, architektury
Komunikacja sieciowa z przykładami w j. Java
Zdalne wywoływanie procedur (Remote Procedure Calling)
Sieciowe systemy plików (Network File System)
Zdalne obiekty: Java Remote Method Invocation i CORBA
Usługi katalogowe
Web Services
2 / 1
Przeznaczenie
Motywacja Współdzielenie zasobów jest głównym powodem budowy
systemów rozproszonych. Zasoby te są zarządzane przez
serwery i udostępniane klientom, lub są opakowywane jako
obiekty i udostępniane innym obiektom-klientom.
Definicja system rozproszony (SR) to taki, którego składniki
zlokalizowane na komputerach należących do sieci komunikują
się i koordynują swoje działania tylko za pomocą przesyłanych
komunikatów (brak pamięci wspólnej - dzielonej).
Definicja system rozproszony jest kolekcją niezależnych komputerów,
które dla użytkowników sprawiają wrażenie jednolitego,
zwartego systemu.
Problemy wynikające z konstrukcji SR:
niejednorodność (heterogeneity) składników
otwartość, która pozwala dodawać lub wymieniać składniki
bezpieczeństwo
skalowalność - zdolność do poprawnej pracy przy zwiększającej się liczbie
użytkowników
tolerowanie awarii
równoległość funkcjonowania składników
przezroczystość (położenia, migracji,...)
3 / 1
Charakterystyki SR
SR mają następujące główne charakterystyki:
równoległość funkcjonowania składników: w sieci komputerów, równoległe
wykonywanie programów jest normą - wielu użytkowników może
równocześnie pracować, współdzieląc zasoby (strony w sieci lub pliki, etc.).
W tym przypadku, koordynacja równolegle działających programów, które
używają wspólnych zasobów jest istotnym zagadnieniem.
brak globalnego zegara: programy w SR kooperują wymieniając
komunikaty. Ścisła koordynacja zwykle zależy od wspólnego czasu
występowania zdarzeń lub wykonywania operacji. Jednak ograniczone
możliwości synchronizowania zegarów komputerów w sieci sprawiają, że
brak jest pojęcia poprawnego czasu systemowego (jako że jedyny sposób
komunikacji to wymiana komunikatów)
niezależne awarie komponentów: SR mogą ulegać swoistym awariom.
Awarie sieci skutkują izolacją połączonych komputerów. Podobnie, awaria
komputera lub niespodziewane zakończenie programu (crash) nie jest
natychmiast widoczna dla innych współpracujących komputerów. Dlatego
każdy komponent może ulegać awarii niezależne, pozostawiając inne w
stanie działania i oczekiwania na dalszą współpracę.
4 / 1
Przykłady SR
Typowymi, dobrze znanymi przykładami SR są:
Internet, który jest ogromną kolekcją wzajemnie połączonych sieci
komputerowych wielu różnych typów. Programy działające na
komputerach dołączonych do tych sieci wzajemnie na siebie oddziałują za
pomocą uzgodnionych środków komunikacji (definiowanych przez
protokoły internetowe).
Intranet, będący wydzieloną częścią Internetu, która jest oddzielnie
administrowana i ma konfigurowane granice aby wymusić lokalne strategie
bezpieczeństwa. Intranet połączony z Internetem poprzez router, który
pozwala użytkownikom pracującym wewnątrz intranetu używać usług poza
nim (Web, e-mail, ftp, etc.). Aby chronić intranet przed nieuprawnionym
dostępem używa się tzw. zapory (firewall). Działanie zapory polega na
filtrowaniu przychodzących i wychodzących komunikatów, na przykład w
zależności od ich zródła lub przeznaczenia. Np. tylko komunikaty związane
z pocztą elektroniczną lub WWW są przekazywane, a inne są blokowane.
sieci urządzeń mobilnych, w tym laptopów i komunikatorów; urządzeń
wbudowanych w wyposażenie mieszkań i budynków (pralki, audio, tv,
ogrzewanie i klimatyzacja, etc.)
aplikacje przemysłowe, w tym systemy korporacyjne i wytwórcze.
5 / 1
Modele SR
Wyróżnia się dwa rodzaje modeli SR:
modele architektury
modele fundamentalne (podstawowe)
Modele architektury SR są związane z:
rozmieszczeniem części w sieci komputerów, w celu identyfikacji
użytecznych wzorców rozkładu danych i obciążenia
relacjami między częściami, uwzględniającymi ich funkcjonalne role i
szablony komunikacji wzajemnej.
Przykładami są modele klient-serwer i procesy równorzędne (peer processes).
Modele podstawowe są związane z bardziej formalnym opisem własności, które
są wspólne dla wszystkich architektur.
model interakcji ma związek z wydajnością i z zagadnieniami określenia limitów
czasowych w SR, na przykład dla dostarczania komunikatów
model awarii określa wszystkie rodzaje niesprawności, jakie mogą powstać w
działaniu procesów i kanałów komunikacyjnych. Definiuje niezawodną
komunikację i poprawne procesy.
model bezpieczeństwa analizuje możliwe zagrożenia dla działania procesów i
kanałów komunikacyjnych. Wprowadza koncepcję bezpiecznego kanału, który
jest odporny na te (znane) zagrożenia.
6 / 1
Modele architektury
Warstwy oprogramowania
Warstwy oprogramowania pomagają w opisie oprogramowania z punktu
widzenia usług, jakich ono dostarcza.
Warstwy w samodzielnej maszynie:
komputer i interfejs sieciowy (karta)
system operacyjny
aplikacje i usługi.
Warstwy w maszynie działającej w SR:
komputer i interfejs sieciowy (karta)
system operacyjny
warstwa pośrednia (middleware)
aplikacje i usługi.
W obu przypadkach, dwie pierwsze warstwy (komputer i interfejs sieciowy (karta);
system operacyjny) tworzą platformę dla rozproszonych systemów i aplikacji.
Platforma dostarcza usług dla następnej warstwy, która jest zaimplementowana
niezależnie na każdym komputerze, dostarczając możliwości programowania
rozproszonego na wyższym poziomie (funkcje do komunikacji i synchronizacji między
procesami.
Typowe platformy: Intel x86/Windows, Intel x86/Linux, Sun SPARC/SunOS.
7 / 1
Modele architektury
Warstwa pośrednia (middleware)
Definicja Warstwa pośrednia jest warstwą oprogramowania, której
zadaniem jest ukrycie całej różnorodności rozwiązań (dot.
sprzętu, sieci, systemów operacyjnych i języków
programowania) i dostarczenie wygodnego i wydajnego modelu
programowania dla twórców rozproszonych aplikacji.
Warstwa pośrednia jest zorganizowana z procesów i obiektów powołanych na
każdym z komputerów, które współdziałają w celu implementacji zadań
systemu rozproszonego. W szczególności, warstwa pośrednia odpowiada za
komunikację, udostępniając abstrakcje typu zdalne wywoływanie procedur
(remote procedure call) i zdalne wywoływanie metod (remote method
invocation).
Najbardziej znane implementacje warstwy pośredniej to:
Remote Procedure Call (Sun)
Remote Method Invocation (Sun)
Common Object Request Broker Architecture ( OMG)
Distributed Component Object Model (Microsoft).
8 / 1
Modele architektury
Klient-serwer
Model klient-serwer jest historycznie najbardziej znaczący i ciągle pozostaje
najszerzej stosowany.
W tym modelu, procesy klientów współpracują z poszczególnymi procesami
serwerów działającymi na oddzielnych komputerach w celu dostępu do
dzielonych zasobów.
Serwery mogą z kolei być klientami innych serwerów, jak na schemacie. Na
przykład, serwer WWW jest zwykle klientem lokalnego serwera plików, który
zarządza plikami w których zapisano strony WWW. Poza tym, serwer WWW
jest zwykle klientem usługi DNS.
9 / 1
Modele architektury
Usługi dostarczane przez wiele serwerów
Usługi mogą być realizowane przez pewną liczbę procesów serwera działających
na oddzielnych komputerach, które to procesy kooperują ze sobą by dostarczyć
usługę procesom klienta.
Serwery mogą rozdzielać (partycjonować) pomiędzy siebie ogół obiektów na
których jest oparta usługa, lub mogą utrzymywać repliki tych obiektów na
pewnej grupie hostów.
Sieć WWW jest typowym przykładem partycjonowania danych, jako że każdy
serwer WWW zarządza swoim zbiorem zasobów (stron).
Replikacja jest używana by zapewnić lepszą wydajność i dostępność danych i
zwiększyć odporność na awarie. Replikacja zabezpiecza istnienie wielu
identycznych kopii danych w procesach działających na różnych komputerach.
10 / 1
Modele architektury
Servery proxy i pamięć cache
Pamięć cache jest składnicą ostatnio używanych danych dotyczących obiektów,
które to dane są bliższe (łatwiej dostępne) niż same obiekty. Kiedy nowy obiekt
zostaje przesłany do danego komputera, jest dodawany do lokalnej pamięci
cache, czasem zastępując starszy obiekt. Kiedy dany obiekt ma być
udostępniony, usługa cache sprawdza zawartość pamięci cache i zwraca
zapisany tam obiekt, jeśli jest on aktualny.
Pamięci cache mogą być związane z poszczególnymi klientami lub mogą być
umieszczone na serwerze proxy, który jest wspólny dla wielu klientów.
11 / 1
Modele architektury
Procesy równorzędne (peer processes)
W tej architekturze wszystkie procesy pełnią takie same role, razem kooperując
jako równorzędni partnerzy (peers) aby wykonać rozproszone działanie lub
obliczenia, bez różnicowania na klientów i serwery.
Eliminacja procesów serwera redukuje opóznienia wynikające z komunikacji
między procesowej realizowanej dla dostępu do lokalnych obiektów. Na
przykład, aplikacja pozwalająca wielu użytkownikom na wspólną pracę nad
dokumentami tekstowymi (podgląd i modyfikacja zawartości). Kod
powiadamiający o zmianach zawartości dokumentów może być zlokalizowany
na każdym komputerze, umożliwiając natychmiastowe poinformowanie
pozostałych zainteresowanych.
12 / 1
Modele architektury
Wariacje modelu klient-serwer: mobilny kod
Mobilny kod oznacza kod, który może być przesłany z jednego komputera na
inny i tam uruchomiony. Często wymaga to uruchomienia go w specjalnym
środowisku, tzw. wirtualnej maszynie na docelowym komputerze.
Typowym przykładem są applety - użytkownik pracujący w przeglądarce
wybiera hiperłącze identyfikujące applet, którego kod jest przechowywany na
serwerze WWW. Kod ten jest następnie ściągany z serwera i uruchamiany w
przeglądarce.
Korzyść wykonywania kodu lokalnie: dobra interaktywność, bo brak opóznień i
ograniczeń przepustowości w komunikacji sieciowej.
Wada: potencjalne zagrożenie bezpieczeństwa dla zasobów lokalnych docelowego
komputera, dlatego applety zwykle mają ograniczone prawa dostępu,
kontrolowane przez używaną maszynę wirtualną.
Mobilny agent jest działającym programem (włącznie z kodem i danymi), który
przemieszcza się z jednego komputera na drugi, realizując pewne zadanie na
rzecz użytkownika (np. zbieranie informacji).
Mobilny agent może w różny sposób wykorzystywać lokalne zasoby, np.
odczytywać rekordy baz danych, etc. Użycie mobilnych agentów pozwala
zredukować obciążenie związane ze zdalnym dostępem, ale wprowadza podobne
zagrożenia bezpieczeństwa jak użycie kodu mobilnego.
13 / 1
Modele architektury
Komputery sieciowe
Używając komputerów typu desktop należy zwykle zainstalować wszystkie
potrzebne programy lokalnie i zarządzać danymi. Może to być problemem dla
mniej doświadczonych użytkowników. Rozwiązaniem jest używanie komputera
sieciowego (network computer), który ładuje system operacyjny i wszystkie
programy aplikacji ze zdalnego serwera plików. Aplikacje wykonują się lokalnie,
ale pliki są zarządzane przez zdalne serwery plików.
Korzyści:
Użytkownicy nie są przywiązani do jednego komputera, ale mogą łatwo
przenosić się na inne komputery sieciowe
Wymagania sprzętowe są zredukowane.
Jeśli komputer sieciowy posiada dysk, przechowuje on tylko podstawowe
oprogramowanie. Pozostała część dysku jest używana jako pamięć podręczna
(cache), przechowująca kopie programów i danych, jakie były ostatnio używane
(czyli ładowane z serwera plików). Zarządzanie pamięcią cache jest
automatyczne - obiekty są z niej usuwane, gdy nowa wersja pliku jest zapisana
na serwerach plików z którymi współpracuje dany komputer sieciowy.
14 / 1
Modele architektury
Cienki klient (thin client)
Nazwa cienki klient (thin client) odnosi się do warstwy oprogramowania, która
prezentuje okienkowy interfejs użytkownika na lokalnym komputerze, podczas
gdy wszystkie programy są uruchamiane na zdalnym serwerze.
Taka architektura cechuje się podobnie niskimi wymaganiami sprzętowymi i
kosztami eksploatacji jak komputer sieciowy, ale w miejsce ładowania kodu
aplikacji z serwera - wykonuje ten kod na tzw. serwerze obliczeniowym lub
serwerze aplikacji (compute server) - b. wydajny komputer, mogący równolegle
wykonywać wiele aplikacji (wieloprocesorowy lub klastrowy).
Implementacje tej architektury:
Systemy UNIX i Linux używają systemu okien X-11, który zarządza
wyświetlaczem i interaktywnymi urządzeniami wejściowymi (klawiatura,
mysz, etc) komputera na którym działa. X-11 dostarcza szerokiej gamy
bibliotek funkcji, stanowiących protokół X-11, używanych do wyświetlania
i manipulacji obiektów graficznych w oknach. X-11 jest nazywany
serwerem okien (window server process), klientami są natomiast wszystkie
aplikacje aktualnie używane w systemie. Procedury serwera są
wywoływane z użyciem mechanizmu RPC.
Citrix WinFrame dostarcza procesu cienkiego klienta, który może być
uruchomiony na wielu platformach w celu emulacji pulpitu mającego
dostęp do programów działających w systemie WinNT na zdalnych
hostach.
15 / 1
Komunikacja międzyprocesowa
To zadanie jest realizowane przez specjalną warstwę oprogramowania,
nazywaną pośrednią (middleware), która jest zaimplementowana na szczycie
warstw odpowiedzialnych za komunikację sieciową, dlatego opiera się na
protokołach UDP i TCP.
Warstwa pośrednia dzieli się na dwie podwarstwy:
niższa obsługuje protokół zapytanie odpowiedz
(request-reply protocol), szeregowanie
(marshalling) i zewnętrzną reprezentację
danych (external data representation), na
bazie UDP i TCP.
wyższa obsługuje zdalne wywołanie procedur
(remote procedure calling - RPC) i zdalne
wywołanie metod (remote method invocation
- RMI). RPC i RMI są abstrakcjami
dostępnymi z poziomu języków
programowania, takich jak C, C++ i Java.
Przykładami systemów zdalnego wywołania metod są CORBA i Java RMI.
16 / 1
Komunikacja międzyprocesowa
Określenia podstawowe
Przekazywanie komunikatów między dwoma procesami wymaga dwu operacji:
send i receive. Aby proces mógł się komunikować z innym procesem, musi on
wysłać komunikat, będący sekwencją bajtów do miejsca przeznaczenia, a tam
inny proces odbiera ten komunikat.
Miejsce przeznaczenia posiada kolejkę komunikatów. Proces wysyłający
powoduje dodanie komunikatu do zdalnej kolejki, a proces odbierający pobiera
komunikat z lokalnej kolejki.
Wyróżnia się dwa podstawowe rodzaje komunikacji:
synchroniczną, w której procesy wysyłający i odbierający synchronizują się
wzajemnie przy każdym komunikacie, a operacje send i receive są blokujące.
Każde wywołanie send powoduje zablokowanie procesu wysyłającego do czasu
gdy odpowiadająca mu operacja receive jest wykonana. Każde wywołanie receive
powoduje zablokowanie procesu odbiorcy do czasu odebrania komunikatu.
asynchroniczną, w której operacja send jest nie-blokująca - proces wysyłający
może kontynuować swoje działanie od momentu skopiowania komunikatu do
lokalnego bufora transmisji, po czym sama transmisja zachodzi równolegle z
normalnym działaniem procesu wysyłającego. Operacja receive może mieć
wariant blokujący lub nie-blokujący. W drugim przypadku, proces odbiorczy
kontynuuje normalne działanie po wywołaniu receive. Operacja receive ma
przekazany bufor na komunikat, który ma zapełnić w tle i powiadomić proces
odbierający o tym zdarzeniu poprzez przerwanie lub polling.
17 / 1
Komunikacja międzyprocesowa
Punkty dostarczania komunikatów
W obrębie protokołów Internet-u, komunikaty są kierowane do pary (adres IP,
lokalny port).
Definicja: lokalny port jest punktem dostarczania komunikatów w
komputerze, identyfikowanym przez numer, będący wartością
całkowitą krótką.
Port może być skojarzony z co najwyżej jednym odbiorcą, ale z wieloma
zródłami.
Proces może używać wielu portów do odbioru komunikatów.
Serwery zwykle publikują numery swoich portów, przeznaczonych dla
klientów. Dowolny proces znający numer portu serwera może wysłać do
niego komunikat (o uzgodnionym formacie).
Jeśli klienci mają używać ustalonych adresów IP serwera do dostępu do jego
usług, to usługi serwera muszą być zawsze uruchamiane na tym samym
komputerze. Inne rozwiązania:
klienci wywołują usługi po nazwie i używa się serwera nazw (lub tzw. binder-a)
do translacji nazw na adresy w czasie działania systemu. To pozwala stosować
relokację usług, ale nie pozwala stosować migracji (zmiana położenia w czasie
działania systemu)
używanie identyfikatorów niezależnych od położenia, o ile system operacyjny na
to pozwala. To umożliwia relokację i migrację usług.
18 / 1
Komunikacja międzyprocesowa
Gniazda (sockets)
Definicja: gniazdo jest abstrakcją, która stanowi punkt połączeniowy
komunikacji międzyprocesowej.
Komunikacja międzyprocesowa polega na transmisji komunikatów pomiędzy
gniazdem jednego procesu i gniazdem drugiego procesu.
Aby proces mógł odbierać komunikaty, jego gniazdo musi być przypisane do
lokalnego portu i jednego z adresów IP komputera, na którym proces działa.
Komunikaty wysłane pod dany adres IP i port mogą być odebrane tylko przez
proces, który ma gniazdo skojarzone z tym adresem i portem.
Procesy mogą używać tego samego gniazda do wysyłania i odbioru komunikatów
Każdy komputer ma dużą ilość (216) portów przeznaczonych do użytku przez
lokalne procesy do odbioru komunikatów
Procesy mogą używać wielu portów do odbioru komunikatów, ale nie mogą
współdzielić portów z innymi procesami na tym samym komputerze. Wyjątkiem
są procesy używające techniki IP multicast - one mogą dzielić porty
Dowolna ilość procesów może wysyłać komunikaty do tego samego portu
Każde gniazdo jest skojarzone z konkretnym protokołem (UDP lub TCP)
19 / 1
Komunikacja międzyprocesowa
Adresy internetowe w Javie
Klasa InetAddress reprezentuje adresy internetowe (IP) . Użytkownicy tej klasy
odwołują się do komputerów za pomocą nazw hostów usługi Domain Name
Service. Na przykład, aby utworzyć obiekt reprezentujący adres internetowy
hosta, który ma nazwę DNS galaxy.uci.agh.edu.pl, należy napisać:
InetAddress aHost = InetAddress.getByName("galaxy.uci.agh.edu.pl");
Metoda getByName może wygenerować wyjątek UnknownHostException gdy
host o podanej nazwie nie istnieje, dlatego należy zadbać o odpowiednią
obsługę tego wyjątku - czego tu nie pokazano !
Użytkownicy tej klasy nie muszą znać adresu IP ani wnikać w szczegóły
formatu adresu (czy jest to IPv4 albo IPv6).
20 / 1
Komunikacja międzyprocesowa
Protokół UDP
Datagram przesyłany z użyciem UDP jest transmitowany bez potwierdzania i
powtarzania w przypadku błądu. Jeśli wystąpi błąd, komunikat może w ogóle nie
być odebrany.
Proces który zamierza wysyłać lub odbierać komunikaty musi najpierw utworzyć
gniazdo przypisane do adresu IP i lokalnego portu. Proces serwera przypisuje
swoje gniazdo do tego portu serwera, który jest udostępniony klientom. Proces
klienta przypisuje swoje gniazdo do dowolnego nie zajętego portu.
Metoda receive zwraca poza komunikatem, adres IP i numer portu nadawcy,
pozwalając odbiorcy komunikatu wysłać odpowiedz.
Nie-blokująca się metoda send: kończy ona działanie, gdy dostarczy komunikat
do bufora protokołu UDP, który zajmuje się transmisją do gniazda docelowego.
Po dostarczeniu, komunikat jest umieszczony w kolejce gniazda przypisanego do
odbiorcy i może być odebrany przez wywołanie metody receive danego gniazda.
Komunikaty są niszczone po osiągnięciu punktu docelowego (adres IP i port),
gdy nie istnieje proces mający gniazdo przypisane do tego portu.
Blokująca się metoda receive: receive czeka na odebranie datagramu, o ile nie
określono timeout-u dla gniazda. Jeśli proces wywołuje receive i musi
kontynuować swoje działanie, to powinien w tym celu powołać nowy wątek.
Metoda receive blokująca się w nieskończoność jest potencjalnie niebezpieczna,
dlatego możliwe jest ustanowienie timeout-u dla gniazda, aby wyeliminować
możliwość zawieszenia się serwera w oczekiwaniu na utracony komunikat.
21 / 1
Komunikacja międzyprocesowa
UDP w Javie
Java wspiera komunikację UDP za pomocą dwu klas:
DatagramPacket, która reprezentuje pakiet zawierający datagram. Instancje
klasy DatagramPacket mogą być transmitowane między procesami. Klasa ma 6
konstruktorów, m.in:
ctor który tworzy instancję na podstawie tablicy bajtów zawierających
komunikat, jego długości i adresu IP i numeru lokalnego portu gniazda w
procesie odbiorczym.
ctor do użytku przy odbiorze komunikatu. Wymaga tylko 2 argumentów:
tablicy bajtów gdzie znajdzie się odebrany komunikat i długości komunikatu.
Instancja klasy mieści odebrany komunikat, jego długość, adres IP i port.
Komunikat może być udostępniony za pomocą metody getData.
DatagramSocket, która obsługuje gniazda używane do wysyłania i odbioru
datagramów UDP. Klasa ma 5 konstruktorów, m.in:
ctor bez parametrów, dla procesów mogących użyć dowolnego portu
ctory z argumentami typów: (int port), (int port, InetAddress addr),
(SocketAddress addr).
Te ctory generują wyjątek SocketException gdy port jest już zajęty, lub wybrano
tzw. znany port (well-known port) (w systemie UNIX/Linux).
22 / 1
Komunikacja międzyprocesowa
UDP w Javie
Klasa DatagramSocket ma dodatkowe metody, m.in:
send i receive służą do transmisji datagramów miedzy gniazdami. Argumentem
metody send jest instancja klasy DatagramPacket, zawierająca komunikat i
adres. Argumentem metody receive jest pusta instancja klasy DatagramPacket,
w której po powrocie znajdzie się odebrany komunikat, wraz z danymi o długości
i zródle. Obie metody mogą wygenerować wyjątek IOException.
setSOTimeout pozwala ustawić timeout dla gniazda. Wtedy metoda receive
blokuje się na ten okres czasu, a jeśli komunikat nie nadejdzie to generowany jest
wyjątek InterruptedIOException.
connect jest używana do połączenia z wybranym zdalnym portem na maszynie
mającej znany adres IP. W tym przypadku gniazdo może wysyłać i odbierać
komunikaty tylko z tego adresu.
Dla ilustracji tych zagadnień dalej dyskutowane są przykłady kodu programów
klienta i serwera.
Serwer najpierw tworzy gniazdo przypisane do swojego portu 9876, a
następnie czeka w pętli na komunikat od klienta, po jego odbiorze
odpowiada wysyłając jego kopię do klienta.
Klient najpierw tworzy gniazdo, wysyła komunikat do serwera na port
9876 i czeka na odpowiedz.
23 / 1
Komunikacja międzyprocesowa
UDP w Javie: przykład kodu serwera
1public class UDPServer {
2 public static void main(String[] args) {
3 try {
4 // args contain message content and server hostname
5 DatagramSocket aSocket = new DatagramSocket(9876);
6 byte[] buffer = new byte[1024];
7 while(true) {
8 DatagramPacket request = new DatagramPacket(buffer, buffer.length);
9 aSocket.receive(request);
10 DatagramPacket reply = new DatagramPacket(request.getData(),
11 request.getLength(), request.getAddress(), request.getPort());
12 aSocket.send(reply);
13 }
14 } catch (SocketException ex) {
15 Logger.getLogger(UDPServer.class.getName()).log(Level.SEVERE, null, ex);
16 } catch (IOException ex) {
17 Logger.getLogger(UDPServer.class.getName()).log(Level.SEVERE, null, ex);
18 }
19 }
20}
24 / 1
Komunikacja międzyprocesowa
UDP w Javie: przykład kodu klienta
12public class UDPClient {
13 public static void main(String[] args) {
14 try {
15 if(args.length < 2) {
16 System.out.println("Usage: UDPClient
");
17 System.exit(-1);
18 }
19 DatagramSocket aSocket = new DatagramSocket();
20 byte[] msg = args[0].getBytes();
21 System.out.println("Sent: " + args[0]);
22 InetAddress aHost = InetAddress.getByName(args[1]);
23 int serverPort = 9876;
24 DatagramPacket request = new DatagramPacket(msg, args[0].length(), aHost, serverPo
25 aSocket.send(request);
26 byte[] buffer = new byte[1024];
27 DatagramPacket reply = new DatagramPacket(buffer, buffer.length);
28 aSocket.receive(reply);
29 System.out.println("Reply: " + new String(reply.getData()));
30 aSocket.close();
31 } catch (SocketException ex) {
32 Logger.getLogger(UDPClient.class.getName()).log(Level.SEVERE, null, ex);
33 } catch (UnknownHostException ex) {
34 Logger.getLogger(UDPClient.class.getName()).log(Level.SEVERE, null, ex);
35 } catch (IOException ex) {
36 Logger.getLogger(UDPClient.class.getName()).log(Level.SEVERE, null, ex);
37 }
38 }
39
40}
25 / 1
Komunikacja międzyprocesowa
Komunikacja z użyciem protokołu TCP
API wspierające komunikację TCP dostarcza abstrakcji będącej strumieniem bajtów,
do którego dane mogą być zapisane i z którego dane mogą być odczytane. Abstrakcja
ta ukrywa pewne cechy sieci, takie jak:
rozmiar komunikatu: aplikacja może decydować, jak dużo danych zapisuje lub
odczytuje ze strumienia. Bazowa implementacja protokołu strumieni TCP
decyduje jak dużo danych zebrać przed wysłaniem pakietu IP.
komunikaty utracone: protokół TCP używa potwierdzeń. Jeśli nadawca nie
otrzyma potwierdzenia w czasie <= od ustawionego timeout-u, to retransmituje
komunikat.
kontrola przepływu (flow control): protokół TCP stara się sterować szybkością
transmisji, aby wyrównać prędkości nadawania i odbioru. Jeśli proces zapisujący
jest za szybki dla procesu odczytującego, to jest on blokowany aż proces
odczytujący upora się z danymi.
duplikaty i uporządkowanie: pakiet IP ma przypisany identyfikator, co pozwala
odbiorcy znalezć i usunąć duplikaty, lub uporządkować pakiety, które zostały
dostarczone w innej kolejności.
przeznaczenie komunikatów: para komunikujących się procesów ustanawia
połączenie zanim zaczną wymieniać dane przez strumień. Kiedy połączenie jest
ustanowione, procesy mogą po prostu zapisywać do i odczytywać ze strumienia,
bez używania adresów IP i numerów portów. Ustanowienie połączenia wymaga
wysłania żądania connect od klienta do serwera, a następnie wysłania żądania
accept od serwera do klienta.
26 / 1
Komunikacja międzyprocesowa
Komunikacja z użyciem protokołu TCP
Kiedy para komunikujących się procesów ustanawia połączenie, jeden z nich pełni rolę
klienta, a drugi pełni rolę serwera.
Rola klienta wymaga utworzenia gniazda przypisanego do dowolnego portu, a
następnie wysłania żądania connect w celu uzyskania połączenia z gniazdem
zdalnego serwera.
Rola serwera wymaga utworzenia gniazda nasłuchującego, które jest przypisane
do portu serwera i oczekującego na żądania połączeń od klientów. Nasłuchujące
gniazdo zarządza kolejką, w której umieszczane są żądania połączeń nadchodzące
od klientów. Kiedy serwer akceptuje połączenie, tworzone jest nowe gniazdo do
komunikacji serwera z danym klientem, zachowując oryginalne gniazdo dla
nasłuchiwania żądań połączeń od innych klientów.
Para gniazd (klienta i serwera) jest połączona parą strumieni, po jednym w każdym
kierunku. A więc każde gniazdo ma strumień wejściowy i strumień wyjściowy. Dzięki
temu jeden z procesów może wysłać informację do drugiego, zapisując w swoim
strumieniu wyjściowym, a drugi proces otrzymuje tę informację przez odczyt ze swego
strumienia wejściowego.
UWAGA: dwa komunikujące się procesy muszą uzgodnić format danych wymienianych
przez strumień: jeśli jeden proces zapisuje wartość całkowitą i zmiennoprzecinkową, to
drugi musi czytać dane w tej samej kolejności i odpowiednio je interpretować. Jeśli
dwa procesy nie kooperują odpowiednio, odbiorca może napotkać błędy przy
interpretowaniu danych lub może zostać zablokowany w oczekiwaniu na dane.
27 / 1
Komunikacja międzyprocesowa
TCP w Javie: przykład kodu klienta
1public class TCPClient {
2 public static void main (String args[]) {
3 // arguments supply message and hostname
4 if(args.length < 2) {
5 System.out.println("Usage: TCPClient ");
6 System.exit(-1);
7 }
8 Socket s = null;
9 try {
10 int serverPort = 7896;
11 s = new Socket(args[1], serverPort);
12 DataInputStream in = new DataInputStream( s.getInputStream());
13 DataOutputStream out =new DataOutputStream( s.getOutputStream());
14 out.writeUTF(args[0]); // UTF is a string encoding
15 String data = in.readUTF(); // read a line of data from the stream
16 System.out.println("Received: "+ data) ;
17 } catch(UnknownHostException e) { System.out.println("Socket:"+e.getMessage());
18 } catch(EOFException e) { System.out.println("EOF:"+e.getMessage());
19 } catch(IOException e) { System.out.println("readline:"+e.getMessage());
20 } finally {
21 if(s!=null)
22 try {
23 s.close();
24 } catch (IOException e) { System.out.println("close:"+e.getMessage()); }
25 }
26 }
27}
28 / 1
Komunikacja międzyprocesowa
TCP w Javie: przykład kodu serwera
1import java.net.*;
2import java.io.*;
3public class TCPServer {
4 public static void main (String args[]) {
5 try{
6 int serverPort = 7896; // the server port
7 ServerSocket listenSocket = new ServerSocket(serverPort);
8 while(true) {
9 Socket clientSocket = listenSocket.accept();
10 Connection c = new Connection(clientSocket);
11 }
12 } catch(IOException e) {System.out.println("Listen socket:"+e.getMessage());}
13 }
14}
29 / 1
Komunikacja międzyprocesowa
TCP w Javie: przykład kodu serwera
15class Connection extends Thread {
16 DataInputStream in;
17 DataOutputStream out;
18 Socket clientSocket;
19 public Connection (Socket aClientSocket) {
20 try {
21 clientSocket = aClientSocket;
22 in = new DataInputStream( clientSocket.getInputStream());
23 out =new DataOutputStream( clientSocket.getOutputStream());
24 this.start();
25 } catch(IOException e) {System.out.println("Connection:"+e.getMessage());}
26 }
27 public void run(){
28 try { // an echo server
29 String data = in.readUTF(); // read a line of data from the stream
30 out.writeUTF(data);
31 } catch (EOFException e){ System.out.println("EOF:"+e.getMessage());
32 } catch(IOException e) { System.out.println("readline:"+e.getMessage());
33 } finally { try { clientSocket.close(); } catch (IOException e) { }
34 }
35 }
36}
30 / 1
Komunikacja międzyprocesowa
TCP w C: przykład kodu klienta
1#include
2#include
3#include
4#define SERVER_PORT 12345 // arbitralne, klient i server
5#define BUF_SIZE 4096 // rozmiar bloku
6int main(int argc, char **argv) {
7 int c, s, bytes;
8 char buf[BUF_SIZE]; // bufor dla odebranego pliku
9 struct hostent *h; // info o serwerze
10 struct sockaddr_in channel; // struktura adresowa: domena, port,adres IP
11 if (argc != 3) fatal("Usage: client server-name file-name");
12 h = gethostbyname(argv[1]); // odczyt adresu IP hosta
13 if (!h) fatal("gethostbyname failed");
14 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // zwraca deskryptor gniazda
15 if (s <0) fatal("socket");
16 memset(&channel, 0, sizeof(channel));
17 channel.sin_family= AF_INET;
18 memcpy(&channel.sin_addr.s_addr, h->h_addr, h->h_length);
19 channel.sin_port= htons(SERVER_PORT);
20 c = connect(s, (struct sockaddr *) &channel, sizeof(channel));
21 if (c < 0) fatal("connect failed");
22 // Polaczenie ustanowione. Przeslij nazwe pliku zakonczona bajtem 0.
23 write(s, argv[2], strlen(argv[2])+1);
24 // Pobierz plik i przeslij na stdout.
25 while (1) {
26 bytes = read(s, buf, BUF_SIZE); // czytanie z gniazda
27 if (bytes <= 0) exit(0); // sprawdzenie konca
28 write(1, buf, bytes); // przeslij na stdout
29 }
30}
31fatal(char *string) {
32 printf("%s\n", string);
33 exit(1);
31 / 1
34}
Komunikacja międzyprocesowa
TCP w C: przykład kodu serwera
1// Kod serwera
2#include
3#include
4#include
5#include
6#include
7
8#define SERVER_PORT 12345 // arbitralne, klient i server to samo
9#define BUF_SIZE 4096 // rozmiar bloku
10#define QUEUE_SIZE 10
11
12int main(int argc, char *argv[]) {
13 int s, b, l, fd, sa, bytes, on = 1;
14 char buf[BUF_SIZE]; // bufor dla wysylanego pliku
15 struct sockaddr_in channel; // adres IP
16 // Utworz strukture zaw. adres gniazda.
17 memset(&channel, 0, sizeof(channel)); // zeruj strukture gniazda
18 channel.sin_family = AF_INET;
19 channel.sin_addr.s_addr = htonl(INADDR_ANY);
20 channel.sin_port = htons(SERVER_PORT);
21 // Pasywne otwarcie. Czekaj na polaczenie.
22 s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // tworz gniazdo
23 if (s < 0) fatal("socket failed");
24 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
32 / 1
Komunikacja międzyprocesowa
TCP w C: przykład kodu serwera
25 b = bind(s, (struct sockaddr *) &channel, sizeof(channel));
26 if (b < 0) fatal("bind failed");
27 l = listen(s, QUEUE_SIZE); // rozmiar kolejki
28 if (l < 0) fatal("listen failed");
29 // Gniazdo przygotowane. Czekaj na polaczenie i obsluz je
30 while (1) {
31 sa = accept(s, 0, 0); // oczekuj na zadanie
32 if (sa < 0) fatal("accept failed");
33 read(sa, buf, BUF_SIZE); // odczytaj nazwe pliku z gniazda
34 // Otworz plik i zwroc uchwyt.
35 fd = open(buf, O_RDONLY); // otworz wysylany plik
36 if (fd < 0) fatal("open failed");
37 while (1) {
38 bytes = read(fd, buf, BUF_SIZE); // odczyt pliku
39 if (bytes <= 0) break; // sprawdzenie konca
40 write(sa, buf, bytes); // wyslij bufor do gniazda
41 }
42 close(fd); // zamknij plik
43 close(sa); // zamknij polaczenie
44 }
45}
46
47fatal(char *string) {
48 printf("%s\n", string);
49 exit(1);
50}
33 / 1
Komunikacja międzyprocesowa
Zewnętrzna reprezentacja danych
Informacja zawarta w działającym programie jest reprezentowana przez
struktury danych - np. zbiór połączonych obiektów - podczas gdy informacja
wewnątrz komunikatu ma postać ciągu bajtów. W konsekwencji, dane muszą
być spłaszczone (przekształcone na ciąg bajtów) przed transmisją i odtworzone
po odebraniu. Dodatkowo, indywidualne dane typów prostych transmitowane w
komunikacie mogą używać odmiennych reprezentacji na odmiennych
platformach.
W celu umożliwienia wymiany danych w tych warunkach stosuje się jedną z
metod:
wartości danych są przekształcane do uzgodnionego zewnętrznego formatu przed
transmisją i przekształcane do lokalnego formatu po odebraniu ich; jeśli dwa
komputery są tego samego typu to konwersję pomija się.
wartości danych są transmitowane w formacie obowiązującym na maszynie
nadawcy, razem z informacją o używanym formacie, a odbiorca przekształca je w
razie potrzeby.
Aby zapewnić możliwość użycia RPC lub RMI, każdy typ danych przesyłanych
jako argument lub zwracanych jako rezultat musi być serializowalny, a
pojedyncze dane typów prostych muszą być reprezentowane w uzgodnionym
formacie, nazywanym zewnętrzną reprezentacją danych (external data
representation).
34 / 1
Komunikacja międzyprocesowa
Szeregowanie (Marshalling)
Szeregowanie jest to proces zamiany kolekcji danych o dowolnej organizacji
na formę możliwą do transmisji w postaci komunikatu.
Wymaga to więc translacji danych o pewnej strukturze i danych
prostych do postaci zgodnej z zewnętrzną reprezentacją danych.
Deszeregowanie jest to proces odtwarzania oryginalnej kolekcji danych, które
odebrano w postaci, jaką definiuje zewnętrzna reprezentacja
danych. Wymaga to odtworzenia danych prostych z ich
zewnętrznej reprezentacji i odtworzenia złożonych struktur
danych.
Znane rozwiązania XDR i szeregowania:
Sun RPC XDR
CORBA Common Data Representation, która może być używana w wielu
językach programowania
serializacja obiektów w Javie, która pozwala spłaszczać i przekształcać do
postaci XDR dowolny pojedynczy obiekt lub drzewo obiektów, które mają być
transmitowane w komunikacie lub zapisane na dysku.
We wszystkich wymienionych przypadkach, wymagane operacje są
obsługiwane przez warstwę pośrednią (middleware layer) bez konieczności
angażowania programisty aplikacji rozproszonej.
35 / 1
Komunikacja międzyprocesowa
Protokół request-reply
36 / 1
RPC
Architektura
Proces serwera definiuje w swoim interfejsie usług procedury dostępne dla zdalnych
wywołań. RPC jest zaimplementowane z wykorzystaniem protokołu
żądanie-odpowiedz (request-reply protocol).
Konfiguracja oprogramowania pracującego zgodnie z RPC:
RPC zostało wprowadzone przez firmę Sun Microsystems w 1995 r. dla komunikacji w
modelu klient-serwer obsługującym sieciowy system plików Sun NFS. Rozwiązanie to
jest też nazywa ONC (Open Network Computing) RPC i jest dostępne jako moduł
systemu operacyjnego Sun Solaris, innych systemów Unix i Linux, a także wielu innych
SO.
37 / 1
RPC
Działanie
Proces klienta, który zamierza używać zdalnej usługi musi posiadać
oddzielną procedurę łącznikową (stub procedure) dla każdej procedury
występującej w interfejsie usługi. Proces klienta musi ponadto posiadać
moduł komunikacyjny (communication module).
Rola procedury łącznikowej jest podobna do roli pośrednika (proxy) - jawi
się ona (i tylko ona) klientowi jako lokalna procedura, ale zamiast
wykonywać docelową operację, wykonuje serializację identyfikatora
procedury i jej argumentów w komunikat żądania, który wysyła za pomocą
swojego modułu komunikacyjnego do serwera. Kiedy nadchodzi komunikat
odpowiedzi, wykonuje deserializację w celu odbioru rezultatu.
Proces serwera zawiera moduł dyspozytora (dispatcher) wraz z oddzielną
procedurą łącznikową serwera serwer stub procedure) i oddzielną
procedurą usługową (service procedure) dla każdej procedury
zdefiniowanej w interfejsie usługi.
Dispatcher wybiera jedną z procedur łącznikowych serwera na podstawie
identyfikatora procedury zawartego w komunikacie żądania.
Procedura łącznikowa serwera (serwer stub procedure) jest podobna do
metody szkieletowej, która odtwarza argumenty z komunikatu żądania,
wywołuje odpowiednią procedurę usługową i marszrutuje zwracane wartości
w komunikacie odpowiedzi.
Procedury usługowe ( service procedure) implementują procedury
zdefiniowane w interfejsie usługi.
38 / 1
RPC
Proces budowy rozproszonej aplikacji
Definicja struktur danych i interfejsu usługi dostarczanej przez serwer
Generowanie szkieletu aplikacji z wykorzystaniem narzędzia rpcgen
Uzupełnienie kodu procedur usługowych w celu spełnienia założeń
funkcjonalnych rozproszonej aplikacji
Kompilacja i łączenie programów klienta i serwera, wykonywane z
wykorzystaniem narzędzia make
39 / 1
Przykład aplikacji RPC
Definicja interfejsu usługi servera
Rozproszona aplikacja obsługuje sklep z utworami muzycznymi. Struktury
danych i interfejs usług definiuje następujący plik:
1/* SKLEP.x: Sklep z utworami muz. */
2struct Utwor {
3 int Id;
4 string Autor<>; //tabl. o nieokreslonym rozmiarze
5 string Tytul<>;
6 float Cena;
7};
8
9program SKLEPPROG {
10 version SKLEPVERS {
11 int OtwarcieSklepu(void) = 1;
12 int DodanieUtworu(struct Utwor) = 2;
13 int IleUtworow(void) = 3;
14 struct Utwor PobranieUtworu(int) = 4;
15 int ZamkniecieSklepu(void) = 5;
16 } = 1;
17} = 0x20000001;
40 / 1
RPC
Generator kodu rpcgen
Na podstawie przygotowanego pliku definicyjnego można automatycznie wygenerować
pliki zródłowe przykładowej aplikacji:
1$ rpcgen -a -N sklep.x
2$ ls
3Makefile.sklep sklep.x sklep_clnt.c sklep_svc.c sklep.h sklep_client.c sklep_server.c
Powstałe pliki reprezentują odpowiednio:
sklep.h plik nagłówkowy z definicjami poszcz. identyfikatorów
sklep_client.c szkielet aplikacji klienckiej
sklep_server.c szkielet serwera
sklep_clnt.c pieniek klienta
sklep_svc.c pieniek serwera
Makefile.sklep plik Makefile zarządzający kompilacją projektu.
Kompilację najprościej jest przeprowadzić z wykorzystaniem programu make:
1$ make -f Makefile.sklep
2 cc -g -c -o sklep_clnt.o sklep_clnt.c
3 cc -g -c -o sklep_client.o sklep_client.c
4 cc -g -o sklep_client sklep_clnt.o sklep_client.o -lnsl
5 cc -g -c -o sklep_svc.o sklep_svc.c cc -g -c -o sklep_server.o sklep_server.c
6 cc -g -o sklep_server sklep_svc.o sklep_server.o -lnsl
W wyniku kompilacji powinny powstać m.in. dwa programy: sklep_client i
sklep_server
41 / 1
Przykład aplikacji RPC
Deklaracje podstawowe
1/* Please do not edit this file. It was generated using rpcgen. */
2
3#ifndef _SKLEP_H_RPCGEN
4#define _SKLEP_H_RPCGEN
5
6#include
7
8#ifdef __cplusplus
9extern "C" {
10#endif
11
12struct Utwor {
13 int Id;
14 char *Autor;
15 char *Tytul;
16 float Cena;
17};
18typedef struct Utwor Utwor;
19
20#define SKLEPPROG 0x20000001
21#define SKLEPVERS 1
42 / 1
Przykład aplikacji RPC
Deklaracje funkcji - wersja STDC i C++
23#if defined(__STDC__) || defined(__cplusplus)
24#define OtwarcieSklepu 1
25extern int * otwarciesklepu_1(CLIENT *);
26extern int * otwarciesklepu_1_svc(struct svc_req *);
27#define DodanieUtworu 2
28extern int * dodanieutwor_1(struct Utwor , CLIENT *);
29extern int * dodanieutworu_1_svc(struct Utwor , struct svc_req *);
30#define IleUtworow 3
31extern int * ileutworow_1(CLIENT *);
32extern int * ileutworow_1_svc(struct svc_req *);
33#define PobranieUtworu 4
34extern struct Utwor * pobranieutworu_1(int , CLIENT *);
35extern struct Utwor * pobranieutworu_1_svc(int , struct svc_req *);
36#define ZamkniecieSklepu 5
37extern int * zamknieciesklepu_1(CLIENT *);
38extern int * zamknieciesklepu_1_svc(struct svc_req *);
39extern int sklepprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
43 / 1
Przykład aplikacji RPC
Deklaracje funkcji - wersja K&R C
41#else /* K&R C */
42#define OtwarcieSklepu 1
43extern int * otwarciesklepu_1();
44extern int * otwarciesklepu_1_svc();
45#define DodanieUtworu 2
46extern int * dodanieutworu_1();
47extern int * dodanieutworu_1_svc();
48#define IleUtworow 3
49extern int * ileutworow_1();
50extern int * ileutworow_1_svc();
51#define PobranieUtworu 4
52extern struct Utwor * pobranieutworu_1();
53extern struct Utwor * pobranieutworu_1_svc();
54#define ZamkniecieSklepu 5
55extern int * zamknieciesklepu_1();
56extern int * zamknieciesklepu_1_svc();
57extern int sklepprog_1_freeresult ();
58#endif /* K&R C */
44 / 1
Przykład aplikacji RPC
Plik XDR
Plik XDR (ang. eXternal Data Representation) jest generowany automatycznie
przez rpcgen i zawiera procedury konwersji dla danych o typach zdefiniowanych
przez użytkownika.
1/* Please do not edit this file. It was generated using rpcgen. */
2
3#include "Sklep.h"
4
5bool_t
6xdr_Utwor (XDR *xdrs, Utwor *objp)
7{
8 register int32_t *buf;
9 if (!xdr_int (xdrs, &objp->Id))
10 return FALSE;
11 if (!xdr_string (xdrs, &objp->Autor, ~0))
12 return FALSE;
13 if (!xdr_string (xdrs, &objp->Tytul, ~0))
14 return FALSE;
15 if (!xdr_float (xdrs, &objp->Cena))
16 return FALSE;
17 return TRUE;
18}
45 / 1
Przykład aplikacji RPC
Kod serwera - lista utworów i funkcja pomocnicza
Pierwsza część kodu serwera zawiera deklarację listy utworów, będącej zmienną
globalną. Po niej następuje definicja funkcji pomocniczej, używanej do
tworzenia nowego utworu.
7#include "Sklep.h"
8#include // naglowki bibl. GLib
9
10
11GSList* utwory = NULL; // lista utworow
12
13Utwor*
14nowy(int id, char *autor, char *tytul, float cena) {
15 Utwor* u = (Utwor*)malloc(sizeof(Utwor));
16 if(u) {
17 u->Id = id;
18 u->Autor = autor;
19 u->Tytul = tytul;
20 u->Cena = cena;
21 }
22 return u;
23}
46 / 1
Przykład aplikacji RPC
Kod serwera - procedura inicjująca pracę serwera
Tworzy ona dwa obiekty typu Utwor i dodaje je do listy utworów. W
zastosowaniach praktycznych należałoby tu zrealizować np. odczyt listy
utworów z pliku lub bazy danych.
25int *
26otwarciesklepu_1_svc(struct svc_req *rqstp) {
27 static int result;
28
29 printf("otwarciesklepu_1_svc() ...\n");
30 Utwor* u = nowy(1, "L.M.", "Scala", 49.99);
31 if(u) utwory = g_slist_append(utwory, u);
32 u = nowy(2, "S.M.", "Amtrak", 99.99);
33 if(u) utwory = g_slist_append(utwory, u);
34 printf("Sklep ma %d utwory\n", g_slist_length(utwory));
35
36 return &result;
37}
47 / 1
Przykład aplikacji RPC
Kod serwera - procedury użytkowe
Przedstawione procedury użytkowe służą do dodania utworu do listy i odczytu
ilości utworów.
39int *
40dodanieutworu_1_svc(struct Utwor arg1, struct svc_req *rqstp) {
41 static int result;
42
43 printf("dodanieutworu_1_svc() ...\n");
44 utwory = g_slist_append(utwory, &arg1);
45 printf("Sklep ma %d utwory\n", g_slist_length(utwory));
46
47 return &result;
48}
49
50int *
51ileutworow_1_svc(struct svc_req *rqstp) {
52 static int result;
53
54 result = g_slist_length(utwory);
55
56 return &result;
57}
48 / 1
Przykład aplikacji RPC
Kod serwera - procedura odczytu i końcowa
Przedstawione procedury użytkowe służą do odczytu utworu z listy i
zakończenia pracy serwera.
59struct Utwor *
60pobranieutworu_1_svc(int arg1, struct svc_req *rqstp) {
61 static struct Utwor result;
62
63 result = *((Utwor*)g_slist_nth_data(utwory, arg1));
64
65 return &result;
66}
67
68int *
69zamknieciesklepu_1_svc(struct svc_req *rqstp) {
70 static int result;
71
72 // aktualizacja danych
73 // zwolnienie utworow zawartych w liscie
74 // zwolnienie listy
75 g_slist_free(utwory);
76
77 return &result;
78}
49 / 1
Przykład aplikacji RPC
Kod klienta
1/* This is sample code generated by rpcgen. */
2#include "Sklep.h"
3
4void toString(Utwor* u) {
5 if(u)
6 printf("Id(%d), Autor(%s), Tytul(%s), Cena(%5.2f)\n",
7 u->Id, u->Autor, u->Tytul, u->Cena);
8}
9
10void sklepprog_1(char *host) {
11 CLIENT *clnt;
12 int *result_1, *result_2, *result_3, *result_5;
13 struct Utwor dodanieutworu_1_arg1;
14 struct Utwor *result_4;
15 int pobranieutworu_1_arg1 = 1;
16#ifndef DEBUG
17 clnt = clnt_create (host, SKLEPPROG, SKLEPVERS, "udp");
18 if (clnt == NULL) {
19 clnt_pcreateerror (host); exit (1);
20 }
21#endif /* DEBUG */
50 / 1
Przykład aplikacji RPC
Kod klienta
22 result_1 = otwarciesklepu_1(clnt);
23 if (result_1 == (int *) NULL)
24 clnt_perror (clnt, "call failed");
25 dodanieutworu_1_arg1.Id = 3;
26 dodanieutworu_1_arg1.Autor = "Chuck Mangione";
27 dodanieutworu_1_arg1.Tytul = "Children of Sanchez";
28 dodanieutworu_1_arg1.Cena = 9.90;
29 result_2 = dodanieutworu_1(dodanieutworu_1_arg1, clnt);
30 if (result_2 == (int *) NULL)
31 clnt_perror (clnt, "call failed");
32 result_3 = ileutworow_1(clnt);
33 if (result_3 == (int *) NULL)
34 clnt_perror (clnt, "call failed");
35 else
36 printf("Sklep oferuje %d utwory\n", *result_3);
37 result_4 = pobranieutworu_1(pobranieutworu_1_arg1, clnt);
38 if (result_4 == (struct Utwor *) NULL)
39 clnt_perror (clnt, "call failed");
40 else {
41 printf("Utwor o indeksie %d to: ", pobranieutworu_1_arg1);
42 toString(result_4);
43 }
44 result_5 = zamknieciesklepu_1(clnt);
45 if (result_5 == (int *) NULL) {
46 clnt_perror (clnt, "call failed");
47 }
51 / 1
Przykład aplikacji RPC
Kod klienta
48#ifndef DEBUG
49 clnt_destroy (clnt);
50#endif /* DEBUG */
51}
52
53int main (int argc, char *argv[])
54{
55 char *host;
56 if (argc < 2) {
57 printf ("usage: %s server_host\n", argv[0]); exit (1); }
58 host = argv[1];
59 sklepprog_1 (host);
60 exit (0);
61}
52 / 1
Wyszukiwarka
Podobne podstrony:
Wyklad SR 4
Wyklad SR 3
Sieci komputerowe wyklady dr Furtak
Wykład 05 Opadanie i fluidyzacja
WYKŁAD 1 Wprowadzenie do biotechnologii farmaceutycznej
mo3 wykladyJJ
ZARZĄDZANIE WARTOŚCIĄ PRZEDSIĘBIORSTWA Z DNIA 26 MARZEC 2011 WYKŁAD NR 3
Wyklad 2 PNOP 08 9 zaoczne
Wyklad studport 8
Kryptografia wyklad
Budownictwo Ogolne II zaoczne wyklad 13 ppoz
wyklad09
więcej podobnych podstron