Linux編程之從零開始搭建RPC分散式系統

来源:http://www.cnblogs.com/skyfsm/archive/2017/02/08/6379995.html
-Advertisement-
Play Games

我一畢業進公司就接觸到了RPC,主要是使用前輩們搭建好的RPC框架以及封裝好的RPC函數進行業務開發,雖說使用RPC框架開發已經近半年了,但一直想知道如何從零開始搭建起這麼一個好用的分散式通信系統框架,近日心血來潮,雖說沒人教怎麼搭建,但自己在網上查閱了大量資料後,開始自己一手一腳從零搭建這麼一個R ...


我一畢業進公司就接觸到了RPC,主要是使用前輩們搭建好的RPC框架以及封裝好的RPC函數進行業務開發,雖說使用RPC框架開發已經近半年了,但一直想知道如何從零開始搭建起這麼一個好用的分散式通信系統框架,近日心血來潮,雖說沒人教怎麼搭建,但自己在網上查閱了大量資料後,開始自己一手一腳從零搭建這麼一個RPC框架,所以就有了以下這篇文章,以記錄我的搭建過程。   首先對RPC做一個簡要介紹。    RPC的全稱是Remote Procedure Call,它能夠在本地以函數調用的形式來實現網路操作,讓程式員集中關註於業務邏輯,不用關心底層的數據通信。   網路通信的應用程式大多數是使用顯式網路編程(explicit network programming)的方式編寫的,比如我們所熟悉的socket編程。客戶端調用socket、connect、read和write,伺服器則調用socket、bind、listen等函數。我們熟悉的大多數應用程式(Web瀏覽器、Web伺服器、Telnet客戶、Telnet伺服器等程式)就是以這種方式編寫的。   編寫分散式應用程式的另一種方法就是使用隱式網路編程(implicit network programming)。遠程過程調用(RPC)提供了這麼一個工具。使用隱式網路編程的好處就是,程式員不需要把把大量精力放在網路通信程式的編寫上,因為這一塊工作已經有RPC框架幫你實現了,所以程式員可以把更多精力放在業務邏輯的開發上去。   這裡就不對RPC做進一步詳細的理論性解析,這篇文章主要講述RPC的實踐,我們將一步一步搭建起一個基於RPC的完整的分散式通信系統框架。本文分為兩個部分,第一部分講述如何利用rpcgen工具搭建起來RPC通用骨架,第二部分我們就使用該骨架進行進一步完善,增加相應的處理函數,把血肉補充完全,做一個簡單的分散式計算系統demo。   一、使用rpcgen工具生成RPC底層骨架   1.生成my.x文件,然後在該文件編寫以下程式 首先創建文件夾rpc(mkdir rpc),以後的所有文件都放在這個文件夾下。 創建my.x文件(my為文件名,.x為尾碼):vi my.x 在my.x填入下麵代碼:
#define MY_RPC_PROG_NUM         0x38000010   //程式號

struct my_io_data_s        //定義消息結構
{
    int mtype;
    int len;
    char data[1024];
};

typedef struct my_io_data_s my_io_data_t;

program MY_RPC_PROG { 

    version MY_RPC_VERS1 {
        int MY_RPCC(my_io_data_t) = 1;    /* 過程號 = 1 */
    } = 1;        /* Version number = 1 */

    version MY_RPC_VERS2 {
        my_io_data_t MY_RPCC(my_io_data_t) = 1;    /* 過稱號 = 1 */
    } = 2;        /* Version number = 2 */

} = MY_RPC_PROG_NUM;    /* Program number */
這裡我創建了兩個版本,version1和version2,版本的數量是可以自己定製的,如果你需要一個的話定義一個即可。因為我打算定義一個版本用於SET的操作,一個用於GET操作,所以定義了兩個版本。   上面使用了RPC語言,我對以上幾個特殊名詞做一下解釋。 每個RPC過程由程式號、版本號和過程號來唯一確定。 RPC版本號:程式號標誌一組相關的遠程過程,程式號的是有範圍的,我們需要在範圍內填寫程式號。  
程式號範圍 簡述
0x00000000 - 0x1FFFFFFF 由Sun公司定義,提供特定服務
0x20000000 - 0x3FFFFFFF 由程式員自己定義,提供本地服務或用於調試
0x40000000 - 0x5FFFFFFF 用於短時間使用的程式,例如回調程式
0x60000000 - 0xFFFFFFFF 保留程式號
  這裡我們使用的範圍當然是0x20000000 - 0x3FFFFFFF,我填的是0x38000010。   版本號:在version的大括弧里我們定義兩個我們將要使用的RPC調用函數的類型,比如: version 1:我們定義了int MY_RPCC(my_io_data_t),這表明我們以後PRC框架使用的RPC調用函數的函數類型那個將會是:int * my_rpcc_1(my_io_data_t *argp, CLIENT *clnt)   version 2:my_io_data_t MY_RPCC(my_io_data_t) 則會變成 my_io_data_t * my_rpcc_2(my_io_data_t *argp, CLIENT *clnt)   所以我們可以根據我們需要的類型模仿編寫即可。   2.使用rpcgen指令生成以下幾個文件 使用rpcgen my.x生成系列文件(可以加參數-C,表示使用ANSI C)     執行該指令後,文件夾下將出現以下幾個文件。 my.h:
/*
 * Please do not edit this file.
 * It was generated using rpcgen.
 */

#ifndef _MY_H_RPCGEN
#define _MY_H_RPCGEN

#include <rpc/rpc.h>


#ifdef __cplusplus
extern "C" {
#endif


struct my_io_data_s {
    int mtype;
    int len;
    char data[1024];
};
typedef struct my_io_data_s my_io_data_s;

typedef my_io_data_s my_io_data_t;

#define MY_RPC_PROG 666
#define MY_RPC_VERS1 1

#if defined(__STDC__) || defined(__cplusplus)
#define MY_RPCC 1
extern  int * my_rpcc_1(my_io_data_t *, CLIENT *);
extern  int * my_rpcc_1_svc(my_io_data_t *, struct svc_req *);
extern int my_rpc_prog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);

#else /* K&R C */
#define MY_RPCC 1
extern  int * my_rpcc_1();
extern  int * my_rpcc_1_svc();
extern int my_rpc_prog_1_freeresult ();
#endif /* K&R C */
#define MY_RPC_VERS2 2

#if defined(__STDC__) || defined(__cplusplus)
extern  my_io_data_t * my_rpcc_2(my_io_data_t *, CLIENT *);
extern  my_io_data_t * my_rpcc_2_svc(my_io_data_t *, struct svc_req *);
extern int my_rpc_prog_2_freeresult (SVCXPRT *, xdrproc_t, caddr_t);

#else /* K&R C */
extern  my_io_data_t * my_rpcc_2();
extern  my_io_data_t * my_rpcc_2_svc();
extern int my_rpc_prog_2_freeresult ();
#endif /* K&R C */

/* the xdr functions */

#if defined(__STDC__) || defined(__cplusplus)
extern  bool_t xdr_my_io_data_s (XDR *, my_io_data_s*);
extern  bool_t xdr_my_io_data_t (XDR *, my_io_data_t*);

#else /* K&R C */
extern bool_t xdr_my_io_data_s ();
extern bool_t xdr_my_io_data_t ();

#endif /* K&R C */

#ifdef __cplusplus
}
#endif

#endif /* !_MY_H_RPCGEN */
View Code

my_clnt.c:

/*
 * Please do not edit this file.
 * It was generated using rpcgen.
 */

#include <memory.h> /* for memset */
#include "my.h"

/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };

int *
my_rpcc_1(my_io_data_t *argp, CLIENT *clnt)
{
    static int clnt_res;

    memset((char *)&clnt_res, 0, sizeof(clnt_res));
    if (clnt_call (clnt, MY_RPCC,
        (xdrproc_t) xdr_my_io_data_t, (caddr_t) argp,
        (xdrproc_t) xdr_int, (caddr_t) &clnt_res,
        TIMEOUT) != RPC_SUCCESS) {
        return (NULL);
    }
    return (&clnt_res);
}

my_io_data_t *
my_rpcc_2(my_io_data_t *argp, CLIENT *clnt)
{
    static my_io_data_t clnt_res;

    memset((char *)&clnt_res, 0, sizeof(clnt_res));
    if (clnt_call (clnt, MY_RPCC,
        (xdrproc_t) xdr_my_io_data_t, (caddr_t) argp,
        (xdrproc_t) xdr_my_io_data_t, (caddr_t) &clnt_res,
        TIMEOUT) != RPC_SUCCESS) {
        return (NULL);
    }
    return (&clnt_res);
}
View Code

my_svc.c

/*
 * Please do not edit this file.
 * It was generated using rpcgen.
 */

#include "my.h"
#include <stdio.h>
#include <stdlib.h>
#include <rpc/pmap_clnt.h>
#include <string.h>
#include <memory.h>
#include <sys/socket.h>
#include <netinet/in.h>

#ifndef SIG_PF
#define SIG_PF void(*)(int)
#endif

static void
my_rpc_prog_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
    union {
        my_io_data_t my_rpcc_1_arg;
    } argument;
    char *result;
    xdrproc_t _xdr_argument, _xdr_result;
    char *(*local)(char *, struct svc_req *);

    switch (rqstp->rq_proc) {
    case NULLPROC:
        (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);
        return;

    case MY_RPCC:
        _xdr_argument = (xdrproc_t) xdr_my_io_data_t;
        _xdr_result = (xdrproc_t) xdr_int;
        local = (char *(*)(char *, struct svc_req *)) my_rpcc_1_svc;
        break;

    default:
        svcerr_noproc (transp);
        return;
    }
    memset ((char *)&argument, 0, sizeof (argument));
    if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
        svcerr_decode (transp);
        return;
    }
    result = (*local)((char *)&argument, rqstp);
    if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {
        svcerr_systemerr (transp);
    }
    if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
        fprintf (stderr, "%s", "unable to free arguments");
        exit (1);
    }
    return;
}

static void
my_rpc_prog_2(struct svc_req *rqstp, register SVCXPRT *transp)
{
    union {
        my_io_data_t my_rpcc_2_arg;
    } argument;
    char *result;
    xdrproc_t _xdr_argument, _xdr_result;
    char *(*local)(char *, struct svc_req *);

    switch (rqstp->rq_proc) {
    case NULLPROC:
        (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);
        return;

    case MY_RPCC:
        _xdr_argument = (xdrproc_t) xdr_my_io_data_t;
        _xdr_result = (xdrproc_t) xdr_my_io_data_t;
        local = (char *(*)(char *, struct svc_req *)) my_rpcc_2_svc;
        break;

    default:
        svcerr_noproc (transp);
        return;
    }
    memset ((char *)&argument, 0, sizeof (argument));
    if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
        svcerr_decode (transp);
        return;
    }
    result = (*local)((char *)&argument, rqstp);
    if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {
        svcerr_systemerr (transp);
    }
    if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
        fprintf (stderr, "%s", "unable to free arguments");
        exit (1);
    }
    return;
}

int
main (int argc, char **argv)
{
    register SVCXPRT *transp;

    pmap_unset (MY_RPC_PROG, MY_RPC_VERS1);
    pmap_unset (MY_RPC_PROG, MY_RPC_VERS2);

    transp = svcudp_create(RPC_ANYSOCK);
    if (transp == NULL) {
        fprintf (stderr, "%s", "cannot create udp service.");
        exit(1);
    }
    if (!svc_register(transp, MY_RPC_PROG, MY_RPC_VERS1, my_rpc_prog_1, IPPROTO_UDP)) {
        fprintf (stderr, "%s", "unable to register (MY_RPC_PROG, MY_RPC_VERS1, udp).");
        exit(1);
    }
    if (!svc_register(transp, MY_RPC_PROG, MY_RPC_VERS2, my_rpc_prog_2, IPPROTO_UDP)) {
        fprintf (stderr, "%s", "unable to register (MY_RPC_PROG, MY_RPC_VERS2, udp).");
        exit(1);
    }

    transp = svctcp_create(RPC_ANYSOCK, 0, 0);
    if (transp == NULL) {
        fprintf (stderr, "%s", "cannot create tcp service.");
        exit(1);
    }
    if (!svc_register(transp, MY_RPC_PROG, MY_RPC_VERS1, my_rpc_prog_1, IPPROTO_TCP)) {
        fprintf (stderr, "%s", "unable to register (MY_RPC_PROG, MY_RPC_VERS1, tcp).");
        exit(1);
    }
    if (!svc_register(transp, MY_RPC_PROG, MY_RPC_VERS2, my_rpc_prog_2, IPPROTO_TCP)) {
        fprintf (stderr, "%s", "unable to register (MY_RPC_PROG, MY_RPC_VERS2, tcp).");
        exit(1);
    }

    svc_run ();
    fprintf (stderr, "%s", "svc_run returned");
    exit (1);
    /* NOTREACHED */
}
View Code

my_xdr.c

/*
 * Please do not edit this file.
 * It was generated using rpcgen.
 */

#include "my.h"

bool_t
xdr_my_io_data_s (XDR *xdrs, my_io_data_s *objp)
{
    register int32_t *buf;

    int i;
     if (!xdr_int (xdrs, &objp->mtype))
         return FALSE;
     if (!xdr_int (xdrs, &objp->len))
         return FALSE;
     if (!xdr_vector (xdrs, (char *)objp->data, 1024,
        sizeof (char), (xdrproc_t) xdr_char))
         return FALSE;
    return TRUE;
}

bool_t
xdr_my_io_data_t (XDR *xdrs, my_io_data_t *objp)
{
    register int32_t *buf;

     if (!xdr_my_io_data_s (xdrs, objp))
         return FALSE;
    return TRUE;
}
View Code

 

3.rpcgen -Sc -o my_client.c my.x 生成my_client.c 使用該指令後我們就生成了客戶端.c文件,這個文件很重要,因為以後我們做業務開發就在這裡做,我們的調用都會從這裡開始。    my_client.c:
/*
 * This is sample code generated by rpcgen.
 * These are only templates and you can use them
 * as a guideline for developing your own functions.
 */

#include "my.h"


void
my_rpc_prog_1(char *host)
{
    CLIENT *clnt;
    int  *result_1;
    my_io_data_t  my_rpcc_1_arg;

#ifndef    DEBUG
    clnt = clnt_create (host, MY_RPC_PROG, MY_RPC_VERS1, "udp");
    if (clnt == NULL) {
        clnt_pcreateerror (host);
        exit (1);
    }
#endif    /* DEBUG */

    result_1 = my_rpcc_1(&my_rpcc_1_arg, clnt);
    if (result_1 == (int *) NULL) {
        clnt_perror (clnt, "call failed");
    }
#ifndef    DEBUG
    clnt_destroy (clnt);
#endif     /* DEBUG */
}


void
my_rpc_prog_2(char *host)
{
    CLIENT *clnt;
    my_io_data_t  *result_1;
    my_io_data_t  my_rpcc_2_arg;

#ifndef    DEBUG
    clnt = clnt_create (host, MY_RPC_PROG, MY_RPC_VERS2, "udp");
    if (clnt == NULL) {
        clnt_pcreateerror (host);
        exit (1);
    }
#endif    /* DEBUG */

    result_1 = my_rpcc_2(&my_rpcc_2_arg, clnt);
    if (result_1 == (my_io_data_t *) NULL) {
        clnt_perror (clnt, "call failed");
    }
#ifndef    DEBUG
    clnt_destroy (clnt);
#endif     /* DEBUG */
}


int
main (int argc, char *argv[])
{
    char *host;

    if (argc < 2) {
        printf ("usage: %s server_host\n", argv[0]);
        exit (1);
    }
    host = argv[1];
    my_rpc_prog_1 (host);
    my_rpc_prog_2 (host);
exit (0);
}
View Code 現在我們就可以在該文件編寫客戶端的代碼了。     5.rpcgen -Ss -o my_server.c my.x生成文件my_server.c 使用該指令後我們就生成了伺服器.c文件,這個文件很重要,因為以後我們做業務開發就在這裡做,我們將在這裡編寫處理客戶端請求的代碼。 my_server.c:
/*
 * This is sample code generated by rpcgen.
 * These are only templates and you can use them
 * as a guideline for developing your own functions.
 */

#include "my.h"

int *
my_rpcc_1_svc(my_io_data_t *argp, struct svc_req *rqstp)
{
    static int  result;

    /*
     * insert server code here
     */

    return &result;
}

my_io_data_t *
my_rpcc_2_svc(my_io_data_t *argp, struct svc_req *rqstp)
{
    static my_io_data_t  result;

    /*
     * insert server code here
     */

    return &result;
}
View Code

 

6.在my_server.c和my_client.c添加測試代碼   所有利用rpcgen生成的文件都已經生成完畢,接下來我們需要添加測試代碼來驗證該RPC骨架是否正常工作。 my_client.c:
/*
 * This is sample code generated by rpcgen.
 * These are only templates and you can use them
 * as a guideline for developing your own functions.
 */

#include "my.h"


void
my_rpc_prog_1(char *host)
{
    CLIENT *clnt;
    int  *result_1;
    my_io_data_t  my_rpcc_1_arg;

#ifndef    DEBUG
    clnt = clnt_create (host, MY_RPC_PROG, MY_RPC_VERS1, "udp");
    if (clnt == NULL) {
        clnt_pcreateerror (host);
        exit (1);
    }
#endif    /* DEBUG */

    result_1 = my_rpcc_1(&my_rpcc_1_arg, clnt);
    if (result_1 == (int *) NULL) {
        clnt_perror (clnt, "call failed");
    }
#ifndef    DEBUG
    clnt_destroy (clnt);
#endif     /* DEBUG */
}


void
my_rpc_prog_2(char *host)
{
    CLIENT *clnt;
    my_io_data_t  *result_1;
    my_io_data_t  my_rpcc_2_arg;

#ifndef    DEBUG
    clnt = clnt_create (host, MY_RPC_PROG, MY_RPC_VERS2, "udp");
    if (clnt == NULL) {
        clnt_pcreateerror (host);
        exit (1);
    }
#endif    /* DEBUG */
    my_rpcc_2_arg.mtype = 3;
    my_rpcc_2_arg.len = 18;
    result_1 = my_rpcc_2(&my_rpcc_2_arg, clnt);
    if (result_1 == (my_io_data_t *) NULL) {
        clnt_perror (clnt, "call failed");
    }
    fprintf(stderr,"recv msg from server! mtype:%d  len:%d \n",result_1->mtype,result_1->len); 
#ifndef    DEBUG
    clnt_destroy (clnt);
#endif     /* DEBUG */
}


int
main (int argc, char *argv[])
{
    char *host;

    if (argc < 2) {
        printf ("usage: %s server_host\n", argv[0]);
        exit (1);
    }
    host = argv[1];
    //my_rpc_prog_1 (host);
    my_rpc_prog_2 (host);
exit (0);
}
View Code

值得註意的是,我們client使用的是UDP協議,當然我們用戶也可以根據自己需要選用TCP協議進行開發。

my_server.c

/*
 * This is sample code generated by rpcgen.
 * These are only templates and you can use them
 * as a guideline for developing your own functions.
 */

#include "my.h"

int *
my_rpcc_1_svc(my_io_data_t *argp, struct svc_req *rqstp)
{
    static int  result;

    /*
     * insert server code here
     */
    return &result;
}

my_io_data_t *
my_rpcc_2_svc(my_io_data_t *argp, struct svc_req *rqstp)
{
    static my_io_data_t  result;

    /*
     * insert server code here
     */
    printf("recv msg from client! len:%d, mt:%d \n",argp->len,argp->mtype);
    result.mtype = 33;
    result.len = 12;
    return &result;
}
View Code   7.測試現象 編譯這client和server   gcc -o client my_clnt.c my_client.c my_xdr.c gcc -o server my_svc.c my_server.c my_xdr.c   我在主機172.0.5.183運行server程式,在172.0.5.183運行client程式,測試現象如下:   server端

client端

以上測試已經證明瞭我們創建的RPC是可以正常通信的,那我們繼續在此框架下完善代碼,構建出可供業務開發的分散式系統的系統框架。     二、完善分散式系統的系統框架   以上的通信骨架過於簡單了,那我們就動動手加點代碼,設計一個簡單的分散式計算系統。這個所謂的分散式計算系統很簡單,就是我們一個客戶端向一個伺服器端提出計算請求,伺服器端將計算得出的結果返回給客戶端。   本次程式是基於本文第一部分的骨架搭建的,大多數文件不需要修改,需要修改的文件如下:
my_client、my_server.c、my_clnt.c、my.h 新增加一個文件:rpc_msg.h

首先我們重新定義一下客戶端和伺服器端通信消息的格式。
typedef struct my_msg_hdr_s
{
    int mtype;
    int len;    
}my_msg_hdr_t;

typedef struct my_msg_s
{
    my_msg_hdr_t msg_hdr;
    int para1;
    int para2;
    int result;
}my_msg_t;
然後我們先改變一下我們的客戶端程式, my_rpc_prog_1主要負責伺服器的設置,比如在該程式里,此函數就用於設置伺服器的開關,所以這個函數只需要把消息發給伺服器端就可以了,不需要伺服器返回數據給它,所以使用了int的返回值。   而my_rpc_prog_2則需要給它返回數據結果的,所以該函數使用了my_io_data_t *作返回值。
my_client.c
/*
 * This is sample code generated by rpcgen.
 * These are only templates and you can use them
 * as a guideline for developing your own functions.
 */

#include "my.h"
#include "rpc_msg.h"


int my_rpc_prog_1(char *host, my_io_data_t* in_msg)
{
    CLIENT *clnt;
    int  *result_1;

#ifndef    DEBUG
    clnt = clnt_create (host, MY_RPC_PROG, MY_RPC_VERS1, "udp");
    if (clnt == NULL) 
    {
        printf("Fail to create rpc client1!\n");
        return -1;
    }
#endif    /* DEBUG */

    result_1 = my_rpcc_1(in_msg, clnt);
    if (result_1 == (int *) NULL) 
    {
        clnt_perror (clnt, "call failed");
        return -1;
    }

    return 0;

}


my_io_data_t * my_rpc_prog_2(char *host, my_io_data_t* in_msg)
{
    CLIENT *clnt;
    my_io_data_t  *result_1 = NULL;

#ifndef    DEBUG
    clnt = clnt_create (host, MY_RPC_PROG, MY_RPC_VERS2, "udp");
    if (clnt == NULL) 
    {
        printf("Fail to create rpc client1!\n");
        return NULL;
    }
#endif    /* DEBUG */

    result_1 = my_rpcc_2(in_msg, clnt);
    if (result_1 == (my_io_data_t *) NULL) 
    {
        clnt_perror (clnt, "call failed");
        return NULL;
    }

    return result_1;

}

void get_compute_result(char *host, int type, int para1, int para2)
{
    my_io_data_t in_msg;
    my_msg_t* rsp;
    my_io_data_t* out_msg;
    my_msg_t* req = (my_msg_t*)&in_msg;


    memset(&in_msg, 0, sizeof(in_msg));
    req->msg_hdr.mtype = type;
    req->msg_hdr.len = sizeof(in_msg) - sizeof(my_msg_hdr_t);
    req->para1 = para1;
    req->para2 = para2;

    out_msg = my_rpc_prog_2(host, &in_msg);

    rsp = (my_msg_t*)out_msg;

    if(rsp == NULL)
    {
        printf("RPC call fail!\n");
        return;
    }

    printf("compute result is %d\n",rsp->result);

}

void server_switch(char *host, int type)
{
    my_io_data_t msg;
    my_msg_t* in_msg = (my_msg_t*)&msg;
    memset(&msg, 0, sizeof(msg));
    in_msg->msg_hdr.mtype = type;
    in_msg->msg_hdr.len = sizeof(msg) - sizeof(my_msg_hdr_t);

    if(my_rpc_prog_1(host, &msg))
    {
        printf("enable server fail!\n");
    }
    printf("Configure server successfully!\n");
}

int main (int argc, char *argv[])
{
    server_switch(SERVER_IP, RPC_enable); //server start
    sleep(1);
    get_compute_result(SERVER_IP, RPC_ADD, 6, 3);
    sleep(1);
    get_compute_result(SERVER_IP, RPC_SUB, 6, 3);
    sleep(1);
    get_compute_result(SERVER_IP, RPC_MUL, 6, 3);
    sleep(1);
    get_compute_result(SERVER_IP, RPC_DIV, 6, 3);
    sleep(1);
    server_switch(SERVER_IP, RPC_disable); //server close
    return 0;
}
當然改了以上的兩個函數,我們當然也需要在my_clnt.c和my.h作出函數聲明的修改。   現在看看server端怎麼處理這些請求的。   my_server.c
/*
 * This is sample code generated by rpcgen.
 * These are only templates and you can use them
 * as a guideline for developing your own functions.
 */

#include "my.h"
#include "rpc_msg.h"

int* my_rpcc_1_svc(my_io_data_t *argp, struct svc_req *rqstp)
{
    static int  result;

    switch(argp->mtype)
    {
        case RPC_enable:
        printf("server start!\n");
        break;

        case RPC_disable:
        printf("server close!\n");
        break;

        default:
        break;
    }

    return &result;
}

my_io_data_t* my_rpcc_2_svc(my_io_data_t *argp, struct svc_req *rqstp)
{
    static my_io_data_t  result;
    my_msg_t* out = (my_msg_t*)&result;
    my_msg_t* in = (my_msg_t*)argp;

    switch(in->msg_hdr.mtype)
    {
        case RPC_ADD:
        out->result = in->para1 + in->para2;
        break;

        case RPC_SUB:
        out->result = in->para1 - in->para2;
        break;

        case RPC_MUL:
        out->result = in->para1 * in->para2;
        break;

        case RPC_DIV:
        out->result = in->para1/in->para2;
        break;

        default:
        break;
    }


    return &result;
}

 

附上有修改的文件 rpc_msg.h
#define SERVER_IP "172.0.5.183"


enum RPC_REQ_TYPE_1
{
    RPC_enable,
    RPC_disable,
};



enum RPC_REQ_TYPE_2
{
    RPC_ADD,
    RPC_SUB,
    RPC_MUL,
    RPC_DIV,
};


typedef struct my_msg_hdr_s
{
    int mtype;
    int len;    
}my_msg_hdr_t;

typedef struct my_msg_s
{
    my_msg_hdr

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 第十二節 linux三劍客之sed命令精講 標簽(空格分隔): Linux實戰教學筆記 陳思齊 [更多資料點我查看][1] 1,前言 我們都知道,在Linux中一切皆文件,比如配置文件,日誌文件,啟動文件等等。如果我們相對這些文件進行一些編輯查詢等操作時,我們可能會想到一些vi,vim,cat,mo ...
  • 僅供參考…… 測試使用環境:Tplink & Tenda渣渣路由器。其他環境或不同。 設置註意事項:副路由器網段設置和主路由一致。主路由不需要開啟WDS、副路由器開啟WDS(連接ok,狀態即顯示"成功")、主副路由通道需要保持一致(手動設置)、DHCP服務開啟(設置網關) IP地址池設置不妥時,一臺 ...
  • 打開終端,輸入su root,進入root賬戶 在/boot/grub2/下麵有個grub.cfg文件 在命令行輸入vim /boot/grub2/grub.cfg 在### BEGIN /etc/grub.d/10_linux ### 後面加上一段 menuentry 'Windows7'{ in ...
  • Linux系統信息存放在文件里,文件與普通的公務文件類似。每個文件都有自己的名字、內容、存放地址及其它一些管理信息,如文件的用戶、文件的大小等。文件可以是一封信、一個通訊錄,或者是程式的源語句、程式的數據,甚至可以包括可執行的程式和其它非正文內容。Linux文件系統具有良好的結構,系統提供了很多文件 ...
  • 用戶訪問示意圖: ...
  • 實例1:每1分鐘執行一次myCommand * * * * * myCommand 實例2:每小時的第3和第15分鐘執行 3,15 * * * * myCommand 實例3:在上午8點到11點的第3和第15分鐘執行 3,15 8-11 * * * myCommand 實例4:每隔兩天的上午8點到1 ...
  • 上篇我們學習了shell中條件選擇語句的用法。接下來本篇就來學習迴圈語句。在shell中,迴圈是通過for, while, until命令來實現的。下麵就分別來看看吧。 for for迴圈有兩種形式: for in語句 基本格式如下: for var in list do commands done ...
  • 1.安裝chocolatey打開cmd.exe執行@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://choco ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...