RPC學習筆記

在查看libc6-dev軟件包提供的工具(用 dpkg -L libc6-dev 命令)的時候,發現此軟件包提供了一個有用的工具rpcgen命令。
通過rpcgen的man手冊看到此工具的作用是把RPC源程序編譯成C語言源程序,從而輕松實現遠程過程調用。
下面的例子程序的作用是客戶端程序取中心服務器上時間的,編程過程如下:
先編寫一個 “ RPC 語言 ” ( RPC Language ( Remote Procedure Call Language ) ) 的源文件 test.x ,文件后綴名為 x 。
源代碼如下:
program TESTPROG {
?? version VERSION {
???? string TEST(string) = 1;
?? } = 1;
} = 87654321;
說明:這里數字87654321是RPC程序編號,還有VERSION版本號為1,都是給RPC服務程序用的。同時指定程序接受一個字符串參數。

運行這個命令:
rpcgen test.x
將生成三個源文件:
test_clnt.c? test.h? test_svc.c
源文件test_clnt.c 內容如下:
/*
?* Please do not edit this file.
?* It was generated using rpcgen.
?*/

#include <memory.h> /* for memset */
#include "test.h"

/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };

char **
test_1(char **argp, CLIENT *clnt)
{
??????? static char *clnt_res;

??????? memset((char *)&clnt_res, 0, sizeof(clnt_res));
??????? if (clnt_call (clnt, TEST,
??????????????? (xdrproc_t) xdr_wrapstring, (caddr_t) argp,
??????????????? (xdrproc_t) xdr_wrapstring, (caddr_t) &clnt_res,
??????????????? TIMEOUT) != RPC_SUCCESS) {
??????????????? return (NULL);
??????? }
??????? return (&clnt_res);
}
說明:這是一個客戶端調用函數,即客戶端代碼需要用到此函數。

源文件test.h內容如下:
/*
?* Please do not edit this file.
?* It was generated using rpcgen.
?*/

#ifndef _TEST_H_RPCGEN
#define _TEST_H_RPCGEN

#include <rpc/rpc.h>


#ifdef __cplusplus
extern "C" {
#endif


#define TESTPROG 87654321
#define VERSION 1

#if defined(__STDC__) || defined(__cplusplus)
#define TEST 1
extern? char ** test_1(char **, CLIENT *);
extern? char ** test_1_svc(char **, struct svc_req *);
extern int testprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);

#else /* K&R C */
#define TEST 1
extern? char ** test_1();
extern? char ** test_1_svc();
extern int testprog_1_freeresult ();
#endif /* K&R C */

#ifdef __cplusplus
}
#endif

#endif /* !_TEST_H_RPCGEN */
說明:這里定義了一些公用頭文件。

源文件test_svc.c內容如下:
/*
?* Please do not edit this file.
?* It was generated using rpcgen.
?*/

#include "test.h"
#include <stdio.h>
#include <stdlib.h>
#include <rpc/pmap_clnt.h>
#include <string.h>
#include <memory.h>
#include <sys/socket.h>
#include <netinet/in.h>

#ifndef SIG_PF
#define SIG_PF void(*)(int)
#endif

static void
testprog_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
??????? union {
??????????????? char *test_1_arg;
??????? } argument;
??????? char *result;
??????? xdrproc_t _xdr_argument, _xdr_result;
??????? char *(*local)(char *, struct svc_req *);

??????? switch (rqstp->rq_proc) {
??????? case NULLPROC:
??????????????? (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);
??????????????? return;

??????? case TEST:
??????????????? _xdr_argument = (xdrproc_t) xdr_wrapstring;
??????????????? _xdr_result = (xdrproc_t) xdr_wrapstring;
??????????????? local = (char *(*)(char *, struct svc_req *)) test_1_svc;
??????????????? break;

??????? default:
??????????????? svcerr_noproc (transp);
??????????????? return;
??????? }
??????? memset ((char *)&argument, 0, sizeof (argument));
??????? if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
??????????????? svcerr_decode (transp);
??????????????? return;
??????? }
??????? result = (*local)((char *)&argument, rqstp);
??????? if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {
??????????????? svcerr_systemerr (transp);
??????? }
??????? if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
??????????????? fprintf (stderr, "%s", "unable to free arguments");
??????????????? exit (1);
??????? }
??????? return;
}

int
main (int argc, char **argv)
{
??????? register SVCXPRT *transp;

??????? pmap_unset (TESTPROG, VERSION);

??????? transp = svcudp_create(RPC_ANYSOCK);
??????? if (transp == NULL) {
??????????????? fprintf (stderr, "%s", "cannot create udp service.");
??????????????? exit(1);
??????? }
??????? if (!svc_register(transp, TESTPROG, VERSION, testprog_1, IPPROTO_UDP)) {
??????????????? fprintf (stderr, "%s", "unable to register (TESTPROG, VERSION, udp).");
??????????????? exit(1);
??????? }

??????? transp = svctcp_create(RPC_ANYSOCK, 0, 0);
??????? if (transp == NULL) {
??????????????? fprintf (stderr, "%s", "cannot create tcp service.");
??????????????? exit(1);
??????? }
??????? if (!svc_register(transp, TESTPROG, VERSION, testprog_1, IPPROTO_TCP)) {
??????????????? fprintf (stderr, "%s", "unable to register (TESTPROG, VERSION, tcp).");
??????????????? exit(1);
??????? }

??????? svc_run ();
??????? fprintf (stderr, "%s", "svc_run returned");
??????? exit (1);
??????? /* NOTREACHED */
}
說明:這是一個標準的服務器端代碼。

運行下列命令生成一個客戶端源文件test_client.c:
rpcgen -Sc -o test_client.c test.x
源代碼test_client.c如下:
/*
?* This is sample code generated by rpcgen.
?* These are only templates and you can use them
?* as a guideline for developing your own functions.
?*/

#include "test.h"


void
testprog_1(char *host)
{
??????? CLIENT *clnt;
??????? char * *result_1;
??????? char * test_1_arg;

#ifndef DEBUG
??????? clnt = clnt_create (host, TESTPROG, VERSION, "udp");
??????? if (clnt == NULL) {
??????????????? clnt_pcreateerror (host);
??????????????? exit (1);
??????? }
#endif? /* DEBUG */

??????? result_1 = test_1(&test_1_arg, clnt);
??????? if (result_1 == (char **) NULL) {
??????????????? clnt_perror (clnt, "call failed");
??????? }
#ifndef DEBUG
??????? clnt_destroy (clnt);
#endif?? /* DEBUG */
}


int
main (int argc, char *argv[])
{
??????? char *host;

??????? if (argc < 2) {
??????????????? printf ("usage: %s server_host\n", argv[0]);
??????????????? exit (1);
??????? }
??????? host = argv[1];
??????? testprog_1 (host);
exit (0);
}
運行這個命令生成服務端源文件test_srv_func.c:
rpcgen -Ss -o test_srv_func.c test.x
源文件test_srv_func.c內容如下:
/*
?* This is sample code generated by rpcgen.
?* These are only templates and you can use them
?* as a guideline for developing your own functions.
?*/

#include "test.h"

char **
test_1_svc(char **argp, struct svc_req *rqstp)
{
??????? static char * result;

??????? /*
???????? * insert server code here
???????? */

??????? return &result;
}
說明:這是一個服務器端調用的函數。

至此,我們就可以編譯生成程序來運行了。
用下面的命令編譯生成服務端程序test_server:
gcc -Wall -o test_server test_clnt.c test_srv_func.c test_svc.c
用下面的命令編譯生成客戶端程序test_client:
gcc -Wall -o test_client test_client.c test_clnt.c?
運行下列命令啟動服務端:
./test_server
運行下列命令可以進行客戶端測試:
./test_client 127.0.0.1
但是由于現的的服務端沒有處理客戶端請求,所以這樣的程序還不能完成任何工作。

下面我們先給服務端程序加上代碼,使這個服務器能完成一定的工作。即修改 test_srv_func.c ,在 “ * insert server code here ” 后面加上取時間的代碼,即修改后的 test_srv_func.c 代碼如下:
/*
?* This is sample code generated by rpcgen.
?* These are only templates and you can use them
?* as a guideline for developing your own functions.
?*/
#include <time.h>
#include "test.h"

char **
test_1_svc(char **argp, struct svc_req *rqstp)
{
??????? static char * result;
??????? static char tmp_char[128];
??????? time_t rawtime;

??????? /*
???????? * insert server code here
???????? */
??????? if( time(&rawtime) == ((time_t)-1) ) {
??????????????? strcpy(tmp_char, "Error");
??????????????? result = tmp_char;
??????????????? return &result;
??????? }
??????? sprintf(tmp_char, "服務器當前時間是 :%s", ctime(&rawtime));
??????? result = tmp_char;

??????? return &result;
}
再修改客戶端代碼以顯示服務器端返回的內容,即修改test_client.c源文件,只需要修改其中的函數testprog_1,修改后如下:
void
testprog_1(char *host)
{
??????? CLIENT *clnt;
??????? char * *result_1;
??????? char * test_1_arg;

??????? test_1_arg = (char *)malloc(128);
#ifndef DEBUG
??????? clnt = clnt_create (host, TESTPROG, VERSION, "udp");
??????? if (clnt == NULL) {
??????????????? clnt_pcreateerror (host);
??????????????? exit (1);
??????? }
#endif? /* DEBUG */

??????? result_1 = test_1(&test_1_arg, clnt);
??????? if (result_1 == (char **) NULL) {
??????????????? clnt_perror (clnt, "call failed");
??????? }
??????? if (strcmp(*result_1, "Error") == 0) {
??????????????? fprintf(stderr, "%s: could not get the time\n", host);
??????????????? exit(1);
??????? }
??????? printf("收到消息 ... %s\n", *result_1);
#ifndef DEBUG
??????? clnt_destroy (clnt);
#endif?? /* DEBUG */
}
重新運行上述編譯命令編譯生成程序:
gcc -Wall -o test_server test_clnt.c test_srv_func.c test_svc.c
gcc -Wall -o test_client test_client.c test_clnt.c?
啟動服務端程序后運行客戶端程序如下:
./test_client 127.0.0.1
收到消息 ... 服務器當前時間是 :Tue Feb 27 11:45:21 2007
為了省略每次輸入gcc命令的麻煩,也為了維護我們的工程,可以運行下列命令生成一個Makefile文件:
rpcgen -Sm test.x > Makefile
生成的Makefile內容如下:
# This is a template Makefile generated by rpcgen

# Parameters

CLIENT = test_client
SERVER = test_server

SOURCES_CLNT.c =?
SOURCES_CLNT.h =?
SOURCES_SVC.c =?
SOURCES_SVC.h =?
SOURCES.x = test.x

TARGETS_SVC.c =???????
TARGETS_CLNT.c =???????
TARGETS =????????????

OBJECTS_CLNT = $(SOURCES_CLNT.c:%.c=%.o) $(TARGETS_CLNT.c:%.c=%.o)
OBJECTS_SVC = $(SOURCES_SVC.c:%.c=%.o) $(TARGETS_SVC.c:%.c=%.o)
# Compiler flags?

CFLAGS += -g?
LDLIBS += -lnsl
RPCGENFLAGS =?

# Targets?

all : $(CLIENT) $(SERVER)

$(TARGETS) : $(SOURCES.x)?
??????? rpcgen $(RPCGENFLAGS) $(SOURCES.x)

$(OBJECTS_CLNT) : $(SOURCES_CLNT.c) $(SOURCES_CLNT.h) $(TARGETS_CLNT.c)?

$(OBJECTS_SVC) : $(SOURCES_SVC.c) $(SOURCES_SVC.h) $(TARGETS_SVC.c)?

$(CLIENT) : $(OBJECTS_CLNT)?
??????? $(LINK.c) -o $(CLIENT) $(OBJECTS_CLNT) $(LDLIBS)?

$(SERVER) : $(OBJECTS_SVC)?
??????? $(LINK.c) -o $(SERVER) $(OBJECTS_SVC) $(LDLIBS)

?clean:
???????? $(RM) core $(TARGETS) $(OBJECTS_CLNT) $(OBJECTS_SVC) $(CLIENT) $(SERVER)
由于我們手工生成了源文件,所以要修改一下這個Makefile,修改后如下:
# This is a template Makefile generated by rpcgen

# Parameters

CLIENT = test_client
SERVER = test_server

SOURCES_CLNT.c =?
SOURCES_CLNT.h =?
SOURCES_SVC.c =?
SOURCES_SVC.h =?
SOURCES.x = test.x

TARGETS_SVC.c = test_clnt.c test_srv_func.c test_svc.c
TARGETS_CLNT.c = test_clnt.c test_client.c?
TARGETS = test.h?? test_clnt.c test_svc.c????

OBJECTS_CLNT = $(SOURCES_CLNT.c:%.c=%.o) $(TARGETS_CLNT.c:%.c=%.o)
OBJECTS_SVC = $(SOURCES_SVC.c:%.c=%.o) $(TARGETS_SVC.c:%.c=%.o)
# Compiler flags?

CFLAGS += -g?
LDLIBS += -lnsl
RPCGENFLAGS =?

# Targets?

all : $(CLIENT) $(SERVER)

$(TARGETS) : $(SOURCES.x)?
??????? rpcgen $(RPCGENFLAGS) $(SOURCES.x)

$(OBJECTS_CLNT) : $(SOURCES_CLNT.c) $(SOURCES_CLNT.h) $(TARGETS_CLNT.c)?

$(OBJECTS_SVC) : $(SOURCES_SVC.c) $(SOURCES_SVC.h) $(TARGETS_SVC.c)?

$(CLIENT) : $(OBJECTS_CLNT)?
??????? $(LINK.c) -o $(CLIENT) $(OBJECTS_CLNT) $(LDLIBS)?

$(SERVER) : $(OBJECTS_SVC)?
??????? $(LINK.c) -o $(SERVER) $(OBJECTS_SVC) $(LDLIBS)

clean:
??????? $(RM) core $(TARGETS) $(OBJECTS_CLNT) $(OBJECTS_SVC) $(CLIENT) $(SERVER) *~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445046.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445046.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445046.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法(10)-leetcode-explore-learn-數據結構-鏈表雙指針技巧

leetcode-explore-learn-數據結構-鏈表21.概述2.例題2.1 環形鏈表判斷2.2 環形鏈表22.3 相交鏈表2.4 刪除鏈表的倒數第N個節點3.小結本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode官網&#xff1a;https://leetcode-cn…

一個簡單的游戲服務器框架

最近看到百度空間的一個帖子&#xff0c;不錯&#xff0c;在這里整理下&#xff0c;轉載至我的博客里&#xff0c;開始自己慢慢琢磨寫一個框架。 我先從上層結構說起&#xff0c;一直到實現細節吧&#xff0c;想起什么就寫什么。 第一部分 服務器邏輯 服務器這邊簡單的分為三…

堆和棧的精華大總結

Java內存分配原理 棧、堆、常量池雖同屬Java內存分配時操作的區域&#xff0c;但其適用范圍和功用卻大不相同。 一般Java在內存分配時會涉及到以下區域&#xff1a; ◆寄存器&#xff1a;我們在程序中無法控制 ◆棧&#xff1a;存放基本類型的數據和對象的引用&#xff0c;但…

算法(11)-leetcode-explore-learn-數據結構-鏈表的經典問題

leetcode-explore-learn-數據結構-鏈表31.反轉一個鏈表2.移除鏈表元素3.奇偶鏈表4.回文鏈表5.小結本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode官網&#xff1a;https://leetcode-cn.com/explore/learn/card/linked-l…

探索式軟件測試

James A.Whittaker [美] 詹姆斯惠特克&#xff08;軟件測試領域絕對的大師&#xff09;著作《Exploratory Software Testing》&#xff0c;中文名《探索式軟件測試》&#xff0c;記得當時被這本書深深吸引啦&#xff08;我不知道有多少做測試的小伙伴看過這本書&#xff09;&am…

Linux線程池的設計

我設計這個線程池的初衷是為了與socket對接的。線程池的實現千變萬化&#xff0c;我得這個并不一定是最好的&#xff0c;但卻是否和我心目中需求模型的。現把部分設計思路和代碼貼出&#xff0c;以期拋磚引玉。個人比較喜歡搞開源&#xff0c;所以大家如果覺得有什么需要改善的…

算法(12)-leetcode-explore-learn-數據結構-雙鏈表的設計

leetcode-explore-learn-數據結構-鏈表4雙鏈表的設計本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode官網&#xff1a;https://leetcode-cn.com/explore/learn/card/linked-list/所有例題的編程語言為python 雙鏈表的設…

安全方面知識

什么是文件上傳漏洞 文件上傳漏洞是指 由于程序員在對用戶文件上傳部分的控制不足或者處理缺陷&#xff0c;而導致的用戶可以越過其本身權限向服務器上上傳可執行的動態腳本文件 這里上傳的文件可以是木馬&#xff0c;病毒&#xff0c;惡意腳本或者WebShell等。 這種攻擊方式是…

CE游戲外掛工具

CHEAT ENGINE(以下簡稱CE)是我見過的最優秀的游戲作弊工具。它的優點多不勝數&#xff0c;雖然單獨從搜索游 戲里面的數值來說&#xff0c;它并不比其他同類軟件強多少&#xff0c;但它不僅僅是個游戲修改工具&#xff0c;它還有其他游戲修改軟件所沒有的一些特點&#xff0c;例…

外掛編程-動作模擬技術

幾乎所有的游戲都有大量繁瑣和無聊的攻擊動作以增加玩家的 功力,還有那些數不完的迷宮,這些好像已經成為了角色游戲的代名詞。現在,外掛可以幫助玩家從這些繁瑣而無聊 的工作中擺脫出來。 1. 鼠標模擬技術 幾乎所有的游戲中都使用了鼠標來改變角色的位置和方向,玩家僅用…

算法(13)-leetcode-explore-learn-數據結構-鏈表小結

leetcode-explore-learn-數據結構-鏈表51.小結2.例題2.1合并兩個有序鏈表思路1:迭代思路2:遞歸2.2 兩數相加2.3 扁平化多級雙向鏈表2.4 復制帶隨機指針的鏈表2.5 旋轉鏈表本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode…

leetcode121買賣股票的最佳時機

給定一個數組&#xff0c;它的第 i 個元素是一支給定股票第 i 天的價格。 如果你最多只允許完成一筆交易&#xff08;即買入和賣出一支股票&#xff09;&#xff0c;設計一個算法來計算你所能獲取的最大利潤。 注意你不能在買入股票前賣出股票。 示例 1: 輸入: [7,1,5,3,6,…

epoll的內核實現

epoll是由一組系統調用組成。 int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); select/poll的缺點在于&#xff1…

算法(14)-數據結構-二叉樹

leetcode-explore-learn-數據結構-二叉樹10.概述1.深度優先遍歷dfs1.1先序遍歷-中左右1.2中序遍歷-左中右1.3后序遍歷-左右中2.廣度優先遍歷bfs3.遍歷-常見問題3.1 二叉樹的最大深度自頂向下自底向上3.2對稱二叉樹3.3路徑總和4.重構-常見問題4.1根據中序和后序遍歷序列構造二叉…

多進程魚多線程的權衡選擇

最近有好多人在網上問道做游戲開發框架用多線程還是多進程呢,或者兩者之間的優缺點,等等類似的問題。下邊小高就帶您小小分析一下: 1、首先要明確進程和線程的含義:進程(Process)是具有一定獨立功能的程序關于某個數據集合上的一次運行活動,是系統進行資源分配和調度的一…

leetcode322 零錢兌換

給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額&#xff0c;返回 -1。 示例 1: 輸入: coins [1, 2, 5], amount 11 輸出: 3 解釋: 11 5 5 1 示例 2: 輸入: coins [2],…

給數據減肥 讓MySQL數據庫跑的更快

在數據庫優化工作中&#xff0c;使數據盡可能的小&#xff0c;使表在硬盤上占據的空間盡可能的小&#xff0c;這是最常用、也是最有效的手段之一。因為縮小數據&#xff0c;相對來說可以提高硬盤的讀寫速度&#xff0c;并且在查詢過程中小表的內容處理時所占用的系統資源比較少…

算法(15)-leetcode-explore-learn-數據結構-運用遞歸解決二叉樹的問題

leetcode-explore-learn-數據結構-二叉樹2本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode官網&#xff1a;https://leetcode-cn.com/explore/learn/card/data-structure-binary-tree/2/traverse-a-tree/7/

leetcode538 把二叉搜索樹轉換成累加樹

給定一個二叉搜索樹&#xff08;Binary Search Tree&#xff09;&#xff0c;把它轉換成為累加樹&#xff08;Greater Tree)&#xff0c;使得每個節點的值是原來的節點值加上所有大于它的節點值之和。 對于每一個點來說&#xff0c;自己的父&#xff0c;和自己父的右子樹都是大…

AWK常用命令華(1)

awk 調用: 1.調用awk: