RPC2--RPC实例:字典(dictionary)

本文从RPC的基础知识出发,结合一个在单机上运行的字典程序,完成一个在Linux环境下运行的客户端服务器程序。

参考博客:

1http://blog.csdn.net/shark863/article/details/2107356

2http://zhoulifa.bokee.com/6128714.html

本文中涉及的所有程序都在实际机器上执行通过。


目录

1---单机上的.c源文件代码

2--单机源文件运行截图

3--rpc文件编辑过程

4--rpc文件编辑运行过程的总结

5--rpc客户机服务器运行截图

1单机上的.c源文件代码如下:

/* dict.c -- main, initw, nextin, insertw, deletew, lookupw */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXWORD 50        /* maximum length of a command or word */
#define DICTSIZ 100        /* maximum number of entries in dictionary. */
char dict[DICTSIZ][MAXWORD + 1];    /* storage for a dictionary of words */
int nwords = 0;            /* number of words in the dictionary */
/* 函数原型 */
int nextin(char *cmd, char *word);
int initw(void);
int insertw(const char *word);
int deletew(const char *word);
int lookupw(const char *word);
/* ------------------------------------------------------------------
* main -- insert, delete, or lookup words in a dictionary as specified
* ------------------------------------------------------------------ */
int main(int argc, char *argv[])
{
    char word[MAXWORD + 1];    /* space to hold word from input line */
    char cmd;
    int wordlen;        /* length of input word */
 printf("I----initialized\ni----insert\nd----delete\nl----lookup\nq----quits\n");
    printf("Please input after ***\n");
 printf("***");
    while (1) {
    wordlen = nextin(&cmd, word);
    if (wordlen < 0) {
        exit(0);
    }
    switch (cmd) {
    case 'I':        /* 初始化 */
        initw();
        printf("Dictionary initialized to empty.\n");
        break;
    case 'i':        /* 插入 */
        insertw(word);
        printf("%s inserted.\n", word);
        break;
    case 'd':        /* 删除 */
        if (deletew(word)) {
        printf("%s deleted.\n", word);
        } else {
        printf("%s not found.\n", word);
        }
        break;
    case 'l':        /* 查询 */
        if (lookupw(word)) {
        printf("%s was found.\n", word);
        } else {
        printf("%s was not found.\n", word);
        }
        break;
    case 'q':        /* 退出 */
        printf("Program quits.\n");
        exit(0);
        break;
    default:        /* 非法输入 */
        printf("command %c invalid.\n", cmd);
        break;
    }            /* end of switch */
 printf("***");
    }                /* end of while */
    return 0;
}                /* end of main */

/* ------------------------------------------------------------------
* nextin -- read a command and(possibly) a word from the next input line
* ------------------------------------------------------------------ */
int nextin(char *cmd, char *word)
{
    int i,ch;
    ch = getc(stdin);
    while (isspace(ch)) {
    ch = getc(stdin);
    }                /* end of while */
    if (ch == EOF) {
    return (-1);
    }
    *cmd = (char) ch;
 if(*cmd=='I' || *cmd=='q'){
  return 0;
 }
 if(*cmd!='i' && *cmd!='d' && *cmd!='l'){
     return 0;
 }
    ch = getc(stdin);
    while (isspace(ch)) {
    ch = getc(stdin);
    }                /* end of while */
    if (ch == EOF) {
    return (-1);
    }
    if (ch == '\n') {
    return (0);
    }
    i = 0;
    while (!isspace(ch)) {
    if (++i > MAXWORD) {
        printf("error: word too long.\n");
        exit(1);
    }
    *(word+i-1)= ch;
    ch = getc(stdin);
    }                /* end of while */
    *(word+i) = '\0';        /* 原来的代码这里有问题 */
    return i;
}                /* end of nextin */

/* ------------------------------------------------------------------
* initw -- initialize the dictionary to contain no words at all
* ------------------------------------------------------------------ */
int initw(void)
{
    nwords = 0;
    return 1;
}                /* end of initw */

/* ------------------------------------------------------------------
* insertw -- insert a word in the dictionary
* ------------------------------------------------------------------ */
int insertw(const char *word)
{
    strcpy(dict[nwords], word);
    nwords++;
    return (nwords);
}                /* end of insertw */

/* ------------------------------------------------------------------
* deletew -- delete a word from the dictionary
* ------------------------------------------------------------------ */
int deletew(const char *word)
{
    int i;
    for (i = 0; i < nwords; i++) {
    if (strcmp(word, dict[i]) == 0) {
        nwords--;
        strcpy(dict[i], dict[nwords]);
        return (1);
    }
    }                /* end of for */
    return (0);
}                /* end of deletew */

/* ------------------------------------------------------------------
* lookupw -- look up a word in the dictionary
* ------------------------------------------------------------------ */

int lookupw(const char *word)
{
    int i;
    for (i = 0; i < nwords; i++) {
    if (strcmp(word, dict[i]) == 0) {
        return (1);
    }
    }              
    return (0);
}              

2单机运行截图

3RPC文件编辑过程

1)编写.x文件,并保存为*.x。本文中,为dict.x文件。

/*  dict.x  */
/* RPC declaration for dictionary program */
const MAXWORD = 50;
const DICTSIZ = 100;
program DICTPROG{
    version DICTVERS{
        int INITW( void ) = 1;
 int INSERTW(string) = 2;
 int DELETEW(string) = 3;
 int LOOKUPW(string) =4;
    }=1;
}=11;

2)使用命令#rpcgen dict.x产生三个文件。分别为dict.h、dict_clnt.c、dict_svc.c。

dict.h中保存了一些头文件的申明、dict.x文件中定义的产量等

dict_svc.c中包含了服务器端的main()函数。主要功能是针对不同的远程调用函数名,实现对服务器端执行的函数名,参数类型,返回值类型的封装,已实现针对不同的远程调用,使用统一的服务器端调用格式。

dict_clnt.c中对客户端可能调用的每一个函数的封装,将客户端对远程进程调用以统一的格式发送至服务器。封装的内容包括:客户端信息、调用函数名、调用函数参数类型、调用函数返回值类型。

3)使用#rpcgen -Ss dict_srv_func.c dict.x命令产生服务器端编辑源程序文件。

     使用#rpcgen -Sc dict_client.c dict.x命令产生客户端编辑源程序文件。

对dict_srv_func.c 和 dict_client.c 文件进行修改,完成所需功能。

本实例的dict_srv_func.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 "dict.h"

char dict[DICTSIZ][MAXWORD + 1];    /* storage for a dictionary of words */
int nwords = 0;            /* number of words in the dictionary */
int initbool =0;

/* ------------------------------------------------------------------
* initw -- initialize the dictionary to contain no words at all
* ------------------------------------------------------------------ */
int initw(void)
{
 if(initbool==1)
  return 1;
    nwords = 0;
 initbool=1;
    return 0;
}                /* end of initw */

/* ------------------------------------------------------------------
* insertw -- insert a word in the dictionary
* ------------------------------------------------------------------ */
int insertw(const char *word)
{
//    printf("insertw1:in server\n");
    strcpy(dict[nwords], word);
//    printf("insertw2:in server\n");
    nwords++;
    return (nwords);
}                /* end of insertw */

/* ------------------------------------------------------------------
* deletew -- delete a word from the dictionary
* ------------------------------------------------------------------ */
int deletew(const char *word)
{
    int i;
    for (i = 0; i < nwords; i++) {
    if (strcmp(word, dict[i]) == 0) {
        nwords--;
        strcpy(dict[i], dict[nwords]);
        return (1);
    }
    }                /* end of for */
    return (0);
}                /* end of deletew */

/* ------------------------------------------------------------------
* lookupw -- look up a word in the dictionary
* ------------------------------------------------------------------ */

int lookupw(const char *word)
{
    int i;
    for (i = 0; i < nwords; i++) {
    if (strcmp(word, dict[i]) == 0) {
        return (1);
    }
    }              
    return (0);
}


int *
initw_1_svc(void *argp, struct svc_req *rqstp)
{
 static int  result;

 /*
  * insert server code here
  */
    result = initw();
 
 return &result;
}

int *
insertw_1_svc(char **argp, struct svc_req *rqstp)
{
 static int  result;

 /*
  * insert server code here
  */
// printf("before:in insertw_1_svc\n");
 result = insertw(*argp);
// printf("after:in insertw_1_svc\n");
 
 return &result;
}

int *
deletew_1_svc(char **argp, struct svc_req *rqstp)
{
 static int  result;

 /*
  * insert server code here
  */
 result = deletew(*argp);
 
 return &result;
}

int *
lookupw_1_svc(char **argp, struct svc_req *rqstp)
{
 static int  result;

 /*
  * insert server code here
  */
 result = lookupw(*argp);
 
 return &result;
}
本实例dict_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 "dict.h"

int nextin(char *cmd, char *word)
{
    int i,ch;
    ch = getc(stdin);
    while (isspace(ch)) {
    ch = getc(stdin);
    }                /* end of while */
    if (ch == EOF) {
    return (-1);
    }
    *cmd = (char) ch;
 if(*cmd=='I' || *cmd=='q'){
  return 0;
 }
 if(*cmd!='i' && *cmd!='d' && *cmd!='l'){
     return 0;
 }
    ch = getc(stdin);
    while (isspace(ch)) {
    ch = getc(stdin);
    }                /* end of while */
    if (ch == EOF) {
    return (-1);
    }
    if (ch == '\n') {
    return (0);
    }
    i = 0;
    while (!isspace(ch)) {
    if (++i > MAXWORD) {
        printf("error: word too long.\n");
        exit(1);
    }
    *(word+i-1)= ch;
    ch = getc(stdin);
    }                /* end of while */
    *(word+i) = '\0';      
    return i;
}                /* end of nextin */


void
dictprog_1(char *host)
{
 CLIENT *clnt;
 int  *result_1;
 char *initw_1_arg;
 int  *result_2;
 char * insertw_1_arg;
 int  *result_3;
 char * deletew_1_arg;
 int  *result_4;
 char * lookupw_1_arg;

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

 
char word[MAXWORD + 1];    /* space to hold word from input line */
char cmd;
int wordlen;  /* length of input word */
printf("I----initialized\ni----insert\nd----delete\nl----lookup\nq----quits\n");
printf("Please input after ***\n");
printf("***");
while (1) {
wordlen = nextin(&cmd, word);
if (wordlen < 0) {
 exit(0);
}
switch (cmd) {
case 'I':   /* 初始化 */
 result_1=initw_1((void *)initw_1_arg,clnt);
 if(result_1==(int *)NULL)
  clnt_perror(clnt,"call failed");
 else if(*result_1==1)
  printf("Dictionary have already initialized.\n");
 else
  printf("Dictionary initialized to empty.\n");
 break;
case 'i':   /* 插入 */
        insertw_1_arg = word;
 result_2=insertw_1(&insertw_1_arg,clnt);
 if(result_2==(int *)NULL)
  clnt_perror(clnt,"call failed");
 else
  printf("%s inserted.\n", word);
 break;
case 'd':   /* 删除 */
        deletew_1_arg = word;
 result_3=deletew_1(&deletew_1_arg,clnt);
 if(result_3==(int *)NULL)
  clnt_perror(clnt,"call failed");
 else if(*result_3==1) {
 printf("%s deleted.\n", word);
 } else if(*result_3==0){
 printf("%s not found.\n", word);
 }
 break;
case 'l':   /* 查询 */
        lookupw_1_arg = word;
 result_4 = lookupw_1(&lookupw_1_arg,clnt);
 if(result_4==(int *)NULL)
  clnt_perror(clnt,"call failed");
 else if(*result_4==1) {
 printf("%s was found.\n", word);
 } else if(*result_4==0){
 printf("%s was not found.\n", word);
 }
 break;
case 'q':   /* 退出 */
 printf("Program quits.\n");
 exit(0);
 break;
default:  /* 非法输入 */
 printf("command %c invalid.\n", cmd);
 break;
}    /* end of switch */
printf("***");
}     /* end of while */

#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];
 dictprog_1 (host);
exit (0);
}
4)使用#rpcgen -Sm dict.x > makefile 命令产生makefile文件

对makefile文件做如下修改:将dict_srv_func.c加入到TARGETS_SVC.c中,将dict_client.c加入到TARGETS_CLNT.c中。

在clean末尾处加入“*~”,与前面内容使用空格隔开。

本实例中makefile文件内容如下所示:

# This is a template Makefile generated by rpcgen

# Parameters

CLIENT = dict_client
SERVER = dict_server

SOURCES_CLNT.c =
SOURCES_CLNT.h =
SOURCES_SVC.c =
SOURCES_SVC.h =
SOURCES.x = dict.x

TARGETS_SVC.c = dict_svc.c  dict_srv_func.c  
TARGETS_CLNT.c = dict_clnt.c  dict_client.c
TARGETS = dict.h   dict_clnt.c dict_svc.c   

OBJECTS_CLNT = $(SOURCES_CLNT.c:%.c=%.o) $(TARGETS_CLNT.c:%.c=%.o)
OBJECTS_SVC = $(SOURCES_SVC.c:%.c=%.o) $(TARGETS_SVC.c:%.c=%.o)
# Compiler flags

CFLAGS += -g
LDLIBS += -lnsl
RPCGENFLAGS =

# Targets

all : $(CLIENT) $(SERVER)

$(TARGETS) : $(SOURCES.x)
 rpcgen $(RPCGENFLAGS) $(SOURCES.x)

$(OBJECTS_CLNT) : $(SOURCES_CLNT.c) $(SOURCES_CLNT.h) $(TARGETS_CLNT.c)

$(OBJECTS_SVC) : $(SOURCES_SVC.c) $(SOURCES_SVC.h) $(TARGETS_SVC.c)

$(CLIENT) : $(OBJECTS_CLNT)
 $(LINK.c) -o $(CLIENT) $(OBJECTS_CLNT) $(LDLIBS)

$(SERVER) : $(OBJECTS_SVC)
 $(LINK.c) -o $(SERVER) $(OBJECTS_SVC) $(LDLIBS)

 clean:
  $(RM) core $(TARGETS) $(OBJECTS_CLNT) $(OBJECTS_SVC) $(CLIENT) $(SERVER) *~
5)使用make命令产生客户端可执行文件:dict_client,产生服务器端可执行文件:dict_server。

在服务器端执行命令(必须以root身份):./dict_server

在客户端执行命令:./dict_client 127.0.0.1

也可以有多个客户端,一个服务器端,字典的初始化只会再所以终端中执行一次。

6)清除产生的过程文件和最终的可执行文件,使用命令make clean。

4rpc编码过程中的总结

1)makefile的程序调试过程:1.make clean删除产生的可执行文件和中间文件;2.rpcgen dict.x 产生两个过程文件;3.对源代码进行修改;

4make,重新生成可执行文件;5产生修改结果

2)可直接使用string类型,在rpc自动生成过程中,会自动转化为char *类型。

3)rpc涉及的几个函数功能介绍:

svc_getargs:作用(在服务器端解码rpc远程调用过程中的参数);参数(rpc建立的连接、参数的类型、服务器端的对应存储空间)

svcerr_decode:作用(给出不能解码出远程调用过程中参数的文字提示)

4)对dict_client.c中进行的修改:

a)将对远程调用函数的参数赋值。若函数名为*,则将该函数的参数赋值给*_version_arg,否则将发生segmentation fault。

b)在客户端,对远程函数*的调用,变成对"*-version"的调用。

c)远程调用函数的参数只能有一个,在对远程函数进行调用的过程中的参数为:(&*_version_arg,clnt),即使用的是原来参数的地址和clnt。

d)远程调用的返回值是本地函数返回值的地址,即要对远程调用的返回值进行取值,才能得到想要的返回值。

e)对每个远程调用函数的返回值,都要进行一次NULL判断,(void *)NULL要进行指针转化,因为返回值本身是一个指针。若为空,

则clnt_perror(clnt,"call failed")。

5)对dict_srv_func.c中进行的修改:

a)将对应的变量及函数体写入文件中。

b)在*_svc(远程调用函数对应的服务器端函数)中添加语句:result = *(*argp)(其中*为函数名称)。

6)client端指向期待server端返回的地址空间,要在调用之前被预先分配,否知出现segmentation fault,server端要返回给client端的

内存空间要自行分配和释放。(来自博客,未来验证)

7)关于segmentation fault(core dumped)。可以在segmentation fault之后,使用gdb和生成的core文件,得到发送段错误的具体位置。

要生成core文件,必须对终端进行一定的设置,且对每个终端的设置是彼此独立的,只有对发生段错误的终端进行设置,才能在运行程序

的当前目录产生core文件。

具体设置如下

#ulimit -a //查看ulimit状态

#ulimit -c unlimited //设置为无限大

#ulimit unlimited //设置为无限大

gdb [exec file] [core file]

在gdb中使用where查看错误发生的地点

5rpc客户机服务器运行截图

1服务器端运行截图


2客户端1运行截图

3客户端3运行截图




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值