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,版本號碼是 value(identifier = value)。在一個程式版本中也可以包含若干個程序宣告,格式如下:
version identifier { procedure_1; ……… procedure_n; } = value; |
(C)宣告程序
宣告程序和一般 C 語言的語法非常類似,必需宣告程序傳遞和傳回參數的資料型態(如,int、unsigned int、void、char)。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 變數是否只有 0、1 和 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 將不明確資料以字元型態來表示。