利用tirpc库实现简单的客户端和服务端
来源 https://www.hlovefp.cn/blog/post/125.html
演示系统:openEuler release 22.03 LTS
1. 准备环境
Bashyum 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);
return;
}
}
int main(char *argv[], int argc)
{
// 1. 调用 svcudp_create 或 svctcp_create 生成一个SVCXPRT结构的指针
// SVCXPRT *svcudp_create(int sock);
// SVCXPRT *svctcp_create(int sock, unsigned int send_buf_size, unsigned int recv_buf_size);
SVCXPRT *xprt_udp = svcudp_create(RPC_ANYSOCK);
if (xprt_udp == NULL) {
printf("error: svcudp_create error.\n");
return -1;
}
SVCXPRT *xprt_tcp = svctcp_create(RPC_ANYSOCK, 0, 0);
if (xprt_tcp == NULL) {
printf("error: svctcp_create error.\n");
return -1;
}
/*
// 2. 调用 svc_register 注册提供的服务
// bool_t svc_register(SVCXPRT *xprt,
unsigned long prognum,
unsigned long versnum,
void (*dispatch)(svc_req *, SVCXPRT *),
unsigned long protocol);
*/
if(!svc_register(xprt_udp, CALCULATOR_PROG, CALCULATOR_VER, dispatch_handle, IPPROTO_UDP)) {
printf("error: svc_register dispatch_calculator udp error.\n");
return -1;
}
if(!svc_register(xprt_tcp, CALCULATOR_PROG, CALCULATOR_VER, dispatch_handle, IPPROTO_TCP)) {
printf("error: svc_register dispatch_calculator tcp error.\n");
return -1;
}
// 3. 调用svc_run
// void svc_run(void);
svc_run();
// 4. 程序退出时调用 svc_destroy 释放
// svc_destroy(SVCXPRT *xprt)
svc_destroy(xprt_udp);
svc_destroy(xprt_tcp);
return 0;
}
// gcc -I/usr/include/tirpc -ltirpc server.c request_xdr.c -o server
4. 客户端代码
C#include <stdio.h>
#include <string.h>
#include <rpc/clnt.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
int main(char *argv[], int argc)
{
const char *ip = "localhost";
struct timeval timeout;
calculator req, rsp;
enum clnt_stat stat;
char op = '0';
// 1. 调用 clnt_create 生成一个CLIENT结构的指针
// CLIENT *clnt_create(const char *ip, const rpcprog_t, const rpcvers_t, const char *);
CLIENT * client = clnt_create(ip, CALCULATOR_PROG, CALCULATOR_VER, "tcp");
//CLIENT * client = clnt_create(ip, CALCULATOR_PROG, CALCULATOR_VER, "udp");
if (NULL == client) {
printf("error: clnt_create error.\n");
return -1;
}
// 2. 调用 clnt_call 来调用远程过程
// enum clnt_stat clnt_call(CLIENT *rh, rpcproc_t proc, xdrproc_t xargs, void *argsp, drproc_t xres, void *resp, struct timeval timeout)
timeout.tv_sec = 30;
timeout.tv_usec = 0;
while(1) {
req.op = -1;
printf("choose the operation(0-ADD 1-SUB 2-MUL 3-DIV 4-QUIT): ");
scanf("%d", &(req.op));
if (req.op > 3 || req.op < 0) break;
printf("Input the first number: ");
scanf("%f", &(req.arg1));
printf("Input the second number: ");
scanf("%f", &(req.arg2));
//stat = rpc_call(ip, CALCULATOR_PROG, CALCULATOR_VER, CALCULATOR_PROC, (xdrproc_t)xdr_calculator, (char *)&req, (xdrproc_t)xdr_calculator, (char *)&rsp, "tcp");
stat = clnt_call(client, CALCULATOR_PROC, (xdrproc_t)xdr_calculator, (char *)&req, (xdrproc_t)xdr_calculator, (char *)&rsp, timeout);
if(stat != RPC_SUCCESS) {
clnt_perror(client, "Call Failed");
return -1;
}
printf("The Result is %lf \n\n", rsp.result);
}
printf("\n");
// 3. 调用 clnt_destroy 关闭连接
// clnt_destroy(CLIENT *rh)
clnt_destroy(client);
return 0;
}
// gcc -I/usr/include/tirpc -ltirpc client.c request_xdr.c -o client
5. 执行
启动服务端程序
Bash[root@localhost rpc]#service rpcbind start
[root@localhost rpc]#./server
新窗口运行客户端程序
Bash[root@localhost rpc]# ./client
choose the operation(0-ADD 1-SUB 2-MUL 3-DIV 4-QUIT): 3
Input the first number: 500
Input the second number: 20
The Result is 25.000000
choose the operation(0-ADD 1-SUB 2-MUL 3-DIV 4-QUIT): 2
Input the first number: 3
Input the second number: 8
The Result is 24.000000
choose the operation(0-ADD 1-SUB 2-MUL 3-DIV 4-QUIT): 1
Input the first number: 90
Input the second number: 1000
The Result is -910.000000
choose the operation(0-ADD 1-SUB 2-MUL 3-DIV 4-QUIT): 1
Input the first number: 9
Input the second number: 100
The Result is -91.000000
choose the operation(0-ADD 1-SUB 2-MUL 3-DIV 4-QUIT): 0
Input the first number: 9
Input the second number: 100
The Result is 109.000000
choose the operation(0-ADD 1-SUB 2-MUL 3-DIV 4-QUIT): q
查看端口
Bash[root@localhost rpc]# netstat -anp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:965 0.0.0.0:* LISTEN 21989/./server
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 852/rpcbind
tcp 0 0 127.0.0.1:965 127.0.0.1:855 ESTABLISHED 21989/./server
tcp 0 0 127.0.0.1:855 127.0.0.1:965 ESTABLISHED 22302/./client
tcp6 0 0 :::111 :::* LISTEN 852/rpcbind
udp 0 0 0.0.0.0:53297 0.0.0.0:* 852/rpcbind
udp 0 0 0.0.0.0:111 0.0.0.0:* 852/rpcbind
udp 0 0 0.0.0.0:965 0.0.0.0:* 21989/./server
udp6 0 0 :::111 :::* 852/rpcbind
udp6 0 0 :::52318 :::* 852/rpcbind
=========== End
标签:tirpc,0.0,CALCULATOR,svc,xdr,xprt,calculator,服务端,客户端 From: https://www.cnblogs.com/lsgxeva/p/18408346