RPC Remote Procedure Call – Távoli eljárás hívás Hagyományos eljáráshívás: Count = read (fd, buf, nbytes)
Paraméterek átadásának a típusai: -
Érték szerinti átadás
-
Referencia szerinti átadás
-
Másoló/visszatöltő (call by copy/restore) (c nyelv nem használja)
Kliens és szerver oldali eljárás csonkok RPC – távoli eljárás hívásokat a helyi eljárás hívásokkal teszi egyenrangúvá Rendszerhívások esetében pl. read eljárás esetében a szerkesztő (linker) hozzáilleszti a megfelelő subrutint ennek ellenére a ugyanúgy van megírva mint egy egyszerű eljárás. Az RPC is hasonlóan oldja meg a távoli eljárás hívást ezeket a subrutinokat kliens és szerveroldali csonkoknak nevezzük.
Az eljárás paramétereit üzenetbe csomagoljuk paraméter csomagolásnak nevezzük (parameter marshaling). Szerver oldalon egy switch segítségével kiválasztjuk a megfelelő eljárást és a szerver csonk által kicsomagolt paraméterekkel, meghívjuk az eljárást.
A hivatkozás szerinti paraméterek esetében, másoló/visszatöltő eljárást használjuk. Optimalizálni lehet az üzenetek méretét, ha ismerjük, hogy az adott paraméter be vagy kimenti paraméter.
Az RPC protokollban megvannak határozva a változok ábrázolási módjai, paraméterek becsomagolásának mikéntje. Viszont szükség van az eljárások meghatározására. Ezt egy interfész segítségével lehet megoldani, amelyben deklaráljuk az összes távoli eljárást és az összes nem standard adattípust, amelyet ezek az eljárások használnak. Ezt az interfészt az IDL (Interface Definition Language) segítségével határozzuk meg. IDL állományt írni olyan, mint egy C fejléc állományt írni kibővítve egy-két plusz kulcsszóval.
Pelda: Egy egyszerű alkalmazás: // File Standalone.cpp #include
// Future server function. void Output(const char* szOutput) { std::cout << szOutput << std::endl; } int main() { // Future client call. Output("Hello Lonely World!"); }
RPC változata:
Az IDL állomány: // File Example1.idl [ // Egy egyedi azonósító amely megkülönbözteti // ezt az interfészt a többitől. uuid(00000001-EAF3-4A7A-A0F2-BCE4C30DA77E), // Az interf;sz verziója. version(1.0), // ez az interfész egy implicit kezelőt használ // hExample1Binding névvel. implicit_handle(handle_t hExample1Binding) ] interface Example1 // Az interfész neve Example1 { // Egy olyan függvény amelyiknek a parameter egy sztring. void Output( [in, string] const char* szOutput); } Az interfész azonósított letre hozásához a uuidgen ezközt használjuk
C:\Program Files\Microsoft Visual Studio .NET\Common7\Tools\Bin\ uuidgen -i > Example1.idl Az IDL állományt a midl compilátorral fordítjuk le. Eredményképpen 3 állomány generálodik.
Szerver: A Example1_s.c és a Example1.h állományokat hozzácsatoljuk a szerverhez // File Example1Server.cpp #include #include "Example1.h" // Szerver függvénye. void Output(const char* szOutput) {
std::cout << szOutput << std::endl; } int main() { RPC_STATUS status; // Protokoll portal kombinálva, hogy fogadja a távoli eljárás hivást status = RpcServerUseProtseqEp( reinterpret_cast("ncacn_ip_tcp"), // TCP/IP protokollra // épít. RPC_C_PROTSEQ_MAX_REQS_DEFAULT, // reinterpret_cast("4747"), // TCP/IP port. NULL); // nincs biztonság. if (status) exit(status); // Example1 interfész regisztrálása. status = RpcServerRegisterIf( Example1_v1_0_s_ifspec, // Interface to register. NULL, // Use the MIDL generated entry-point vector. NULL); // Use the MIDL generated entry-point vector. if (status) exit(status); // megkezdi a regisztrált interfészek irányába // beérkező kapcsolodások hallgatását // a tavoli folyamatok reszéréről // addig várakoznak mig meghivjuk a // RpcMgmtStopServerListening eljárást. status = RpcServerListen( 1, // Recommended minimum number of threads. RPC_C_LISTEN_MAX_CALLS_DEFAULT, // Recommended //maximum number of threads. FALSE); // Start listening now. if (status) exit(status); } // Memoria lefoglalás az RPC számára. // Futás közben ezt a két eljárást használja az RPC a helyfoglálásra és // felszabadításra hogy elég memoria legyen a sztring elküldéséhez void* __RPC_USER midl_user_allocate(size_t size) { return malloc(size); } // Memoria felszabadítása. void __RPC_USER midl_user_free(void* p) { free(p); }
Kliens // Example1Client.cpp #include #include "Example1.h" int main() { RPC_STATUS status; unsigned char* szStringBinding = NULL; // Látrehoz egy kötési sztring kezelőt. // Nem más mint egy printf. // Ne mitt történik a kapcsolódás. status = RpcStringBindingCompose( NULL, // UUID to bind to. reinterpret_cast("ncacn_ip_tcp"), // TCP/IP reinterpret_cast("localhost"), // TCP/IP cim reinterpret_cast("4747"), // TCP/IP port NULL, // Protokoll dependens hálózati opciók. &szStringBinding); // Kapcsolodási sztring kimenet. if (status) exit(status); // Validálja a kapcsolodási sztring kezelő formátumát es // áatalakitja egy kapcsolatkezelővé // Itt sincs kapcsolodás status = RpcBindingFromStringBinding( szStringBinding, // A kapcsolódási sztring. &hExample1Binding); // Berakja az eredményt az implicit kezelőbe // Kezelőt az IDL állományba deklaráltuk if (status) exit(status); RpcTryExcept { // Meghivja az RPC függvént. Implicit kezelő használatával // A kapcsolódás itt történik. Output("Hello RPC World!"); } RpcExcept(1) { std::cerr << "Runtime reported exception " << RpcExceptionCode() << std::endl; } RpcEndExcept // Felszabadítja a sztring által foglalt memoriát. status = RpcStringFree( &szStringBinding); // Sztring. if (status) exit(status);
// Felszabadítja a kezelőt az erőforrásokat es lekapcsolodik a szerverről. status = RpcBindingFree( &hExample1Binding); // Frees the implicit binding handle defined in // the IDL file. if (status) exit(status); } // Memory allocation function for RPC. // The runtime uses these two functions for allocating/deallocating // enough memory to pass the string to the server. void* __RPC_USER midl_user_allocate(size_t size) { return malloc(size); } // Memory deallocation function for RPC. void __RPC_USER midl_user_free(void* p) { free(p); }
Implicit és explicit kezelők Eddigiekben Implicit kezelőt alkalmaztunk. Ez nem alkalmas hogy egyszerre több szerverhez kapcsolodjunk. Ebben az esetben meg kell változtatni az IDL-t a szervert és a klienset IDL // Example1Explicit.idl [ // A unique identifier that distinguishes this // interface from other interfaces. uuid(00000002-EAF3-4A7A-A0F2-BCE4C30DA77E), // This is version 1.0 of this interface. version(1.0), // Az interfész explicit kapcsolódási kezelőt használ. explicit_handle ] interface Example1Explicit // Az interfész neve { // A függvény az interfész kezelő és egy sztring bemenettel rendelkezik void Output( [in] handle_t hBinding, [in, string] const char* szOutput); } Szerver // Example1ExplicitServer.cpp
#include #include "Example1Explicit.h" // Szerver függvény. void Output(handle_t hBinding, const char* szOutput) { std::cout << szOutput << std::endl; } // main – hasonlit az eddigiekhez. Kliens // File Example1ExplicitClient.cpp #include "Example1Explicit.h" int main() { // RpcStringBindingCompose – meghivjuk mint eddig. handle_t hExample1ExplicitBinding = NULL; // Validálja a kapcsolodási sztring kezelő formátumát es // áatalakitja egy kapcsolatkezelővé // Itt sincs kapcsolodás status = RpcBindingFromStringBinding( szStringBinding, // The string binding to validate. &hExample1ExplicitBinding); // Put the result in the explicit // binding handle. if (status) exit(status); RpcTryExcept { // Meghivja az RPC függvént. Implicit kezelő használatával // A kapcsolódás itt történik. Output(hExample1ExplicitBinding, "Hello RPC World!"); } RpcExcept(1) { std::cerr << "Runtime reported exception " << RpcExceptionCode() << std::endl; } RpcEndExcept // RpcStringFree - ugyanugy. // Felszabadítja a kezelőt az erőforrásokat es lekapcsolodik a szerverről. status = RpcBindingFree( &hExample1ExplicitBinding); if (status) exit(status); }
Application Configuration File (ACF) Itt direct használtuk a kezelőket az IDL állományba de ez a lehetőség egy Microsoft bővités. Általába külön kell egy Application Configuration File amely tartalmazza ezeket. // File Example1.acf [ // This interface will use an implicit binding handle named hExample1Binding. implicit_handle(handle_t hExample1Binding) ] interface Example1 // The interface is named Example1 { } // File Example1Explicit.acf [ // This interface will use explicit binding handle. explicit_handle ] interface Example1Explicit // The interface is named Example1Explicit { }
Szerver kikapcsolása A szerver addig fut ameddig valaki valamilyen modon lekapcsolja. A legjobb mód hogy ezt tegyük az ha meghivjuk RpcMgmtStopServerListening függvényt. Lehet ezt egy másik szálból vagy egy olyan függvényt írni ami ezt megcsinálja es ezt meghívhatja egy kliens.
Gyakori Hibak: -“Fatal error C1189: #error : You need a Windows 2000 or later to run this stub because it uses these features: /robust command line switch.” Megoldas Go to project properties, C/C++, Preprocessor, Preprocessor “;_WIN32_WINNT=0x0500” in the end of the line. Könyvészet: Tanenbaum : Elosztott rendszerek Anders Dalvander : Introduction to RPC - Part 1 http://www.codeproject.com/internet/rpcintro1.asp#Introduction0 http://blogs.msdn.com/eldar/archive/2006/02/28/540981.aspx
Definitions
and add