首页 > 其他分享 >利用tirpc库实现简单的客户端和服务端

利用tirpc库实现简单的客户端和服务端

时间:2024-09-11 15:46:40浏览次数:8  
标签:tirpc 0.0 CALCULATOR svc xdr xprt calculator 服务端 客户端

利用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);
            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

相关文章

  • Java服务端中的数据验证:使用Bean Validation与Spring Validator的最佳实践
    Java服务端中的数据验证:使用BeanValidation与SpringValidator的最佳实践大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在Java服务端开发中,数据验证是确保应用数据准确性和可靠性的关键步骤。本文将探讨BeanValidation和SpringValidator这两种数......
  • Java服务端开发中的请求优化:从HTTP/1.1到HTTP/2与gRPC的升级
    Java服务端开发中的请求优化:从HTTP/1.1到HTTP/2与gRPC的升级大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代Java服务端开发中,提升请求性能是至关重要的。随着HTTP/2和gRPC的引入,优化请求性能变得更加有针对性和高效。本文将探讨如何从HTTP/1.1......
  • NetworkManager接收和处理客户端请求通信机制
    NetworkManager守护进程通过监听D-Bus通信来接收和处理来自客户端(如nmcli或其他应用程序)的请求。这是Linux中进程间通信(IPC)的一种常见方式。D-Bus是一个消息总线系统,允许应用程序在不直接通信的情况下,通过消息总线交换数据。NetworkManager使用D-Bus作为其主要的通信机......
  • Kafka客户端核心参数详解
    目录从客户端理解Kafka正确使用方式一、客户端1、消息发送者主流程2、消息消费者主流程二、从客户端属性来梳理客户端工作机制1、消费者分组消费机制2、生产者拦截器机制3、消息序列化机制4、消息分区路由机制5、生产者消息缓存机制6、发送应答机制7、生产者消息......
  • KingFusion|KingFusion客户端接入KingHistorian历史数据
    哈喽,你好啊,我是雷工!本来想着再也不会使用到KingFusion这个软件了,结果有个相关的项目有一些问题让去处理,时隔多半年不得不重新再次打开这个软件,庆幸的是虚拟机内的软件还能正常运行,避免了重新安装一遍的繁琐;前面记录了《KingIOServer数据存入KingHistorian的......
  • 实时监控windows服务端口连接并打印到一个文件上
    话不多说,直接上脚本,test.bat 文件,目录生成netstat_log.txt,文件里面直接时间和netstat 信息都打印在那了@echooffset"logfile=netstat_log.txt":loop  echo%date%%time%>>%logfile%  echo.>>%logfile%  netstat-ano>>%logfile%  echo.>>%lo......
  • ssh客户端选择
    背景挑选一款用于连接linux主机的ssh客户端。当然,ssh不止可以连接远程linu主机,但我主要是这个用途。需求保存使用的连接,下次连接可以快速连接上,最好不需要输入密码。界面最好好看一些,像PuTTY这种就显得有些老了。最好是多平台的,比如linux和windows都能用。最好是开源的,......
  • 在Java服务端实现策略模式:如何灵活应对业务逻辑的多变性
    在Java服务端实现策略模式:如何灵活应对业务逻辑的多变性大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在Java服务端开发中,业务逻辑的复杂性和多变性常常需要灵活的设计模式来应对。策略模式是一种经典的设计模式,用于定义一系列算法,将每一个算法封装......
  • Java中的请求幂等性处理:如何确保服务端的操作重复安全
    Java中的请求幂等性处理:如何确保服务端的操作重复安全大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在服务端开发中,请求幂等性是确保系统稳定性和可靠性的关键因素之一。请求幂等性意味着一个操作可以重复执行多次,但其结果不会改变,这对于避免重复提......
  • U3D德州工程源码带视频教程带控服务端打包透视客户端u3d打包java后端Spring Boot框架
    U3D德州工程源码带控服务端打包透视客户端u3d打包java后端SpringBoot框架实现技术安卓苹果U3D开发,C#语言后端java SpringBo完整开源不加密,搭建视频教程https://www.bilibili.com/video/BV1PnHLewEkZ/长达八十分钟的搭建教程......