Sieci neuronowe w prognozowaniu – podejmowanie decyzji i

Download Report

Transcript Sieci neuronowe w prognozowaniu – podejmowanie decyzji i

W. Bartkiewicz
Systemy rozproszone
Wykład 5. Komunikacja międzyprocesowa z wykorzystaniem
usług warstwy pośredniej - RPC
Oprogramowanie warstwy pośredniej
• Oprogramowanie warstwy pośredniej to aplikacja, która logicznie
przynależy do warstwy zastosowań, lecz zawiera wiele protokołów
ogólnego przeznaczenia, niezależnych od innych, bardziej konkretnych
aplikacji.
• Protokoły komunikacyjne oprogramowania warstwy pośredniej tworzą
podstawy usług komunikacyjnych wysokiego poziomu.
• Do podstawowych usług komunikacyjnych oprogramowania warstwy
pośredniej należą:
–
–
–
–
Zdalne wywołania procedur (RPC).
Zdalne wywołania obiektów.
Obsługa kolejek komunikatów.
Zaplecze strumieni komunikacji nośników ciągłych.
Zmodyfikowany model komunikacji
sieciowej
Zastosowanie
Warstwa pośrednia
Transport
Sieć
Łącze danych
Poziom fizyczny
Protokół aplikacji
Protokół warstwy pośredniej
Protokół transportu
Protokół sieci
Protokół łącza danych
Protokół fizyczny
Sieć
6
5
4
3
2
1
RPC – Wywołania procedur zdalnych
• RPC jest konstrukcją ukrywającą działania komunikacyjne pod znaną
abstrakcją wywołania procedury.
– Odwołanie do serwera ma po stronie klienta postać identyczną jak zwykłe
wywołanie podprogramu.
– Abstrakcja ta ukrywa w rzeczywistości wysłanie odpowiedniego
komunikatu do procesu serwera (określającego procedurę jaka ma zostać
wykonana przez serwer), wykonanie przez serwer odpowiadającego mu
przetwarzania i odesłanie odpowiedzi.
– Dane przesyłane przez klienta i niezbędne do wykonania przez serwer
procedury, podawane są jako wartości parametrów. Odpowiedź serwera
przekształcana jest na wartości parametrów wyjściowych oraz wartości
powrotne z funkcji.
Semantyka wywołań RPC
• Definicja zdalnej procedury określa parametry wejściowe i wyjściowe.
– Parametry wejściowe przekazywane są serwerowi jako wartości
argumentów w komunikacie zamawiającym i kopiowane do zmiennych,
które są przekazywane jako parametry do procedury w środowisku
wykonawczym serwera.
– Parametry wyjściowe zwracane są klientowi w komunikacie z odpowiedzią,
gdzie używa się ich do zastąpienia wartości odpowiednich zmiennych w
środowisku wywołania.
– Jeśli parametr używany jest jako wejściowy i wyjściowy, to jego wartość
musi zostać przesłana zarówno w zamówieniu jak i w odpowiedzi.
• Dostarczenie informacji o tym czy parametr używany jest na wejściu,
wyjściu, czy w obu przypadkach powoduje konieczność zastosowania w
każdym systemie RPC języka opisu interfejsu (IDL - Interface
Definition Language).
Namiastki klienta i serwera
• Jeśli dana procedura ma być wykonana zdalnie, to system RPC w
programie klienta umieszcza jej inną wersję, tzw. namiastkę procedury
(stub procedure) nazywaną namiastką klienta (client stub). Jest to
normalny, lokalny podprogram o sygnaturze zgodnej z procedurą
zdalną. Zgodność sygnatury osiągana jest poprzez wspólną definicję
interfejsu.
• Namiastka klienta umieszcza identyfikator procedury oraz parametry w
komunikacie, który wysyła do serwera wykorzystując lokalny SO.
Następnie blokuje się w oczekiwaniu na odpowiedź serwera.
• W programie serwera system RPC instaluje tzw. ekspedytora
(dispatcher) oraz namiastki procedur serwera (server stubs).
Ekspedytor na podstawie identyfikatora procedury w komunikacie z
zamówieniem wybiera odpowiednią namiastkę procedury, przekazując
jej argumenty.
Namiastki klienta i serwera (c.d.)
• Namiastka serwera rozpakowuje parametry z komunikatu, tworząc w
środowisku serwera odpowiednie bufory zarówno dla danych
wejściowych jak i wyników działania. Następnie wywołuje (również
poprzez zwykłe wywołanie lokalne) procedurę zdalną serwera.
Wykonuje ona swoje zadanie, zwracając również w normalny sposób
wynik namiastce serwera.
• Namiastka serwera umieszcza wynik działania procedury w
komunikacie z odpowiedzią dla klienta, wysyła go a następnie
przechodzi w stan oczekiwania na nadejście następnego komunikatu.
• Namiastka klienta odbiera komunikat, rozpakowuje z niego wyniki,
kopiuje je do obszarów pamięci w których oczekuje ich podprogram
klienta wywołujący procedurę zdalną i kończy działanie w zwykły
sposób.
Działanie RPC
Komputer klienta
Komputer serwera
Proces klienta
Wywoła- Przetaczanie Wysyłanie
parametrów
nie
zamówienia
lokalne
Lokalny Odwrotne
Przyjęcie
przetaczanie
powrót
wyników odpowiedzi
Klient
Namiastka
procedury
klienta
Moduł
komunikacyjny
(SO)
Odwrotne
Przyjęcie
przetaczanie Wykonazamówienia parametrów
nie
procedury
Wybór
procedury
Powrót
Wysłanie
Przetaczanie
odpowiedzi
wyników
Moduł
komunikacyjny
(SO)
Namiastka Procedura
procedury usługowa
Ekspedytor serwera
Specyfika wywołań RPC
• Procedura zdalna wykonywana jest w środowisku odmiennym od miejsca
jej wywołania, toteż nie ma dostępu do zmiennych środowiska
wywołania, np. do zmiennych globalnych zadeklarowanych w programie
wywołującym.
• Proces przekazywania danych między różnymi środowiskami
wykonawczymi nazywamy przetaczaniem (marshaling).
• W wieloplatformowych środowiskach RPC występuje konieczność
uwzględnienia różnych reprezentacji binarnych danych, tzn.
zdefiniowania protokołów ich przesyłania wspólnych dla serwera i
klienta.
• Przekazywanie przez proces w komunikacie do innych procesów adresów
komórek pamięci lub ich równoważników (np. odniesień, referencji,
uchwytów) jest bezcelowe. Tak więc generalnie argumenty i wyniki
procedur zdalnych nie powinny zawierać struktur danych ze wskaźnikami
do komórek pamięci.
• Systemy RPC radzą sobie zwykle z przetaczaniem wskaźników
(odniesień) do prostych tablic i struktur, kopiując (przesyłając) między
namiastkami klienta i serwera całe wskazywane dane.
Język Opisu Interfejsu (IDL)
• Aby uprościć korzystanie z mechanizmów RPC, interfejsy definiujące
zbiory podprogramów realizowanych przez serwer i możliwych do
wywołania przez klienta, określane są zazwyczaj w specjalnym języku
opisu interfejsu (IDL – Interface Definition Language).
• Zastosowanie wspólnej definicji interfejsu zarówno dla serwera, jak i
klienta zapewnia, że typy argumentów i wartości powrotnych oraz
protokoły przetaczania ich wartości, używane przez klienta będą zgodne
ze zdefiniowanymi na serwerze.
• Definicje interfejsów zapisane w IDL kompilowane są przy użyciu
kompilatora interfejsu, tworzącego składowe konsolidowane z
programami serwera i klienta.
Zadania kompilatora interfejsu
• Generowanie namiastek procedur klienta, odpowiadających sygnaturom
procedur w interfejsie. Namiastki te zostaną skompilowane i połączone
z programem klienta.
• Generowanie namiastek procedur serwera odpowiadających sygnaturom
procedur w interfejsie. Namiastki te zostaną skompilowane i połączone
z programem serwera.
• Na podstawie sygnatur procedur z interfejsu, określających typy
argumentów i wartości powrotnych, wygenerowanie operacji
przetaczania oraz odwrotnego przetaczania w każdej namiastce
procedury.
• Wygenerowanie dla każdej procedury z interfejsu szablonu
bibliotecznego charakterystycznego dla danego języka programowania
(na przykład w języku C/C++ pliku nagłówkowego z deklaracjami
sygnatur poszczególnych procedur). Programista dostarcza następnie ich
implementacji.
Wiązanie
• Definicja interfejsu określa tekstową nazwę usługi za pomocą której
odwołują się do niej klienty. Dla celów komunikacyjnych niezbędne jest
jednak dostarczenie klientom informacji o punkcie końcowym serwera.
• Przez wiązanie (binding) rozumiemy odwzorowanie nazwy usługi na
konkretny identyfikator komunikacyjny, określający punkt końcowy
serwera (identyfikator portu, grupy portów lub dowolna inna forma
miejsca przeznaczenia) do którego wysyłane będą komunikaty.
• Program klienta zawierać może sieciowy adres serwera. Oznacza to
jednak, że przy każdym przemieszczeniu serwera niezbędne jest
poinformowanie o tym wszystkich klientów.
• Łącznik (binder) jest odrębną usługą utrzymującą tablicę odwzorowań
nazw usług na punkty końcowe serwerów. Łącznik jest przykładem
usług nazewniczych, w których rejestruje się serwery aby umożliwić ich
lokalizację przez klientów.
Uszkodzenia dostaw
• Komunikaty od czasu do czasu są gubione przez nadawców, odbiorców
oraz bramy sieci.
• Sieci mogą ulegać podziałowi. Oznacza to, że jeden lub więcej węzłów
sieci może zostać oddzielony od reszty sieci.
• Procesy ulegają niekiedy awariom. Ogólnie biorąc nadawca nie jest w
stanie odróżnić awarii procesu od awarii komunikacyjnej. Jeśli proces
nie odpowiada pomimo wykonania ustalonej liczby N prób
skomunikowania się z nim, to zakłada się, że jest nieosiągalny. Wybór N
jest trudny: jeśli będzie za małe, to można omyłkowo uznać proces za
uszkodzony, natomiast gdy N będzie za duże to czas potrzebny na
wykrycie uszkodzenia może stać się zbyt długi.
• Nie ma uszkodzeń danych. Komunikaty są odbierane poprawnie –
zapewniają to mechanizmy wykrywania błędów na poziomie sieci.
Protokoły wymiany RPC
• Protokół zamówienia (request – R). Może być używany w sytuacji, gdy
procedura zdalna nie musi zwracać żadnej wartości, a klient nie wymaga
potwierdzenia, że ją wykonano. Klient może kontynuować działanie
natychmiast po wysłaniu komunikatu z zamówieniem, gdyż nie musi
czekać na komunikat z odpowiedzią.
• Protokół zamówienie – odpowiedź (request-reply – RR). Polega na
wymianie pary komunikatów zamówienie – odpowiedź. Nie są
wymagane komunikaty potwierdzające, gdyż za potwierdzenie
zamówienia klienta służy odpowiedź serwera.
• Protokół zamówienie – odpowiedź – potwierdzenie (request-replyacknowledge reply – RRA).Zakłada wymianę trzech komunikatów, z
dodatkowym potwierdzeniem od klienta do serwera. Komunikat
potwierdzający odpowiedź zawiera identyfikator zamówienia, wzięty z
odpowiedzi. Jego nadejście powoduje potraktowanie przez serwer
wszystkich odpowiedzi o mniejszych identyfikatorach zamówień jako
potwierdzonych. Tak więc zgubienie jakiegoś potwierdzenia jest
nieszkodliwe.
Problemy gwarancji dostaw
• Na wypadek awarii serwera, lub zagubień komunikatów z
zamówieniami lub odpowiedziami, namiastka klienta powinna stosować
odliczanie czasu przy czekaniu na odpowiedź od serwera.
• Po wyczerpaniu czasu oczekiwania namiastka klienta może przerwać
swoje działanie i wrócić do klienta, przekazując informację, że operacja
się nie powiodła. Zazwyczaj jednak powtarza ona (retransmituje)
wysyłanie komunikatu zamawiającego do czasu aż skończy się to
powodzeniem lub stanie się prawie pewne, że opóźnienie wynika z
braku odpowiedzi od serwera, a nie z zaginięcia komunikatu.
• W przypadku retransmitowania komunikatu z zamówieniem, serwer
może go otrzymać więcej niż jeden raz, a więc może wykonać operację
kilkukrotnie. Jest to dopuszczalne jeśli operacja realizowana przez
serwer jest idempotentna, tzn. wykonanie wielokrotne ma taki sam
skutek jak jednokrotne.
Problemy gwarancji dostaw
• Jeśli powtórki komunikatów dochodzą do serwera podczas realizacji
zadania (ponieważ jego wykonanie jest bardziej czasochłonne niż limit
czasu przewidziany przez klienta), to aby uniknąć powtórzenia operacji,
serwer może implementować protokół rejestracji i rozpoznawania
zamówień tego samego klienta, w celu odfiltrowania duplikatów o takim
samym identyfikatorze zamówienia. Duplikaty będą ignorowane,
odpowiedź przesłana po zakończeniu zadania.
• Jeśli powtórne zamówienie dochodzi po wysłaniu odpowiedzi, serwer
może korzystać z rejestru (historii) odpowiedzi. Wyszukując odpowiedź
na zamówienie o danym identyfikatorze od danego klienta, serwer
retransmituje ponownie komunikat z odpowiedzią, bez konieczności
powtórnej realizacji zadania.
• Strategia ta może być kosztowna pod względem alokacji zasobów.
Zazwyczaj serwer przechowuje tylko ostatnią odpowiedź dla danego
klienta – przyjście od klienta nowego zamówienia traktowane jest jako
potwierdzenie otrzymania odpowiedzi na poprzednie.
• Jeśli klient nie zgłasza nowych zadań, komunikaty z rejestru odpowiedzi
usuwane są ponadto po upływie określonego czasu.
Semantyki niezawodności wywołań RPC
• Semantyka wywołania ewentualnego. Nie zakłada tolerowania
uszkodzeń. Jeśli komunikat z odpowiedzią nie nadejdzie po
wyznaczonym czasie i nie ponawia się prób, to nie ma pewności czy
procedura została wykonana. Nie wiadomo bowiem, czy zaginął
komunikat z zamówieniem lub awarii uległ serwer, czy też wykonanie
doszło do skutku, a zaginął komunikat z odpowiedzią. Na ogół trudno
taką semantykę zaakceptować.
• Semantyka wywołania co najmniej jednokrotnego. Retransmitowanie
komunikatów odbywa się bez odfiltrowania powtórzeń, tak więc serwer
może go otrzymać i wykonać więcej niż raz. Klient w końcu otrzyma
odpowiedź, albo zostanie poinformowany, że serwer uległ awarii. Po
zakończeniu wywołania RPC serwer nie będzie wiedział ile razy je
wykonano. Semantyka ta jest do przyjęcia, jeśli wszystkie procedury
serwera mogą być zaprojektowane za pomocą działań idempotentnych.
Semantyki niezawodności wywołań RPC
• Semantyka wywołania co najwyżej jednokrotnego. Zachodzi
odfiltrowywanie powtórzeń oraz retransmitowanie odpowiedzi bez
ponownego wykonywania działań. Jeśli serwer nie ulegnie awarii i
klient otrzyma wynik wywołania, to procedura została wykonana
dokładnie jeden raz. W przeciwnym przypadku następuje sygnalizacja
sytuacji wyjątkowej. Tę semantykę wybiera się zazwyczaj w
implementacjach
RPC,
zwłaszcza
do
realizacji
operacji
nieidempotentnych.
Sun RPC
• Jednym z najbardziej znanych uniksowych systemów zdalnych wywołań
procedur jest system Sun RPC.
• Początkowo zaprojektowany został do komunikacji klient/serwer w
sieciowym systemie plików Sun NFS.
• Sun RPC jest załączane jako część rozmaitych systemów operacyjnych
firmy Sun i również jest dostępne w innych instalacjach systemu NFS.
• Programiści maja do wyboru użytkowanie RPC ponad protokołami
UDP/IP lub TCP/IP.
• System Sun RPC zawiera język opisu interfejsu nazywany XDR oraz
kompilator interfejsu o nazwie rpcgen.
Język Sun XDR
• Interfejs identyfikowany jest poprzez tzw. numer programu i numer
wersji.
• Opis interfejsu składa się z definicji procedur, stałych oraz typów. Obok
typów prostych, dopuszczalne jest definiowanie struktur, wyliczeń oraz
unii. Składnia definicji typów jest taka sama jak w języku C.
• Definicja procedury określa jej sygnaturę oraz numer (począwszy od 1).
Numer zero zarezerwowany jest dla automatycznie generowanej
procedury pustej, służącej do testowania dostępności serwera.
• Dozwolony jest tylko jeden parametr wejściowy.
• Zarówno typ parametru wejściowego jak i wartości powrotnej mogą
mieć charakter prosty lub zmiennej strukturalnej.
• Procedury wieloparametrowe muszą więc przekazywać parametry jako
elementy jednej struktury.
Język Sun XDR
const MAX = 100;
typedef int plik_id;
typedef int plik_wsk;
typedef int dlugosc;
struct Dane {
int len;
char bufor[MAX];
};
struct pisz_arg {
plik_id p;
plik_wsk pozycja;
Dane dane;
};
struct czyt_arg {
plik_id p;
plik_wsk pozycja;
dlugosc len;
};
Program CZYTAJPISZPLIK {
version WERSJA {
Dane CZYTAJ(czyt_arg) = 1;
void PISZ(pisz_arg) = 2;
} = 2;
} = 9999;
Kompilator rpcgen
Kompilator interfejsu rpcgen generuje:
• Namiastki procedur klienta.
• Namiastki procedur serwera, ekspedytora oraz procedurę główną (main)
serwera.
• Procedury XDR przetaczania i odwrotnego przetaczania danych na
użytek ekspedytora oraz namiastek klienta oraz serwera.
• Plik nagłówkowy o nazwie takiej samej jak nazwa interfejsu
(programu), np. CzytajPiszPlik.h, z deklaracjami wspólnych zmiennych
i typów do użytku w programach klienta i serwera. Sygnatury procedur
usługowych podane są w postaci prototypów funkcji języka C.
• Nazwy procedur są takie same jak w interfejsie, z tym że zamienia się je
na małe litery, dodaje znak podkreślenia oraz numer wersji.
• Argumentem każdej procedury użytkowej jest wskaźnik do jednego
argumentu lub struktury zawierającej wszystkie argumenty.
• Podobnie jest w przypadku wartości powrotnej – zwracany jest
wskaźnik do wyniku lub struktury z wynikami.
Program serwera
• Program serwera implementuje funkcje usługowe, zgodnie z
prototypami wygenerowanymi w pliku nagłówkowym przez rpcgen.
• Wartość powrotna jest wskaźnikiem, musi więc być to adres zmiennej
statycznej.
• Moduł zawierający procedury użytkowe musi zostać skonsolidowany z
wygenerowanymi przez rpcgen modułami bibliotecznymi zawierającymi
funkcję main, procedurę ekspedytora, i namiastki serwera i procedury
przetaczające.
• Sun RPC działa bez ogólnosieciowych usług wiązania. Zamiast tego
udostępnia na komputerze serwera lokalne usługi wiązania, nazywane
programami odwzorowania portów (port mapper).
• Funkcja main tworzy gniazdo do przyjmowania komunikatów z
zamówieniami klientów, a następnie eksportuje interfejs usługi, podając
lokalnemu programowi odwzorowania portów numer programu
(interfejsu), numer wersji oraz identyfikator portu serwera.
Serwer Sun RPC
#include <stdio.h>
#include <rpc/rpc.h>
#include „CzytajPiszPlik.h”
void* pisz_2(pisz_arg* a) {
/* kod procedury zapisu*/
}
Dane* czytaj_2(czyt_arg* a) {
static Dane wynik;
/*kod procedury zapisu*/
return &wynik;
}
Program klienta
• Moduł zawierający procedury użytkowe musi zostać skonsolidowany z
wygenerowanymi przez rpcgen modułami bibliotecznymi zawierającymi
namiastki procedur klienta i procedury przetaczające.
• Do pobrania interfejsu punktu końcowego serwera służy funkcja
clnt_create, zwracająca uchwyt klienta zawierający informacje
niezbędne do komunikacji z portem serwera. W parametrach podajemy
nazwę serwera, numer programu i wersji. Funkcja ta kontaktuje się z
usługami odwzorowania portu w celu pobrania pełnej informacji
komunikacyjnej. Porty serwerów nie muszą więc być ogólnie znane.
• Program klienta wywołuje funkcje usługowe, zgodnie z prototypami
wygenerowanymi w pliku nagłówkowym przez rpcgen, rozszerzonymi o
dodatkowy parametr uchwytu klienta.
• Semantyka wywołania procedury serwera jest co najmniej jednokrotna.
Odstęp czasu miedzy kolejnymi próbami ma wartość domyślną, którą
można określić podczas uzyskiwani uchwytu.
Klient Sun RPC
#include <stdio.h>
#include <rpc/rpc.h>
#include „CzytajPiszPlik.h”
main() {
CLIENT* client_handle;
char* nazwa_serw = ”kkkk”;
czyt_arg a;
Dane* ptr;
client_handle = clnt_create(nazwa_serw, CZYTAJPISZPLIK, WERSJA, ”udp”);
if ( client_handle == NULL ) {
clnt_pcreateerror(nazwa_serw);
exit(1);
}
a.p = 10;
a.pozycja = 100;
a.len = 1000;
ptr = czytaj_2(&a, client_handle);
clnt_destroy(client_handle);
}
DCE RPC
• DCE (Distributed Computing Environment) jest rozproszonym
środowiskiem obliczeniowym, opracowanym przez konsorcjum OSF
(Open Software Foundation – obecnie Open Software Group).
• DCE opracowane początkowo dla systemu UNIX zostało przeniesione
do wszystkich głównych systemów operacyjnych, łącznie z systemami
VMS, Windows, a także systemami operacyjnymi komputerów
osobistych.
• Specyfikacje DCE RPC zostały zaadaptowane przez Microsoft jako
podstawowy system obliczeń rozproszonych.
• Wiele modeli obiektów rozproszonych stanowi bezpośrednie
rozwinięcie koncepcji zawartych w DCE RPC.
DCE RPC
• Interfejs specyfikowany jest z wykorzystaniem specjalnego języka o
nazwie IDL.
• Pliki IDL zawierają deklaracje prototypów funkcji specyfikowane w
formie zbliżonej do składni języka C. Zawierać mogą również
deklaracje typów, stałych oraz informacje inne potrzebne do
przetaczania parametrów i wyników.
• Interfejs DCE RPC identyfikowany jest przez jednoznaczny
identyfikator UID, składający się ze 128 bitowej liczby dwójkowej.
Identyfikatory UID generowane są przez generator uuidgen, na
podstawie informacji o lokalizacji komputera oraz czasu.
• Wiązanie w DCE RPC ma charakter lokalny, tzn. środowisko utrzymuje
na maszynie serwera proces zwany demonem DCE (DCE daemon),
wykonujący usługę odwzorowania portów.
• Środowisko DCE dostarcza jednak również globalnych usług wiązania.
Serwer RPC może zostać zarejestrowany w globalnej usłudze
katalogowej.
DCE RPC
• Usługi DCE RPC realizowane mogą być na bazie różnych protokołów.
• Domyślna semantyka wywołania procedury zdalnej ma charakter co
najwyżej jednokrotny.
• Procedura może zostać jednak oznaczona w pliku IDL jako
idempotentna. Wówczas wywołana będzie co najmniej jednokrotnie.
Przykład: Korzystanie z DCE RPC w
systemie Windows
• Generujemy pusty plik interfejsu przy użyciu programu uuidgen z opcją
/I. W naszym przykładzie będzie to plik motd.idl.
• Modyfikujemy otrzymany plik IDL, definiując interfejs usługi.
• Zmieniamy nazwę interfejsu na MOTD, deklarujemy procedurę zdalną
GetMOTD. Opis funkcji jest w zasadzie taki sam jak w C. W nawiasach
kwadratowych informujemy kompilator interfejsu jak mają być
traktowane parametry. Opisy te nazywamy atrybutami.
uuid(9ebec1c7-bb8a-4063-ac06-d6441c6badea),
version(1.0)
]
interface MOTD
{
void GetMOTD([out, string, max_is(ct)] char* msg, [in] unsigned int ct);
}
Przykład: Korzystanie z DCE RPC w
systemie Windows
• Istnieje wiele innych atrybutów, które można użyć do opisu innych
typów parametrów (np. takich jak tablice). Można również definiować
własne typy.
• Parametry mogą być również jednocześnie wejściowe i wyjściowe.
Należy jednak robić tak jedynie w przypadku gdy rzeczywiście
potrzebny nam jest przepływ danych w obie strony. Pozwoli to uniknąć
przetaczania przez sieć śmieci.
uuid(9ebec1c7-bb8a-4063-ac06-d6441c6badea),
version(1.0)
]
interface MOTD
{
void GetMOTD([out, string, max_is(ct)] char* msg, [in] unsigned int ct);
}
Przykład: Korzystanie z DCE RPC w
systemie Windows
• Tworzymy nowy, pusty plik o takiej samej nazwie jak plik IDL i
rozszerzeniu ACF. Będzie on zawierał linię tworzącą niejawny uchwyt
używany przez mechanizm RPC dla naszego interfejsu.
[implicit_handle (handle_t MOTDHandle)]
interface MOTD {}
Przykład: Korzystanie z DCE RPC w
systemie Windows
• Kompilujemy stworzony interfejs z wykorzystaniem programu MIDL.
Tworzy on plik nagłówkowy motd.h, który włączany będzie zarówno do
kodu klienta jak i serwera, oraz dwa pliki motd_c.c (plik funkcji
pośrednich po stronie klienta) i motd_s.c (funkcje pośrednie po stronie
serwera).
• Piszemy kod serwera. Do projektu włączamy również plik motd_s.c
oraz rpcrt4.lib – bibliotekę zawierającą funkcje środowiska RPC.
• Piszemy kod klienta. Konsolidujemy go z plikiem modt_c.c oraz
również z biblioteką rpcrt4.lib.
Serwer RPC dla Windows –
przydatne funkcje
RPC_STATUS RPC_ENTRY RpcServerUseProtseqEp( //definiuje punkt
końcowy
unsigned char *Protseq,
// Identyfikator protokołu
unsigned int MaxCalls,
// Długość kolejki wywołań
unsigned char *Endpoint,
// Id. punktu końcowego (numer
gniazda, id potoku)
void *SecurityDescriptor
//Deskryptor zabezpieczeń (tylko
Protseq
potoki)
); Identyfikator (tekstowy) protokołu, np. ”ncacn_np” dla nazwanych potoków,
”ncacn_ip_tcp” dla tcp.
Endpoint
Identyfikator (tekstowy) punktu końcowego, np. dla ”ncacn_np” identyfikator
potoku, dla ”ncacn_ip_tcp” numer gniazda.
Serwer RPC dla Windows –
przydatne funkcje
RPC_STATUS RPC_ENTRY RpcServerRegisterIf(
//rejestracja interfejsu
RPC_IF_HANDLE IfSpec,
// uchwyt interfejsu (wygenerowany przez
MIDL)
UUID *MgrTypeUuid,
// UUID menedżera interfejsu (zazwyczaj
NULL)
RPC_MGR_EPV *MgrEpv
// Menedżer interfejsu (NULL to defaultowy
// wygenerowany przez MIDL)
);
RPC_STATUS RPC_ENTRY RpcServerListen(
//start nasłuchiwania
unsigned int MinimumCallThreads,
// Min. liczba wątków
serwera (zazw. 1)
unsigned int MaxCalls,
// Max liczba jednoczesnych k;ientów
unsigned int DontWait
// Zazwyczaj 0
);
Serwer RPC dla Windows (1)
#include <stdio.h>
#include <stdlib.h>
#include "motd.h"
void main() {
RPC_STATUS status;
unsigned int cMaxCalls = 20;
//dla gniazd
//status = RpcServerUseProtseqEp((unsigned char*)"ncacn_ip_tcp",
//
cMaxCalls, (unsigned char*)"201", NULL);
status = RpcServerUseProtseqEp((unsigned char*)"ncacn_np",
cMaxCalls, (unsigned char*)"\\pipe\\MOTD", NULL);
if ( status ) { exit(status); }
status = RpcServerRegisterIf(MOTD_v1_0_s_ifspec, NULL, NULL);
if ( status ) { exit(status); }
status = RpcServerListen(1, cMaxCalls, 0);
if ( status ) { exit(status); }
}
Serwer RPC dla Windows (2)
void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t len) {
return malloc(len);
}
void __RPC_USER MIDL_user_free( void __RPC_FAR * ptr) {
free(ptr);
}
Serwer RPC dla Windows (3)
static char *motd[]= {
"Yawn a more Roman way",
"Rats live on no evil star.",
"If I had a hi-fi",
"Rise to vote sir",
"Evil Olive",
"Never odd or even",
"No lemons, no melon"
};
/* Funkcja(e)*/
void GetMOTD(unsigned char __RPC_FAR *msg, unsigned int ct) {
static int n=0;
char tmp[1024];
sprintf(tmp,"%d: %s",++n,motd[n%(sizeof(motd)/sizeof(char *))]);
strncpy((char*)msg,tmp,ct);
}
Klient RPC dla Windows – przydatne
funkcje
RPC_STATUS RPC_ENTRY RpcStringBindingCompose(
unsigned char *ObjUuid,
// Ident. interfejsu
unsigned char *ProtSeq,
// Ident. protokołu
unsigned char *NetworkAddr,
// Adres sieciowy komputera
unsigned char *EndPoint,
// Id. punktu końcowego
unsigned char *Options,
// Opcje
unsigned char **StringBinding
// Łańcuch połączenia (parametr wyjściowy)
);
Protseq
Identyfikator (tekstowy) protokołu, np. ”ncacn_np” dla nazwanych potoków,
”ncacn_ip_tcp” dla tcp.
NetworkAddr
Adres sieciowy komputera serwera, np. dla ”ncacn_np” nazwa Windows, dla
”ncacn_ip_tcp” adres IP lub nazwa domenowa.
Endpoint
Identyfikator (tekstowy) punktu końcowego, np. dla ”ncacn_np” identyfikator
potoku, dla ”ncacn_ip_tcp” numer gniazda.
Klient RPC dla Windows – przydatne
funkcje
RPC_STATUS RPC_ENTRY RpcBindingFromStringBinding(
unsigned char *StringBinding,
// Łańcuch połączenia
RPC_BINDING_HANDLE *Binding
// Adres uchwytu klienta
);
RPC_STATUS RPC_ENTRY RpcStringFree(
unsigned char **String
);
RPC_STATUS RPC_ENTRY RpcBindingFree(
RPC_BINDING_HANDLE *Binding
);
Klient RPC dla Windows (1)
#include <stdio.h>
#include <stdlib.h>
#include "motd.h"
unsigned char* pszStringBinding = NULL;
RPC_STATUS RPC_init(int argc, char* argv[]) {
RPC_STATUS status;
unsigned char* pszNetworkAddres = NULL;
if ( argc ==2 ) pszNetworkAddres = (unsigned char*)argv[1];
// Dla gniazd
// status = RpcStringBindingCompose(NULL, (unsigned char*)"ncacn_ip_tcp",
//
pszNetworkAddres, (unsigned char*)"201",
//
NULL, &pszStringBinding);
status = RpcStringBindingCompose(NULL, (unsigned char*)"ncacn_np",
pszNetworkAddres, (unsigned char*)"\\pipe\\MOTD",
NULL, &pszStringBinding);
if ( status ) {return status;}
status = RpcBindingFromStringBinding(pszStringBinding, &MOTDHandle);
return status;
}
Klient RPC dla Windows (2)
RPC_STATUS RPC_close() {
RPC_STATUS status;
status = RpcStringFree(&pszStringBinding);
if ( status ) {return status;}
status = RpcBindingFree(&MOTDHandle);
return status;
}
void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t len) {
return malloc(len);
}
void __RPC_USER MIDL_user_free( void __RPC_FAR * ptr) {
free(ptr);
}
Klient RPC dla Windows (3)
void main(int argc, char* argv[]) {
unsigned long ulCode;
char msg[100];
RPC_STATUS status = RPC_init(argc, argv);
if ( status ) {
printf("Blad laczenia z RPC\n");
exit(status);
}
GetMOTD((unsigned char*)msg, sizeof(msg));
printf("Z serwera: %s\n", msg);
status = RPC_close();
if ( status ) {
exit(status);}
}
Klient RPC dla Windows (4) –
Przechwytywanie wyjątków RPC
void main(int argc, char* argv[]) {
unsigned long ulCode;
char msg[100];
RPC_STATUS status = RPC_init(argc, argv);
if ( status ) {
printf("Blad laczenia z RPC\n");
exit(status);
}
RpcTryExcept {
GetMOTD((unsigned char*)msg, sizeof(msg));
printf("Z serwera: %s\n", msg);
}
RpcExcept(1) {
ulCode = RpcExceptionCode();
printf("Runtime reported exception 0x%lx = %ld\n", ulCode, ulCode);
}
RpcEndExcept
status = RPC_close();
if ( status ) {
exit(status);}
}