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

 

 

 

 
posted @ 2024-09-11 15:46  lsgxeva  阅读(81)  评论(0编辑  收藏  举报