TCP/IP 與 Internet 網路:第九章 RPC 高階程式介面  上一頁           下一頁

 

翻轉工作室:粘添壽

 

9-7 RPC 定義語言

RPC『定義語言』(Definition Language是製作 RPC 規格檔的描述語言,其中包含規格檔的製作方法和抽象資料的表示法,以下分別介紹之。

9-7-1 規格檔之製作

製作『規格檔』(Specification File必需依照 RPC 定義語言的標準來敘述,才能被 rpcgen 所編譯。所編寫規格檔的副檔名,一定必須是.x。一個.x檔案裡必需包含交換資料的『資料型態』(Data Type『程式號碼』(Program Number『程式版本』(Program Version『程序名稱』(Procedure Name。它的定義非常簡單,基本表示法為一個程式名稱包含一組程式版本,又一個程式版本包含一組遠端程序名稱,所包含的內容以左右大掛號({ })標示。以下列步驟來說明:

(A) 宣告程式號碼

以關鍵字 program 宣告一個程式號碼,其名稱為 identifier,而程式號碼是一個不含負數的整數 value。在一個程式號碼可包含若干個版本宣告,格式如下:

program identifier {

      version_1;

      ……..

      version_n

} = value;

(B) 宣告程式版本

利用關鍵字 version 宣告一個程式版本,版本名稱為 identifier,版本號碼是 valueidentifier = value)。在一個程式版本中也可以包含若干個程序宣告,格式如下:

version identifier {

       procedure_1;

       ………

       procedure_n;

} = value;

(C)宣告程序

宣告程序和一般 C 語言的語法非常類似,必需宣告程序傳遞和傳回參數的資料型態(如,intunsigned intvoidchar)。procedure_name 為程序名稱,value 是程序號碼。

data_type procedure_name( data_type) = value;

(D) 範例說明

規格檔範例如下:

program TIMEINFO {

      version TIMEVERS {

             unsigned int GETTIME (void) = 1;

       } = 1;

} = 0x20000006;

以上範例定義一個名稱為 TIMEINFO 的程式,它的程式號碼為 0x20000006。該程式中包含一個版本名稱,其版本名稱為 TIMEVERS,版本號碼是 1。此程式版本也只包含一個遠端程序(GETTIME),程序號碼為 1。呼叫 GETTIME 程序時,並沒有傳遞參數(void),但傳回一個沒有符號的整數(unsigned int)。此範例經過 rpcgen 編譯後,所產生的標頭檔會包含下列宣告值:(部分檔案):

# define TIMEINFO ((u_long) 0x20000006)

# define TIMEVERS ((u_long) 1)

# define GETTIME ((u_long) 1)

extern u_int *gettime_1();

9-7-2 資料型態之宣告

RPC 定義語言的資料宣告也非常類似 ANSI C 語言的宣告方式,但為了符合交換的抽象資料型態,RPC 另外自行定義宣告方式。定義語言所宣告的資料型態經由 rpcgen 編譯後,也必須符合 ANSI C 的資料型態。以下分別介紹 RPC 所宣告的資料型態。

(A) 常數(Consttant

RPC 定義常數的格式如下:

const identifier = integer_value;

以關鍵字 const 宣告常數,其名稱為 identifier,而值是 integer_value。範例如下:

const MAX_COUNTER = 1024;

經過 rpcgen 編譯後,結果為:

# define MAX_COUNTER 1024

(B) 結構(Structures

RPC 的結構宣告與 ANSI C 的型態相同,範例如下:

struct intpair {

int a;

int b;

};

經過 rpcgen 編譯後,結果為:

struct intpair {

int a;

int b;

};

typedef struct intpair intpair;

關鍵字 typedef 是讓 intpair 來取代 struct intpair,因此在 RPC 程式中只要用 intpair 宣告即可。

(C) 列舉(Enumerations

如同結構一樣,RPC 宣告列舉也和 ANSI C 語言相同,範例如下:

enum light {

RED = 0,

AMBER = 1,

GREEN = 2

};

經過 rpcgen 編譯後,結果為:

enum light {

RED = 0,

AMBER = 1,

GREEN = 2

};

typedef enum light light;

(D) 聯集(Unions

RPC 的聯集定義有點類似 C 語言,但是兩者在本質上有相當的差異。C 語言的聯集是定義一串元件的儲存空間,而 RPC 的聯集是條件式的資料型態規格。我們用一個範例來說明 RPC 聯集的使用,此範例為某一個程序接收到訊息的處理情形:

(1)    程序接收到一個字元陣列(Array),其中包含著以參數描述的使用者名稱。

(2)    判斷該使用者是否已登入系統,如果使用者已登入,則將原字元陣列填入使用者登入時間(字元),並回應給傳送者。如果使用者並未登入,則回應空白資料(NULL)。如果根本沒有這位使用者,則回應錯誤訊息(整數表示)。

在這個範例中,該程序可能回應三種資料型態的一種:字元陣列、整數或空白(NULL void)。RPC 聯集定義此類型資料的轉送,它包含選擇(switch)來決定何時要傳送何種資料型態。RPC 聯集的語法如下:

union identifier switch (declaration) {

case value_1 : declaration_2;

case value_2 : declaration_2;

…..

default : declaration_n;

}

聯集變數名稱為 identifier,而條件判斷之變數是 declaration,在每一相對條件都有一個專屬宣告。

如果依照前述的範例,則 RPC 聯集的宣告如下:

const MAX_BUF = 30;

union time_results switch (int status) {

case 0:   char timeval[MAX_BUF];

case 1:   void;

case 2:   int reason;

};

這表示 time_result 可能有三種資料型態:字元陣列、空值(void)或整數,到底會使用何種型態,則依照 status 變數的內容而定。此例經由 rpcgen 編譯後,結果如下:

# define MAX_CHAR 30

struct time_results {

int status;

union {

char timeval[MAX_CHAR];

int reason;

} time_result_u;

};

typedef struct time_results time_results;

rpcgen 編譯後,好像和原來 RPC 規格檔有很大的差異,此點必須特別說明如下:

(1)    結構資料所包含的聯集資料之間,如有相同的變數名稱,則在聯集的變數名稱的後面加入_u

(2)    void 宣告被省略掉,因它只在沒有訊息傳送的情況下才會發生。

(3)    結構變數之名稱和原來 RPC 的聯集變數名稱一樣。

(4)    當實現程序時,必需確定 status 變數是否只有 01 2 三種數值的機會,如以 RPC 規格-檔裡所描述,確實只能這三種數值。因此在程序中必需依照結構變數特殊處理:

如果放置 0 status 變數內,則必須將資料填入 time_results_u.timeval 陣列中。

如果設定 status 1 時,則不需填入任何值到聯集內。

如果設定 status 2 時,則必須填入一個整數到 time_results_u.reason 元件內。

(5)    當客戶端收到程序的回應時,首先必須測試 status 的內容,再來決定應接收何種型態的資料,如此便不會發生錯誤。

(E) 型態定義(Type Definitions

RPC 的型態定義和 C 語言完全相同,rpcgen 編譯後也沒有改變,範例如下:

typedef long conuter_t;

(F) 陣列宣告(Declarations of Arrays

RPC 定義語言允許使用宣告含有結構(Structure)、聯集(Union)和型態定義(typedef)的陣列,同時也允許宣告固定長度和可變長度的陣列。對於固定長度的宣告較沒有問題,也和 C 語言相同,大部分 rpcgen 也不會作任何改變。但 C 語言並不提供可變長度陣列的宣告,因此,rpcgen 會做較特殊的處理。宣告固定長度和不固定長的陣列情況如下:

如宣告固定長度陣列,範例如下:

int proc_times [100];

表示整數陣列長度固定為 100,經過 rpcgen 編譯後也沒有改變。

如宣告可變長度陣列,範例如下:

typedef long x_records <50>;

這表示 x_records 陣列的長度可以由 0 50,資料型態是長整數。如經過 rpcgen 編譯後,結果如下:

typedef struct {

u_int x_records_len;

long *x_records_val;

} x_records;

因為 C 語言沒有可變長度陣列的宣告,因此必須用結構宣告來取代。結構中 x_records_len 代表第幾個陣列變數;而 x_records_val 是陣列內容的指標位址。原來規格檔內有最長限制 50,編譯後成為沒有最長限制。

(G) 指標宣告(Declarations of Pointers

RPC 的指標宣告和 C 語言相同,rpcgen 也不會做任何改變,範例如下:

int  *nextp;

雖然指標宣告如同 C 語言,但使用在遠端程序呼叫中傳遞訊息,就和同一主機上傳遞有很大的不同點。如遠端程序呼叫使用指標參數時,而它所傳遞的是記憶體位址,在不同電腦之間的記憶體位址根本沒有意義。因此,必須考慮以下列方式宣告:

struct linked_list {

int value;

struct linked_list *nextp;

};

以上就是利用串列結構來表示,並可利用布林代數(value)來表示資料是否存在。

(H) 字串(Strings

字串宣告可以是可變長度,範例如下:

string  name<50>;

rpcgen 編譯後,結果為:

char  *name;

雖然並沒有指定最長長度,但在程序編寫時儘量不要超過規格檔(.x)所限定的範圍。

(I) 布林值(Boolean Values

RPC 定義語言宣告布林值範例如下:

bool  waiting;

rpcgen 編譯後結果為:

boot_t  waiting;

C 語言裡並沒有 boot_t 的資料型態,這必需利用 RPC 函數庫定義資料型態(typedef),使這個變數僅能填入 TRUE FALSE

(J) 空值(Void

如同一般宣告,RPC 宣告一個空值的格式如下;

void;

但在 RPC 內宣告空值只有兩個地方:

(1)  聯集定義:在聯集元件中不傳回任何值。

(2)  程式定義:在程式宣告中不傳遞任何參數。

(K) 不明確資料(Opaque Data

RPC 定義語言允許宣告不明確資料,表示資料型態並未指定,而當程式需要時在指定何種資料型態。固定長度的宣告語法為:

opaque  extra_bytes[1024];

rpcgen 編譯後為:

char  extra_bytes[1024];

可變長度的 RPC 宣告如下:

opaque  more_bytes<1024>;

rpcgen 編譯後,結果為:

struct {

u_int  more_bytes_len;

char  *more_bytes_val;

} more_bytes;

由此可見,rpcgen 將不明確資料以字元型態來表示。

 

 

<GOTOP>