其他分享
首页 > 其他分享> > RPC简单使用,编写远程计算器

RPC简单使用,编写远程计算器

作者:互联网

1、RPC简介

RPC是Remote Procedure Call的缩写,利用RPC可以实现本机上调用远程机的函数或过程。调用过程如下:
在这里插入图片描述

2、外部数据表示(XDR)

2.1、简介

XDR是数据描述与编码的标准。XDR协议对于在不同体系的计算机之间进行数据传输非常有用。XDR属于ISO表示层。XDR用语言描述数据结构并且仅用于描述结构。工作原理:
在这里插入图片描述

2.2、XDR相关函数

1、初始化XDR流
函数xdrstdio_create用户初始化一个XDR流。

#include<rpc/types.h>
#include<rpc/xdr.h>
void xdrstdio_create(XDR *xdrs, FILE *file, enum xdr_op op);

函数xdrstdio_create 中,参数fp为指向FILE结构的指针。参数op为枚举类型,取值为XDR_ENCODE, XDR_DECODE, XDR_FREE。XDR_ENCODE操作将数据编码后输出到流,XDR_DECODE操作将把流上的数据解吗。参数handle为指向XDR结构的指针,在以后的操作中,都要用到此指针。
2、释放XDR流
函数xdr_destroy用于释放已经分配的XDR流,参数handle为指向XDR句柄的指针。

#include<rpc/xdr.h>
void xdr_destroy(XDR *xdrs);

3、各种类型数据的XDR转换函数
其中handle为调用xdrstdio_create生成的,第二个参数为指向要转换的数据的指针。如果转换成功,则返回TRUE,否则返回FALSE。

#include<rpc/xdr.h>
#include<rpc/types.h>
bool_t xdr_int(XDR *xdrs, int *ip);     //整数
bool_t xdr_u_int(XDR *xdrs, unsigned *up);    //无符号整数
bool_t xdr_short(XDR *xdrs, short *sp);    //短整型
bool_t xdr_u_short(XDR *xdrs, unsigned short *usp);  //无符号短整型
bool_t xdr_long(XDR *xdrs, long *lp);       //长整型
bool_t xdr_u_long(XDR *xdrs, unsigned long *ulp);   //无符号长整型
bool_t xdr_bool(XDR *xdrs, bool_t *bp);     //布尔型
bool_t xdr_enum(XDR *xdrs, enum_t *ep);   //枚举
bool_t xdr_float(XDR *xdrs, float *fp);       //单精度浮点型
bool_t xdr_double(XDR *xdrs, double *dp);   //双精度浮点型
bool_t xdr_char(XDR *xdrs, char *cp);        //字符型
bool_t xdr_string(XDR *xdrs, char **sp, unsigned int maxsize);   //字符串型
bool_t xdr_bytes(XDR *xdrs, char **sp, unsigned int *sizep,
                        unsigned int maxsize);    //字节流
bool_t xdr_vector(XDR *xdrs, char *arrp, unsigned int size,
                         unsigned int elsize, xdrproc_t elproc);  //定长数组
bool_t xdr_array(XDR *xdrs, char **arrp, unsigned int *sizep,
                        unsigned int maxsize, unsigned int elsize,
                        xdrproc_t elproc);  //变长数组

3、RPC编程

3.1、简介

RPC程序避免了网络界面的细节,为程序员提供了网络服务,而不要求他们了解基本网络的存在方式及功能。
一个RPC程序通常包括下列主要部分:
“RPC程序的程序号,版本号和过程号”:RPC程序用程序号,程序版本和过程号唯一地标志通过RPC调用的过程。
“网络选择”:可以编写在指定传输和传输类型操作的程序,也可以编写在系统或用户选择的传输上操作的程序。
“rpcbind服务”:rpcbind是用来连接网络服务和通过网络地址的基础服务。
”外部数据表示XDR“:在RPC客户机方和服务器方之间传送的数据按XDR传输语法编码。
程序号
每个RPC过程由程序员、版本号和过程号唯一标志。
程序号规则:
在这里插入图片描述
网络选择
网络选择是通过它用户和应用程序可以根据需要选用所用的传输协议。主要是udp或tcp。
rpcbind服务
rpcbind协议定义了一个网络服务,为客户机提供一个标准的方法以查找服务器支持的任一远程程序的传输地址。rpcbind一般使用端口号111。

3.2、RPC调用

客户机首先调用clnt_create生成一个CLIENT结构的指针,然后调用函数clnt_call来调用远程过程,调用clnt_destroy关闭连接,最后退出应用程序。在服务器方,调用svcudp_create或svctcp_create生成一个SVCXPRT结构的指针,然后调用函数svc_register注册提供的服务,最后调用svc_run,服务端程序在退出时需要调用svc_destroy释放连接。

3.3、RPC相关函数

1、clnt_create函数
用于生成一个指向CLIENT结构的指针,如返回NULL,则表示出错。该函数的第一个参数为服务器名称或IP地址,第二个参数为程序号,第三个参数为版本号,第四个参数为协议类型。

#include<rpc/clnt.h>
CLIENT *clnt_create(char *host, unsigned long prog,
                           unsigned long vers, char *proto);

2、clnt_call函数
用于调用服务器的某一过程吗,函数返回RPC_SUCCESS,则代表调用成功,否则出错。函数第一个参数为clnt_create 生成的CLIENT结构的指针,第二个参数为过程号,第三个参数为调用的过程的返回类型,第四个参数为传递给过程的参数地址,第五个参数为编解码的函数名,第六个参数为编解码函数的参数地址,最后一个参数为给定的超时值。

#include<rpc/clnt.h>
enum clnt_stat clnt_call(CLIENT *clnt, unsigned long procnum,
                           xdrproc_t inproc, char *in,
                           xdrproc_t outproc, char *out,
                           struct timeval tout);

3、clnt_destroy函数
释放clnt_create返回的指针,关闭连接。

#include<rpc/clnt.h>
clnt_destroy(CLIENT *clnt);

4、svcudp_create函数
用于生成一个无连接的UDP服务的SVCXPRT结构的指针,返回NULL则出错。函数的唯一参数为套接字类型,一般为RPC_ANYSOCK。

#include<rpc/svc.h>
SVCXPRT *svcudp_create(int sock);

5、svctcp_create函数
用于生成一个面向连接的TCP服务的SVCXPRT结构的指针,返回NULL则出错。函数第一个参数为套接字类型,一般为RPC_ANYSOCK。第二个参数为发送缓冲区大小,为0代表取系统默认值,第三个参数为接收缓冲区大小,为0代表系统默认值。

#include<rpc/svc.h>
SVCXPRT *svctcp_create(int sock, unsigned int send_buf_size,
                              unsigned int recv_buf_size);

6、svc_register函数
用于向主机注册一项服务,返回TRUE代表成功,返回FALSE代表失败。函数第一个参数为svcudp_create 或svctcp_create生成的SVCXPRT结构的指针,第二个参数为程序号,第三个参数为版本号,第四个参数为处理请求的函数名,最后一个参数为协议名,IPPROTO_UDP代表UDP协议,IPPROTO_TCP代表TCP协议。

#include<rpc/svc.h>
bool_t svc_register(SVCXPRT *xprt, unsigned long prognum,
                           unsigned long versnum,
                           void (*dispatch)(svc_req *, SVCXPRT *),
                           unsigned long protocol);

svc_run函数
服务器程序注册完提供的服务后,调用函数svc_run等待客户机请求的到来,该函数不返回,也没有参数。

#include<rpc/svc.h>
void svc_run(void);

7、svc_sendreply函数
当服务器得到客户机请求的结构时,服务器程序需要调用函数svc_sendreply向客户机发送报文。函数返回TRUE则调用成功,FALSE则失败。函数第一个参数为SVCXPRT指针,第二个参数为编解码函数名称,第三个参数为结果的地址。

#include<rpc/svc.h>
bool_t svc_sendreply(SVCXPRT *xprt, xdrproc_t outproc, char *out);

4、实现远程计算器

4.1、公用头文件xdr_math.h

#include<iostream>
#include<rpc/types.h>
#include<rpc/xdr.h>
#include<rpc/clnt.h>
#include<rpc/svc.h>

struct MATH
{
    int op;
    float arg1;
    float arg2;
    float result;
};

bool xdr_math(XDR *xdrsp, struct MATH * resp)
{
    if(!xdr_int(xdrsp, &resp->op))
    {
        return false;
    }
    if(!xdr_float(xdrsp, &resp->arg1))
    {
        return false;
    }
    if(!xdr_float(xdrsp, &resp->arg2))
    {
        return false;
    }
    if(!xdr_float(xdrsp, &resp->result))
    {
        return false;
    }
    return TRUE;
}

#define MATH_PROG ((unsigned long)0x20000001)
#define MATH_VER ((unsigned long)0x1)
#define MATH_PROC ((unsigned long)0x1)
#define ADD 0
#define SUB 1
#define MUL 2
#define DIV 3

4.2、客户端程序math_client.cpp

#include "xdr_math.h"

using namespace std;
void usage(char * name)
{
    cout<<"Usage: "<<name<<"hostname/IPaddr\n";
    exit(1);
}

int main(int argc, char **argv)
{    
    if(argc != 2)
    {
        usage(argv[0]);
    }
    cout<<"choose the operation:\n \t 0---ADD\t 1---SUB\t 2---MUL\t 3---DIV\n";
    char c;
    cin>>c;
    struct MATH math;
    switch (c)
    {
        case '0':
            math.op = ADD;
            break;
        case '1':
            math.op = SUB;
            break;
        case '2':
            math.op = MUL;
            break;
        case '3':
            math.op = DIV;
            break;
        default :
            cout<< "error :operation\n";
            exit(1);
    }
    cout<<"Input the first number:";
    cin>>math.arg1;
    cout<<"Input the second number:";
    cin>>math.arg2;

    CLIENT * client = clnt_create(argv[1], MATH_PROG, MATH_VER, "tcp");
    //CLIENT * client = clnt_create(argv[1], MATH_PROG, MATH_VER, "udp");
    if(client == NULL)
    {
        cout<<"error :clnt_create\n";
        exit(1);
    }
    
    struct timeval timeout;
    timeout.tv_sec = 30;
    timeout.tv_usec = 0;
    
    enum clnt_stat stat = clnt_call(client, MATH_PROC, (xdrproc_t)xdr_math, (char *)&math, (xdrproc_t)xdr_math, (char *)&math, timeout);
    if(stat != RPC_SUCCESS)
    {
        clnt_perror(client, "Call Failed");
        exit(1);
    }
    cout<<"The Result is "<<math.result<<" .\n";
    clnt_destroy(client);
    return 0;    
}

4.3、服务器程序math_server.cpp

#include "xdr_math.h"
using namespace std;

static void mathprog(struct svc_req * rqstp, SVCXPRT *transp)
{
    switch(rqstp->rq_proc)
    {
        case NULLPROC:
            svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
            return ;
        case MATH_PROC:
            break;
        default:
            svcerr_noproc(transp);
            return ;
    }
    
    struct MATH math;
    memset((char *)&math, 0, sizeof(math));
    if(!svc_getargs(transp, (xdrproc_t)xdr_math, (caddr_t)&math))
    {
        svcerr_decode(transp);
        return;
    }
    switch(math.op)
    {
        case ADD:
            math.result = math.arg1 + math.arg2;
            break;
        case SUB:
            math.result = math.arg1 - math.arg2;
            break;
        case MUL:
            math.result = math.arg1 * math.arg2;
            break;
        case DIV:
            math.result = math.arg1 / math.arg2;
            break;
        default:
            break;
    }
    if(!svc_sendreply(transp, (xdrproc_t)xdr_math, (caddr_t)&math))
    {
        cout<<"error:unable to send reply\n";
        exit(1);
    }
    if(!svc_freeargs(transp, (xdrproc_t)xdr_math, (caddr_t)&math))
    {
        cout<<"error:unable to free args\n";
        exit(1);
    }
    return;
}

int main(int argc, char **argv)
{
    SVCXPRT *transpu = svcudp_create(RPC_ANYSOCK);
    if(transpu == NULL)
    {
        cout<<"error: cannot create udp service.\n";
        exit(1);
    }
    if(!svc_register(transpu, MATH_PROG, MATH_VER, mathprog, IPPROTO_UDP))
    {
        cout<<"error: cannot register (MATH_PROG udp) .\n";
        exit(1);
    }
    
    SVCXPRT *transpt = svctcp_create(RPC_ANYSOCK, 0, 0);
    if(transpt == NULL)
    {
        cout<<"error: cannot create tcp service.\n";
        exit(1);
    }
    if(!svc_register(transpt, MATH_PROG, MATH_VER, mathprog, IPPROTO_TCP))
    {
        cout<<"error: cannot register (ADDPROG tdp) .\n";
        exit(1);
    }
    svc_run();
    cout<<"error: scv_run returned";
    exit(1);
}

4.4、编译

g++ -o math_client math_client.cpp -lnsl
g++ -o math_server math_server.cpp -lnsl

4.5、执行

启动rpcbind

service rpcbind start

启动服务器程序

./math_server &

启动客户端程序

./math_client localhost

在这里插入图片描述

源代码:https://download.csdn.net/download/weixin_39407199/19130975

标签:create,unsigned,RPC,XDR,计算器,include,远程,math,xdr
来源: https://blog.csdn.net/weixin_39407199/article/details/117362947