歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Unix知識 >> 關於Unix

Sun RPC 編程簡介

摘要: 本文簡單介紹了RPC(Remote Procedure Call 遠程過程調用)的原理結構、特點, 及其開放給編程人員不同層次的編程接口。並且例舉實例示范如何通過Rpcgen 編譯工 具來快速開發RPC應用。 一、 概述 在傳統的編程概念中,過程是由 程序員 在本地編譯完成

摘要:  
    本文簡單介紹了RPC(Remote Procedure Call 遠程過程調用)的原理結構、特點,
及其開放給編程人員不同層次的編程接口。並且例舉實例示范如何通過Rpcgen 編譯工
具來快速開發RPC應用。

一、 概述
在傳統的編程概念中,過程是由程序員在本地編譯完成,並只能局限在本地運行的一段
代碼,也即其主程序和過程之間的運行關系是本地調用關系。因此這種結構在網絡日益
發展的今天已無法適應實際需求。總所周知,傳統過程調用模式無法充分利用網絡上其
他主機的資源(如CPU、Memory等),也無法提高代碼在實體間的共享程度,使得主機資
源大量浪費。
而本文要介紹的RPC編程,正是很好地解決了傳統過程所存在的一系列弊端。通過RPC我
們可以充分利用非共享內存的多處理器環境(例如通過局域汪連接得多台工作站),這樣
可以簡便地將你的應用分布在多台工作站上,應用程序就像運行在一個多處理器的計算機
上一樣。你可以方便的實現過程代碼共享,提高系統資源的利用率,也可以將以大量數值
處理的操作放在處理能力較強的系統上運行,從而減輕前端機的負擔。
二、 RPC的結構原理及其調用機制
如前所述RPC其實也是種C/S的編程模式,有點類似C/S Socket 編程模式,但要比它
更高一層。當我們在建立RPC服務以後,客戶端的調用參數通過底層的RPC傳輸通道,可以
是UDP,也可以是TCP(也即TI-RPC-無關性傳輸),並根據傳輸前所提供的目的地址及RPC
上層應用程序號轉至相應的RPC Application Porgramme Server ,且此時的客戶端處於等
待狀態,直至收到應答或Time Out超時信號。具體的流程圖如F1。當服務器端獲得了請求
消息,則會根據注冊RPC時告訴RPC系統的例程入口地址,執行相應的操作,並將結果返回
至客戶端。


F1
當一次RPC調用結束後,相應線程發送相應的信號,客戶端程序才會繼續運行。
當然,一台服務主機上可以有多個遠程過程提供服務,那麼如何來表示一個唯一存
在的遠程過程呢?一個遠程過程是有三個要素來唯一確定的:程序號、版本號和過程號。
程序號是用來區別一組相關的並且具有唯一過程好的遠程過程。一個程序可以有一個或幾
個不同的版本,而每個版本的程序都包含一系列能被遠程調用的過程,通過版本的引入,
使得不同版本下的RPC能同時提供服務。每個版本都包含有許多可供遠程調用的過程,每個
過程則有其唯一標示的過程號。
三、 基於RPC的應用系統開發
通過以上對RPC原理的簡介後,我們再來繼續討論如何來開發基於RPC的應用系統。
一般而言在開發RPC時,我們通常分為三個步驟:
a、 定義說明客戶/服務器的通信協議。
    這裡所說的通信協議是指定義服務過程的名稱、調用參數的數據類型和返回參數的數據
    類型,還包括底層傳輸類型(可以是UDP或TCP),當然也可以由RPC底層函數自動選擇
    連接類型建立TI-RPC。最簡單的協議生成的方法是采用協議編譯工具,常用的有Rpcgen,
    我會在後面實例中詳細描述其使用方法。
b、 開發客戶端程序。
c、 開發服務器端程序。
開發客戶端和服務器端的程序時,RPC提供了我們不同層次的開發例程調用接口。不
同層次的接口提供了對RPC不同程度控制。一般可分為5個等級的編程接口,接下來我們
分別討論一下各層所提供的功能函數。
1、 簡單層例程
簡單層是面向普通RPC應用,為了快速開發RPC應用服務而設計的,他提供
       了如下功能函數。
  函數名              功能描述
Rpc_reg( ) 在一特定類型的傳輸層上注冊某個過程,來作為提供服務的RPC程序
Rpc_call( ) 遠程調用在指定主機上指定的過程
Rpc_Broadcast( )  向指定類型的所有傳輸端口上廣播一個遠程過程調用請求

2、 高層例程
           在這一層,程序需要在發出調用請求前先創建一個客戶端句柄,或是在偵聽請
   求前先建立一個服務器端句柄。程序在該層可以自由的將自己的應用綁在所有的
傳輸端口上,它提供了如下功能函數。

  函數名              功能描述
Clnt_create( )  程序通過這個功能調用,告訴底層RPC服務器的位置及其傳輸類型
Clnt_create_timed( )  定義每次嘗試連接的超時最大時間
Svc_create( )  在指定類型的傳輸端口上建立服務器句柄,告訴底層RPC事件過程的相應入口地址
Clnt_call() 向服務器端發出一個RPC調用請求

3、 中間層例程
   中間層向程序提供更為詳細的RPC控制接口,而這一層的代碼變得更為復雜,
       但運行也更為有效,它提供了如下功能函數。
  函數名              功能描述
Clnt_tp_create( )  在指定的傳輸端口上建立客戶端句柄
Clnt_tp_create_timed( )  定義最大傳輸時延
Svc_tp_creaet( )  在指定的傳輸端口上建立服務句柄
Clnt_call( )  向服務器端發出RPC調用請求

4、 專家層例程
   這層提供了更多的一系列與傳輸相關的功能調用,它提供了如下功能函數。

  函數名              功能描述
Clnt_tli_create( )  在指定的傳輸端口上建立客戶端句柄
Svc_tli_create( )  在指定的傳輸端口上建立服務句柄
Rpcb_set( )  通過調用rpcbind將RPC服務和網絡地址做映射
Rpcb_unset( )  刪除rpcb_set( ) 所建的映射關系
Rpcb_getaddr( )  調用rpcbind來犯會指定RPC服務所對應的傳輸地址
Svc_reg( )  將指定的程序和版本號與相應的時間例程建起關聯
Svc_ureg( )  刪除有svc_reg( ) 所建的關聯
Clnt_call( )  客戶端向指定的服務器端發起RPC請求

 


5、 底層例程
   該層提供了所有對傳輸選項進行控制的調用接口,它提供了如下功能函數。


  函數名              功能描述
Clnt_dg_create( ) 采用無連接方式向遠程過程在客戶端建立客戶句柄
Svc_dg_create( )  采用無連接方式建立服務句柄
Clnt_vc_create( )  采用面向連接的方式建立客戶句柄
Svc_vc_create( )  采用面向連接的方式建立RPC服務句柄
Clnt_call( )  客戶端向服務器端發送調用請求
四、 實例介紹
    以下我將通過實例向讀者介紹通過簡單層RPC的實現方法。通常在此過程中我們
將使用RPC協議編譯工具-Rpcgen。Rpcgen 工具用來生成遠程程序接口模塊,它將以RPC
語言書寫的源代碼進行編譯,Rpc 語言在結構和語法上同C語言相似。由Rpcgen 編譯生
成的C源程序可以直接用C編譯器進行編譯,因此整個編譯工作將分為兩個部分。Rpcgen
的源程序以.x結尾,通過其編譯將生成如下文件:
a) 一個頭文件(.h)包括服務器和客戶端程序變量、常量、類型等說明。
b) 一系列的XDR例程,它可以對頭文件中定義的數據類型進行處理。
c) 一個Server 端的標准程序框架。
d) 一個Client 端的標准程序框架。
   當然,這些輸出可以是選擇性的,Rpcgen 的編譯選項說明如下:
  選項 功能
'-' a 生成所有的模板文件
'-' Sc 生成客戶端的模板文件
'-' Ss 生成服務器端的模板文件
'-' Sm 生成Makefile 文件
(詳見Solaris Rpcgen Manaul) 

Rpcgen 源程序 time.x:
/* time.x: Remote time printing protocol */
program TIMEPROG {
   version PRINTIMEVERS {
     string PRINTIME(string) = 1;
   } = 1;
} = 0x20000001;
time_proc.c源程序:
/* time_proc.c: implementation of the remote procedure "printime" */ 
#include <stdio.h> 
#include <rpc/rpc.h>           /* always needed */ 
#include "time.h"  /* time.h will be generated by rpcgen */ 
#include <time.h>         
/* Remote version of "printime" */ 
char ** printime_1(char **msg,struct svc_req *req)

{
   static char * result; /* must be static! */
   static char tmp_char[100];
   time_t rawtime;
   
   FILE *f;

   f = fopen("/tmp/rpc_result", "a+");
   if (f == (FILE *)NULL) {
     strcpy(tmp_char,"Error");
     result = tmp_char;;
     return (&result);
    }
   fprintf(f, "%s\n", *msg); //used for debugging 
   fclose(f);
   time(&rawtime);
   sprintf(tmp_char,"Current time is :%s",ctime(&rawtime));
   result =tmp_char;
   return (&result);
}
rtime.c源代碼
/*
* rtime.c: remote version
* of "printime.c"
*/

#include <stdio.h>
#include "time.h" /* time.h generated by rpcgen */

main(int argc, char **argv)

{
  CLIENT *clnt;
  char *result;
  char *server;
  char *message;
 

 if (argc != 3) {
     fprintf(stderr, "usage: %s host message\n", argv[0]);
     exit(1);
    }

  server = argv[1];
  message = argv[2];

  /*
   * Create client "handle" used for
   * calling TIMEPROG on the server
   * designated on the command line.
   */

  clnt = clnt_create(server, TIMEPROG, PRINTIMEVERS, "visible");

  if (clnt == (CLIENT *)NULL) {
   /*
    * Couldn't establish connection
    * with server.
    * Print error message and die.
    */

   clnt_pcreateerror(server);
   exit(1);
   }

  /*
   
* Call the remote procedure
   * "printime" on the server
   */

   result =*printime_1(&message,clnt);
   if (result== (char *)NULL) {
     /*
      * An error occurred while calling
      * the server.
      * Print error message and die.
      */

     clnt_perror(clnt, server);
     exit(1);
    }

   /* Okay, we successfully called
    * the remote procedure.
    */

   if (strcmp(result,"Error") == 0) {

   /*
    * Server was unable to print
    * the time.
    * Print error message and die.
    */

    fprintf(stderr, "%s: could not get the time\n",argv[0]);
    exit(1);
    }
    printf("From the Time Server ...%s\n",result);  
    clnt_destroy( clnt );
    exit(0);
}
   有了以上的三段代碼後,就可用rpcgen 編譯工具進行RPC協議編譯,命令如下:
   $rpcgen time.x
   rpcgen 會自動生成time.h、time_svc.c、time_clnt.c
   再用系統提供的gcc進行C的編譯,命令如下:
   $gcc rtime.c time_clnt.c -o rtime -lnsl //客戶端編譯
$gcc time_proc.c time_svc.c -o time_server -lnsl //服務器端編譯
編譯成功後即可在Server端運行time_server,立即將該服務綁定在rpc服務端口上提供
服務。在客戶端運行./rdate hostname msg (msg 是一字符串,筆者用來測試時建立的),
立即會返回hostname 端的時間。
     由於,在Sun Solaris 中無法獲取遠端Server 上時鐘信息的功能(不改變本
     地Server時鐘),筆者曾將此程序應用於計費服務器同時鐘服務器同步監測的網管
     系統中,運行穩定,獲得了較好的效果。應該說RPC的應用是十分廣泛的,特別是
     在分布式計算領域中尤為顯得重要。當然,筆者也是剛接觸RPC,還有很多地方了
     解的不夠深刻,望廣大讀者多指教。

 

參考文獻:
《SUN Solaris8 ONC+ Dev》

Copyright © Linux教程網 All Rights Reserved