利用tirpc库实现简单的客户端和服务端
利用tirpc库实现简单的客户端和服务端
来源 https://www.hlovefp.cn/blog/post/125.html
演示系统:openEuler release 22.03 LTS
1. 准备环境
Bash
yum install rpcgen -y # rpcgen命令
yum -y install libtirpc libtirpc-devel # rpc库
一个RPC程序通常包括(每个RPC过程由程序员、版本号和过程号唯一标志):
程序号
版本号
过程号
网络选择 传输协议,主要是udp或tcp
rpcbind服务 rpcbind是用来连接网络服务和通过网络地址的基础服务,一般使用端口号111。
外部数据表示XDR 在RPC客户机方和服务器方之间传送的数据按XDR传输语法编码。
2. 生成XDR文件
RPC使用XDR标准传送数据,参考:https://www.hlovefp.cn/blog/post/124.html
Bash
[root@localhost rpc]# cat request.x # request.x 定义的客户端服务器数据结构
struct calculator {
int op;
float arg1;
float arg2;
float result;
};
[root@localhost rpc]# rpcgen request.x # 生成 request.h 和 request_xdr.c
C
[root@localhost rpc]# cat request.h
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#ifndef _REQUEST_H_RPCGEN
#define _REQUEST_H_RPCGEN
#include <rpc/rpc.h>
#ifdef __cplusplus
extern "C" {
#endif
struct calculator {
int op;
float arg1;
float arg2;
float result;
};
typedef struct calculator calculator;
/* the xdr functions */
#if defined(__STDC__) || defined(__cplusplus)
extern bool_t xdr_calculator (XDR *, calculator*);
#else /* K&R C */
extern bool_t xdr_calculator ();
#endif /* K&R C */
#ifdef __cplusplus
}
#endif
#endif /* !_REQUEST_H_RPCGEN */
Bash
[root@localhost rpc]# cat request_xdr.c
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include "request.h"
bool_t
xdr_calculator (XDR *xdrs, calculator *objp)
{
register int32_t *buf;
if (!xdr_int (xdrs, &objp->op))
return FALSE;
if (!xdr_float (xdrs, &objp->arg1))
return FALSE;
if (!xdr_float (xdrs, &objp->arg2))
return FALSE;
if (!xdr_float (xdrs, &objp->result))
return FALSE;
return TRUE;
}
3. 服务端代码
C
#include <stdio.h>
#include <string.h>
#include <rpc/auth.h>
#include <rpc/svc.h>
#include "request.h"
#define CALCULATOR_PROG ((unsigned long)0x20000001)
#define CALCULATOR_VER ((unsigned long)0x1)
#define CALCULATOR_PROC ((unsigned long)0x1)
#define CALCULATOR_ADD 0
#define CALCULATOR_SUB 1
#define CALCULATOR_MUL 2
#define CALCULATOR_DIV 3
void handle_calculator(struct svc_req *req, SVCXPRT *xprt)
{
calculator c;
memset(&c, 0x00, sizeof(c));
// 获取数据
if (!svc_getargs(xprt, (xdrproc_t)xdr_calculator, (caddr_t)&c)) {
printf("error: svc_getargs error.\n");
svcerr_decode(xprt);
return;
}
switch(c.op) {
case CALCULATOR_ADD:
c.result = c.arg1 + c.arg2;
break;
case CALCULATOR_SUB:
c.result = c.arg1 - c.arg2;
break;
case CALCULATOR_MUL:
c.result = c.arg1 * c.arg2;
break;
case CALCULATOR_DIV:
if (c.arg2 != 0) {
c.result = c.arg1 / c.arg2;
}
break;
default:
break;
}
if(!svc_sendreply(xprt, (xdrproc_t)xdr_calculator, (caddr_t)&c)) {
printf("error: svc_sendreply error.\n");
}
if(!svc_freeargs(xprt, (xdrproc_t)xdr_calculator, (caddr_t)&c)) {
printf("error: svc_freeargs error.\n");
}
}
void dispatch_handle(struct svc_req *req, SVCXPRT *xprt)
{
printf("rq_prog : %d service program number\n", req->rq_prog);
printf("rq_vers : %d service protocol version\n", req->rq_vers);
printf("rq_proc : %d the desired procedure\n", req->rq_proc);
// 当服务器得到客户机请求的结构时,服务器程序需要调用函数svc_sendreply向客户机发送报文。
// bool_t svc_sendreply(SVCXPRT *xprt, xdrproc_t outproc, char *out);
switch (req->rq_proc) {
case CALCULATOR_PROC:
handle_calculator(req, xprt);
return;
case NULLPROC:
svc_sendreply(xprt, (xdrproc_t)xdr_void, NULL);
return;
default:
svcerr_noproc(xprt);