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.mzph.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: