使用rpcgen構(gòu)建分布式程序的例子_第1頁
使用rpcgen構(gòu)建分布式程序的例子_第2頁
使用rpcgen構(gòu)建分布式程序的例子_第3頁
使用rpcgen構(gòu)建分布式程序的例子_第4頁
使用rpcgen構(gòu)建分布式程序的例子_第5頁
已閱讀5頁,還剩17頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、!-分布式系統(tǒng)的重要不言而喻,下面是使用rpcgen工具構(gòu)建一個分布式程序 的例子,其實主要就是VTCP/IP進(jìn)行網(wǎng)際互聯(lián) 卷3上面的那個例子,就是下面 這本書.另外還參考了 分布式系統(tǒng) 原理與范例 這本書,即Andrew S. Tanenbaum寫的 這本,這本書是學(xué)習(xí)分布式的必讀書之一.相當(dāng)不錯.整個文章分為三部分,第一部分是對RPC遠(yuǎn)程過程調(diào)用)的一個介紹,第二部 分是對使用rpcgen構(gòu)建分布式程序的一個介紹,第三部分是使用rpcgen構(gòu)建分 布式程序的詳細(xì)步驟.可以先按照第三部分的步驟自己先試試,如果有興趣再回過頭來看它的介紹. 另外,我寫的這個只是一個入門,如果以后有機(jī)會再詳細(xì)地討

2、論一下內(nèi)部的機(jī) 理.事情總是一步一步來的. RPC概念1.1介紹在中間件的實現(xiàn)中,弓I入了遠(yuǎn)程過程調(diào)用RPC( Remote Procedure Call )的概念。同時,許多分布式系統(tǒng)是基于進(jìn)程間的顯式消息交換的,然而消息的發(fā)送和接收過程無法隱藏通信的存在,而通信的隱藏對于在分布式系統(tǒng)中實現(xiàn)訪問透明性是極為重要的。因此這個問題在很長一段時間內(nèi)都沒有找到合適的解決辦 法,后來Birrel和Nelson在1984年的一篇論文中引入了一套 與傳統(tǒng)方法截然 不同的通信處理手段。他們認(rèn)為應(yīng)該允許程序調(diào)用位于其它機(jī)器上的進(jìn)程。當(dāng)機(jī)器A上的進(jìn)程調(diào)用B上的進(jìn)程時,A上的調(diào)用進(jìn)程被掛起,而B上的被調(diào)用進(jìn) 程開

3、始執(zhí)行。調(diào)用方可以通過使用參數(shù)將信息傳送給被調(diào)用方,然后可以通過傳 回的結(jié)果得到信息。編程人員看不到任何消息傳遞過程。這種方法就稱為遠(yuǎn)程過程調(diào)用RPC目前,RPC乍為一種廣泛使用的技術(shù),已成為許多分布式系統(tǒng)的 基礎(chǔ)。1.2構(gòu)建分布式程序的兩種模式在設(shè)計分布式應(yīng)用時,程序員可以使用下列兩種方法之一:面向通信的設(shè)計由通信協(xié)議開始。設(shè)計報文格式和語法,指明對每個傳入報文將如何反應(yīng)以及 如何產(chǎn)生每個外發(fā)報文,以此來設(shè)計客戶和服務(wù)器各構(gòu)件。面向應(yīng)用的設(shè)計由應(yīng)用開始。設(shè)計常規(guī)的應(yīng)用程序來解決問題。構(gòu)建并測試可在單臺機(jī)器上運(yùn) 行的常規(guī)程序的工作版本。將這個程序劃分成兩個或多個程序片,加入通信協(xié)議 以允許每片

4、程序在單獨(dú)的計算機(jī)上執(zhí)行。遠(yuǎn)程過程調(diào)用模型使用面向應(yīng)用的方法,它強(qiáng)調(diào)的是所要解決的問題而不是所 需要的通信。利用遠(yuǎn)程過程調(diào)用,程序員首先設(shè)計一個解決問題的常規(guī)程序, 接 著將其 劃分成若趕干片,這些程序片運(yùn)行在兩臺或更多的計算機(jī)上。程序員可 遵循良好的設(shè)計原則,以便使代碼模塊化并且可維護(hù)。在理想的情況下,遠(yuǎn)程過程調(diào)用提供的不只是抽象上的概念。它允許程序員在 將一個程序劃分成若干片之前,先構(gòu)建,編譯和測試一個解決該問題的常規(guī)程序 的版本,以便確保能夠正確解決問題。不但如此,因為RPC以方法調(diào)用為邊界劃分程序,所以將程序劃分為本地部分和遠(yuǎn)程部分并不會引起程序結(jié)構(gòu)的很大變 化。實際 上,將某些過程從

5、一個程序轉(zhuǎn)移到遠(yuǎn)程機(jī)器上時,有可能不需要改變。1.3常規(guī)過程調(diào)用的概念性模型 如下圖所示,為常規(guī)的程序調(diào)用。本地過程調(diào)用.jpg1.4遠(yuǎn)程過程調(diào)用模型遠(yuǎn)程過程調(diào)用模型使用了和常規(guī)程序一樣的過程抽象, 邊界跨越兩臺計算機(jī)。如下圖所示。但是它允許一個過程的:| szmj遠(yuǎn)程過程調(diào)用.jpg1.5常規(guī)過程調(diào)用的執(zhí)行和返回程序從一個主程序開始執(zhí)行,并一直繼續(xù)下去,直到遇到一個過程調(diào)用。這個 調(diào)用使程序的執(zhí)行轉(zhuǎn)入到某個指定的代碼處繼續(xù)執(zhí)行。常規(guī)過程調(diào)用的執(zhí)行流程 如下圖所示:主軽序找碼過理A代碼常規(guī)過程調(diào)用.jpg1.6分布式系統(tǒng)的過程模型在分布式系統(tǒng)中,其中的某個過程有可能在另外的機(jī)器上, 模型如下圖

6、如示:因此,其調(diào)用過程機(jī)器1 1側(cè)I:剖4黑)機(jī)需2上購進(jìn)崔AO第器機(jī)曙:J匕郵】過程創(chuàng)1脛并簿)分布式中的過程調(diào)用.jpg1.7客戶-服務(wù)器和RPC之間的對比遠(yuǎn)程過程調(diào)用允許程序員以一種他所熟悉的環(huán)境來思考客戶和服務(wù)器的交互, 如同常規(guī)的過程調(diào)用,遠(yuǎn)程過程調(diào)用把控制權(quán)傳遞給被調(diào)用的進(jìn)程。也像常規(guī)過程調(diào)用一樣,在調(diào)用進(jìn)程中,系統(tǒng)把調(diào)用過程的執(zhí)行掛起。 而只允許被調(diào)用過程 執(zhí)行。當(dāng)遠(yuǎn)程程序發(fā)出響應(yīng)時,這對應(yīng)于在常規(guī)過程調(diào)用中執(zhí)行return。控制權(quán)返回給調(diào)用者, 被調(diào)用過程停止執(zhí)行。 嵌套的過程調(diào)用的想法也可應(yīng)用到遠(yuǎn)程過程 調(diào)用。遠(yuǎn)程過程調(diào)用也許要調(diào)用另一個遠(yuǎn)程過程。如上圖所示。二 . 分布式程

7、序的生成原理RPC 的實現(xiàn)包括一個工具,它自動地生成實現(xiàn)分布式程序所需要的大多數(shù)代 碼。這個工具叫做rpcgen,它讀取一個規(guī)約文件作為輸入,生成 C的源文件作 為 輸出。規(guī)范文件包含常量,全局?jǐn)?shù)據(jù)類型,全局?jǐn)?shù)據(jù)以及遠(yuǎn)程過程(包括過 程參數(shù)和結(jié)果類型)的聲明。 rpcgen 產(chǎn)生的代碼包含了實現(xiàn)客戶和服務(wù)器程序 所需 要的大部分源代碼。具體地說,rpcgen包括參數(shù)整理,發(fā)送RPC報文,把 傳入調(diào)用分派到正確的過程, 發(fā)送應(yīng)答, 在參數(shù)和結(jié)構(gòu)的外部表示和本地數(shù)據(jù)表 示 之間進(jìn)行轉(zhuǎn)換。 rpcgen 的輸出與應(yīng)用程序和程序員編寫的少數(shù)文件相結(jié)合 后,便產(chǎn)生出了完整的客戶和服務(wù)器程序。rpcgen

8、 讀取輸入文件,該文件包含有對遠(yuǎn)程過程的說明。它產(chǎn)生四個輸出文 件,每個文件都包含有源代碼,如果輸入的文件(即規(guī)約文件)具有名字 q.x , 則輸出的文件如下所示:q.h :常量和類型的聲明 q_xdr.c XDR 過程調(diào)用 q_clnt.c 客戶端的通信接口 q_svc.c 服務(wù)器端的通信接口 具體步驟在第三部分。三. 分布式程序的生成步驟在這里,舉的例子就是Douglas E.Comer書上的那個例子。這個例子重點(diǎn)在于 解釋 rpcgen 如何工作的。3.1 查找字典在該例子中, 考慮實現(xiàn)一個簡單的數(shù)據(jù)庫功能的程序。 該數(shù)據(jù)庫提供四個基本 的操作:初始化,插入一個新的條目,刪除一個條目,查

9、找一個條目。假設(shè)每個 條目都 是一個單詞。因此該數(shù)據(jù)庫的功能就可以看作是一個字典。應(yīng)用程序插 入一組單詞,接著使用數(shù)據(jù)庫來檢查新單詞, 以便知道這個單詞是否在該字典中。 3.2 構(gòu)建程序的八個步驟包含了該常規(guī)程序:(1)構(gòu)建解決該問題的常規(guī)應(yīng)用程序 要構(gòu)建這個字典應(yīng)用例子的分布式版本, 第一步要求程序員構(gòu)造解決該問題的 常規(guī)程序。文件 dict.cCODE:/* dict.c */#include<stdio.h> #include<stdlib.h> #include<ctype.h> #include<string.h>#define MAX

10、WORD 50#define DICTSIZ 100/*/*每個單詞的長度限制 */字典一共可以存放多少個單詞 */char dictDICTSIZMAXWORD+1; int nwords = 0;/* nextin():讀入命令與單詞*/ int nextin(char *cmd, char *word)int i, j;char buf100 = 0;if (fgets(buf, sizeof(buf)-1, stdin) = NULL)return -1;for (i = 0; i < 100; i+)if (bufi != ' ')break;if (i = 1

11、00)return -1;*cmd = bufi;while (1)if (i = 100) return -1;i+;if (bufi = ' ')continue;else if (bufi = 'n')return 0;elsebreak;j = 0;while (bufi != 'n')*word+ = bufi;i+;j+;return j;!-/* initw():*/初始化這個字典int initw()nwords = 0;return 1;/* insertw(const char *word):向這個字典中加入單詞*/int i

12、nsertw(const char *word)strcpy(dictnwords, word);nwords+;return nwords;/* deletew(const char *word): */從這個字典中刪除單詞int deletew(const char *word)int i;for (i = 0; i < nwords; i+)if (strcmp(word, dicti) = 0)nwords-;strcpy(dicti, dictnwords);return 1;return 0;/* lookupw(const char *word): */在字典中查找某個單詞

13、int lookupw(const char *word)!-int i;for (i = 0; i < nwords; i+)if (strcmp(word, dicti) = 0)return 1;return 0;int main(int argc, char *argv)char wordMAXWORD+1;char cmd;int wrdlen;while (1)wrdlen = nextin(&cmd, word);if (wrdlen < 0)exit(0);wordwrdlen = '0'switch(cmd)case 'I'

14、:initw();printf("Dictionary initialized to empty.n");break;case 'i':insertw(word);printf("%s inserted.n", word);break;case 'd':if (deletew(word)printf("%s deleted.n", word);elseprintf("%s not found.n", word);break;case 'l':if (lookupw(w

15、ord)printf("%s is found.n", word);elseprintf("%s is not found.n", word); break;case 'q':printf("program quits.n");exit(O);default:p rintf("command %c invalid.'n", cmd);break;return 0;該dict.c程序使用了一個二維數(shù)組來存儲單詞。全局變量nwords記錄了任何時刻字典中單詞的數(shù)量。主程序包含一個循環(huán),在每次循環(huán)

16、中讀取并處 理輸入文件中的一行。該程序就是普通的常規(guī)程序,編譯并運(yùn)行:gcc dict.c - o dict./dict可以如下來實驗QUOTE:i hello i world l hello d hello l hello q(2將程序劃分成兩部分常規(guī)程序一旦構(gòu)建完成并經(jīng)過測試,就可以將它劃分成本地構(gòu)件和遠(yuǎn)程 構(gòu)件了。下圖為這個程序的過程調(diào)用情況:程序組織情況.jpg在考慮哪個過程可以轉(zhuǎn)移到遠(yuǎn)程機(jī)器上時,程序員必須考慮每個過程所需要 的資源。例如nextin在每次被調(diào)用時要讀取下一個輸入行,并對它進(jìn)行分析。 因為 它需要訪問程序的標(biāo)準(zhǔn)輸入,所以nextin必須放在主程序的機(jī)器中。簡單 來說就

17、是執(zhí)行I/O或者訪問文件描述符的過程不能輕易地轉(zhuǎn)移到遠(yuǎn)程機(jī)器中。同時還要考慮每個過程所要訪問的數(shù)據(jù)所處的位置。例如,lookupw需要訪問全部單詞數(shù)據(jù)庫,如果執(zhí)行l(wèi)ookupw的機(jī)器不同于字典所處的機(jī)器,對lookupw 的RPC調(diào)用就必須將整個字典作為參數(shù)來傳遞。將巨大的數(shù)據(jù)結(jié)構(gòu)作為參數(shù)傳遞 給遠(yuǎn)程過程的效率非常低。一般來說執(zhí)行過程的機(jī)器應(yīng)當(dāng)與放置過程所要訪問數(shù)據(jù)的機(jī)器是同一臺,將巨大的數(shù)據(jù)結(jié)構(gòu)傳遞給遠(yuǎn)程過程的效率是很低的。對于本應(yīng)用來說,應(yīng)當(dāng)把過程 insertw ,deletew,initw ,lookupw和字典本 身放在同一臺機(jī)器中。下圖說明了這種劃分方式:H算機(jī)戈k的近程樫徑il算

18、機(jī)劃分程序.jpg相當(dāng)于將dict.c劃分成了兩個文件dictl.c和dict2.c。這里的兩個程序只是一個示例,不需要寫出來,但是后面將要編寫與這兩個程序相似的 另外兩個程序。文件dictl.c包含主程序和過程nextin :CODE:/* dictl.c */#includevstdio.h>#includevstdlib.h> #include<ct yp e.h> #include<string.h>!-#define MAXWORD 50 char dictDICTSIZMAXWORD+1;int nwords = 0;int nextin(cha

19、r *cmd, char *word)int i, j;char buf100 = 0;if (fgets(buf, sizeof(buf)-1, stdin) = NULL) return -1;for (i = 0; i < 100; i+)if (bufi != ' ')break;if (i = 100)return -1;*cmd = bufi;while (1)if (i = 100)return -1;i+;if (bufi = ' ')continue;else if (bufi = 'n')return 0;elsebre

20、ak;j = 0;while (bufi != 'n')*word+ = bufi;j+;return j;i+;!-int main(int argc, char *argv)char wordMAXWORD+1;char cmd;int wrdlen;while (1)wrdlen = nextin(&cmd, word);if (wrdlen < 0)exit(0);wordwrdlen = '0'switch(cmd)case 'I':initw();printf("Dictionary initialized t

21、o empty.n");break;case 'i':insertw(word);printf("%s inserted.n", word); break;case 'd':if (deletew(word)printf("%s deleted.n", word);elseprintf("%s not found.n", word);break;case 'l':if (lookupw(word)printf("%s is found.n", word);

22、elseprintf("%s is not found.n", word);break;case 'q':printf("program quits.n");exit(0);default:printf("command %c invalid.n", cmd);break;return 0;!- 文件 dict2.c 包含了來自最初的應(yīng)用程序的一些函數(shù)。它們將成為遠(yuǎn)程 程序的一部分。另外,還包含對各個函數(shù)要共享的全局?jǐn)?shù)據(jù)的聲明。CODE:/* dict2.c */ #include<string.h> #d

23、efine MAXWORD 50 #define DICTSIZ 100 char dictDICTSIZMAXWORD+1;int nwords = 0;int initw()nwords = 0;return 1;int insertw(char *word)strcpy(dictnwords, word);nwords+;return nwords;int deletew(char *word)int i;for (i = 0; i < nwords; i+)if (strcmp(word, dicti) = 0)nwords-;strcpy(dicti, dictnwords);

24、return 1;return 0;int lookupw(char *word)int i;for (i = 0; i < nwords; i+)if (strcmp(word, dicti) = 0) return 1;return 0;注意,對符號常量MAXWORD定義在兩個構(gòu)件中都出現(xiàn)了,因為它們都 要聲明用于存儲字的變量, 然而,只有在文件 dict2.c 中才含有用于存儲 字典的數(shù)據(jù)結(jié)構(gòu)的聲明,因為只有遠(yuǎn)程過程才包含字典的數(shù)據(jù)結(jié)構(gòu)。此時gcc - c dictl.cgcc - c dict2.c 編譯一下檢查是否有語法錯誤。( 3)創(chuàng)建 rpcgen 規(guī)約 程序員一旦為某個分

25、布式程序選擇了一種結(jié)構(gòu),就可以準(zhǔn)備 rpc 規(guī)約 了。從本質(zhì)上說, rpcgen 規(guī)約文件包含了對遠(yuǎn)程程序的聲明以及它所使 用的數(shù)據(jù)結(jié)構(gòu)。該規(guī)約文件包含常量, 類型定義,以及對客戶和服務(wù)器程序的聲明。 即: 聲明在客戶或服務(wù)器中所使用的常量RPC編程語言來給出。對于該例子的規(guī)約如下,聲明所使用的數(shù)據(jù)類型 聲明遠(yuǎn)程程序,每個程序中所包含的過程以及它們的參數(shù)類型 所有這些聲明必須用rdict.x :CODE:/* rdict.x */const MAXWORD = 50;const DICTSIZ = 100;struct exampleint exfield1;char exfield2;pro

26、gram RDICTPROG/*遠(yuǎn)程程序的名稱 */version RDICTVERS/*版本 */int INITW(void) = 1; int INSERTW(string)= 2;int DELETEW(string) = 3;int LOOKUPW(string)= 4;/*/*/*/*第一個函數(shù) */第二個函數(shù) */第三個函數(shù) */第四個函數(shù) */該程序的版本號 = 0x30090949;/*遠(yuǎn)程程序標(biāo)識 , 必須唯一 */ ( 4)運(yùn)行 rpcgen在完成了規(guī)約后,程序員運(yùn)行 rpcgen 來檢查語法錯誤,并且生成四個代碼文件: rdict.h rdict_clnt.c rdic

27、t_svc.c和 rdict_xdr.c 。 = 1;/*/輸入命令:rpcgen rdict.x為rpcgen產(chǎn)生的XDF轉(zhuǎn)換文件,XDF即卩External Data 。是數(shù)據(jù)傳輸?shù)囊粋€規(guī)范。為rpcgen產(chǎn)生的客戶端的代碼。為rpcgen產(chǎn)生的服務(wù)器端的代碼。rdict.h 為rpcgen產(chǎn)生的.h文件,即頭文件。rdict_xdr.cRep rese ntati onrdict_cl nt.crdict_svc.c這些文件一旦生成,就可以被編譯成目標(biāo)代碼的形式。-c rdict_cInt.c-c rdict_svc.c-c rdict_xdr.cgccgccgcc到了這一步,需要程序員

28、再編寫接口過程。程序員需要編寫四個程序, 分別為客戶端程序與客戶端的接口程序,服務(wù)器程序與服務(wù)器端的接口程 序。如下圖所示,圖中陰影部分為需要程序員自己編寫的代碼:稈序框架jpg(5)編寫接口過程rpcgen 產(chǎn)生的文件并沒有構(gòu)成完整的程序,它還要求程序員必須編寫 客戶端和服務(wù)器端的接口??蛻舳私涌?rdict_cif.c 程序如下所示:CODE:/* rdict_cif.c */ #include<rpc/rpc.h> #include<stdio.h> #define RPC_CLNT #include"rdict.h" extern CLIEN

29、T *handle;static int *ret;int initw()ret = initw_1(0, handle);return ret = NULL ? 0 : *ret;int insertw(char *word)char *arg;arg = &word;ret = insertw_1(arg, handle);return ret = NULL ? 0 : *ret;int deletew(char *word)char *arg;arg = &word;ret = deletew_1(arg, handle);return ret = NULL ? 0 :

30、*ret;int lookupw(char *word)char *arg;arg = &word;ret = lookupw_1(arg, handle);return ret = NULL ? 0 : *ret; 服務(wù)器端接口例程 rdict_sif.c 如下:CODE:/* rdict_sif.c */ #include<rpc/rpc.h> #define RPC_SVC #include"rdict.h" static int retcode;int initw(void), insertw(char *), deletew(char *),

31、lookupw(char *);int *insertw_1_svc(char *w, struct svc_req* rqstp)retcode = insertw(*(char*)w);return &retcode;int *initw_1_svc(void* w, struct svc_req* rqstp)retcode = initw();return &retcode;int *deletew_1_svc(char *w, struct svc_req* rqstp)retcode = deletew(*(char*)w);return &retcode;

32、int *lookupw_1_svc(char *w, struct svc_req *rqstp)retcode = lookupw(*(char*)w);return &retcode;(6)編譯和鏈接客戶程序首先gcc - c rdict_cif.c編譯客戶端的接口程序。再 gcc - c rdict_xdr.c 編譯 XDR程序。RPC!信例程用該句柄和而客戶端程序還需要聲明并初始化一個句柄, 服務(wù)器通信。客戶端程序 rdict.c 如下:CODE:/* rdict.c */ #include<rpc/rpc.h> #include<stdlib.h>

33、#include<stdio.h> #include<ctype.h> #include"rdict.h"服務(wù)器端 ip address */#define MAXWORD 50 #define RMACHINE "" /*CLIENT *handle;int nextin(char *cmd, char *word)int i, j;char buf100 = 0;if (fgets(buf, sizeof(buf)-1, stdin) = NULL)return -1;for (i = 0; i < 1

34、00; i+) if (bufi != ' ')break;if (i = 100) return -1;*cmd = bufi;while (1)if (i = 100)return -1;i+;if (bufi = ' ')continue;else if (bufi = 'n')return 0;elsebreak;j = 0;while (bufi != 'n')*word+ = bufi;!-j+;return j;int main(int argc, char *argv)char wordMAXWORD+1;char

35、 cmd;int wrdlen;handle = clnt_create(RMACHINE, RDICTPROG, RDICTVERS, "tcp");if (handle = NULL)printf("cound not contact remote program.n");exit(1);while (1)wrdlen = nextin(&cmd, word);if (wrdlen < 0)exit(0);wordwrdlen = '0'switch(cmd)case 'I':initw();printf("Dictionary initialized to empty.n");break;case 'i':insertw(word);printf("%s inserted.n", word);break;case 'd':if (deletew(word)printf("%s deleted.n", word); elseprintf("%s not found

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論