RPC原理,应用开发

这学期学习中间件,其实我不知道什么是中间件,于是就学习了。

定义:

中间件(Middleware)是一种软件,处于系统软件(操作系统和网络软件)与应用软件之间,它能使应用软件之间进行跨网络的协同工作(也就是互操作)

 

第一次实验真正呢个接触到了RPC,,但是,它到底是什么东西呢?

RPC:远程过程调用协议(RPC:Remote Procedure Call protocol)

远程过程调用(RPC)是一种从远程计算机程式上请求一个服务器,而不必了解上层网络技术的协议。RPC 协议假定某些传输协议的存在,如 TCP 或 UDP,使得通信程式之间能传输信息数据。在 ISO 网络通信模式中 RPC 跨越了传输层和应用层。RPC 使得生成应用程式包括分布式复用程式更加容易。

 

查了一些资料,但还是不知道它是什么东西,但我觉得它不是一个实实在在的软件,于是就继续查资料。还发现了一个我又不知道的东西:rpcgen,原来它是一个代码生成工具。使用它可以方便rpc编程。原来rpcgen不是去下载的,它是在linux下安装的,通过命令来安装!装好后就通过rpcgen自己的命令来使用它,类似于gcc编译器吧。

 

根据我自己的理解,我总结了下:RPC编程就是先写出一个程序,这个程序有很多个方法来组成,我们可以把这个程序分割成许多程序片,这些程序片放在不同的计算机上,那么,当要运行该程序时,只需运行主程序片,该主程序片会去请求调用其他机器上的程序片(相当于客户端请求服务端),并返回程序片执行的结果。这就是所谓的分布式计算。这样做的好处就是可以节省本地计算机的空间,利用别的计算机资源,但是我觉得这样字也有不好的地方,那就是调用返回的速度问题,会不会产生阻塞?我猜像腾讯这样的公司,它应该就是利用这个分布式原理来提供远程服务的,要不然那么大的用户群,哪有那么万能的服务器啊!

以下是百度上关于RPC的工作原理:

工作原理

  运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:

  1.调用客户端句柄;执行传送参数

  2.调用本地系统内核发送网络消息

  3.消息传送到远程主机

  4.服务器句柄得到消息并取得参数

  5.执行远程过程

  6.执行的过程将结果返回服务器句柄

  7.服务器句柄返回结果,调用远程系统内核

  8.消息传回本地主机

  9.客户句柄由内核接收消息

  10.客户接收句柄返回的数据

发现一篇好文章:http://blog.csdn.net/hitxiaotao/archive/2008/04/09/2267523.aspx

还发现了以下一片好文章,于是就转载过来了。此篇文章教会我们如何在linux下进行RPC编程。

以下文字引用自http://www.yuanma.org/data/2006/0918/article_1560.htm
一、概述

在传统的编程概念中,过程是由程序员在本地编译完成,并只能局限在本地运行的一段代码,也即其主程序和过程之间的运行关系是本地调用关系。因此这种结构在网络日益发展的今天已无法适应实际需求。总而言之,传统过程调用模式无法充分利用网络上其他主机的资源(如CPU、 Memory等),也无法提高代码在实体间的共享程度,使得主机资源大量浪费。
而本文要介绍的RPC编程,正是很好地解决了传统过程所存在的一系列弊端。通过RPC我们可以充分利用非共享内存的多处理器环境(例如通过局域网连接的多台工作站),这样可以简便地将你的应用分布在多台工作站上,应用程序就像运行在一个多处理器的计算机上一样。你可以方便的实现过程代码共享,提高系统资源的利用率,也可以将以大量数值处理的操作放在处理能力较强的系统上运行,从而减轻前端机的负担。
二、RPC的结构原理及其调用机制
如前所述RPC其实也是一种C/S的编程模式,有点类似C/S Socket 编程模式,但要比它更高一层。当我们在建立RPC服务以后,客户端的调用参数通过底层的RPC传输通道,可以是UDP,也可以是TCP(也即TI-RPC —无关性传输),并根据传输前所提供的目的地址及RPC上层应用程序号转至相应的RPC Application Porgramme Server ,且此时的客户端处于等待状态,直至收到应答或Time Out超时信号。具体的流程图如图1。当服务器端获得了请求消息,则会根据注册RPC时告诉RPC系统的例程入口地址,执行相应的操作,并将结果返回至客户端。
当一次RPC调用结束后,相应线程发送相应的信号,客户端程序才会继续运行。
当然,一台服务主机上可以有多个远程过程提供服务,那么如何来表示一个唯一存在的远程过程呢?一个远程过程是有三个要素来唯一确定的:程序号、版本号和过程号。程序号是用来区别一组相关的并且具有唯一过程号的远程过程。一个程序可以有一个或几个不同的版本,而每个版本的程序都包含一系列能被远程调用的过程,通过版本的引入,使得不同版本下的RPC能同时提供服务。每个版本都包含有许多可供远程调用的过程,每个过程则有其唯一标示的过程号。
三、基于RPC的应用系统开发
通过以上对RPC原理的简介后,我们再来继续讨论如何来开发基于RPC的应用系统。一般而言在开发RPC时,我们通常分为三个步骤:
a、定义说明客户/服务器的通信协议。
这里所说的通信协议是指定义服务过程的名称、调用参数的数据类型和返回参数的数据类型,还包括底层传输类型(可以是 UDP或TCP),当然也可以由RPC底层函数自动选择连接类型建立TI-RPC。最简单的协议生成的方法是采用协议编译工具,常用的有Rpcgen,我会在后面实例中详细描述其使用方法。
b、开发客户端程序。
c、开发服务器端程序。
开发客户端和服务器端的程序时,RPC提供了我们不同层次的开发例程调用接口。不同层次的接口提供了对RPC不同程度控制。一般可分为5个等级的编程接口,接下来我们分别讨论一下各层所提供的功能函数。
1、简单层例程
简单层是面向普通RPC应用,为了快速开发RPC应用服务而设计的,他提供了如下功能函数。

函数名

功能描述

Rpc_reg( )

在一特定类型的传输层上注册某个过程,来作为提供服务的RPC程序

Rpc_call( )

远程调用在指定主机上指定的过程

Rpc_Broadcast( )

向指定类型的所有传输端口上广播一个远程过程调用请求

2、高层例程
在这一层,程序需要在发出调用请求前先创建一个客户端句柄,或是在侦听请求前先建立一个服务器端句柄。程序在该层可以自由的将自己的应用绑在所有的传输端口上,它提供了如下功能函数。

函数名

功能描述

Clnt_create( )

程序通过这个功能调用,告诉底层RPC服务器的位置及其传输类型

Clnt_create_timed( )

定义每次尝试连接的超时最大时间

Svc_create( )

在指定类型的传输端口上建立服务器句柄,告诉底层RPC事件过程的相应入口地址

Clnt_call()

向服务器端发出一个RPC调用请求

3、中间层例程
中间层向程序提供更为详细的RPC控制接口,而这一层的代码变得更为复杂,但运行也更为有效,它提供了如下功能函数。

函数名

功能描述

Clnt_tp_create( )

在指定的传输端口上建立客户端句柄

Clnt_tp_create_timed( )

定义最大传输时延

Svc_tp_creaet( )

在指定的传输端口上建立服务句柄

Clnt_call( )

向服务器端发出RPC调用请求

4、专家层例程
这层提供了更多的一系列与传输相关的功能调用,它提供了如下功能函数。

函数名

功能描述

Clnt_tli_create( )

在指定的传输端口上建立客户端句柄

Svc_tli_create( )

在指定的传输端口上建立服务句柄

Rpcb_set( )

通过调用rpcbind将RPC服务和网络地址做映射

Rpcb_unset( )

删除rpcb_set( ) 所建的映射关系

Rpcb_getaddr( )

调用rpcbind来犯会指定RPC服务所对应的传输地址

Svc_reg( )

将指定的程序和版本号与相应的时间例程建起关联

Svc_ureg( )

删除有svc_reg( ) 所建的关联

Clnt_call( )

客户端向指定的服务器端发起RPC请求

5、底层例程
该层提供了所有对传输选项进行控制的调用接口,它提供了如下功能函数。

函数名

功能描述

Clnt_dg_create( )

采用无连接方式向远程过程在客户端建立客户句柄

Svc_dg_create( )

采用无连接方式建立服务句柄

Clnt_vc_create( )

采用面向连接的方式建立客户句柄

Svc_vc_create( )

采用面向连接的方式建立RPC服务句柄

Clnt_call( )

客户端向服务器端发送调用请求

............引用文字未尽,详细请参见 http://www.yuanma.org/data/2006/0918/article_1560.htm

关于“RPC语言”
RPC语言也是一种专门的编程语言,当然这里我们不需要知道太多,只需要能看懂下面这种基本结构就行了:
program TESTPROG {
   version VERSION {
     string TEST(string) = 1;
   } = 1;
} = 87654321;
这里TESTPROG和VERSION是两个变量,用于标识一个单独的RPC接口。这被RPC服务程序,比如portmap用到,我们可以不用关心,变量名字也是随便取的。但取值要在你的系统中是唯一的。
“string TEST(string) = 1;”这一行说明有两个函数test_VERSION和test_VERSION_svc,这里由于VERSION变量为1,所以函数名为test_1和test_1_svc,这两个函数用于在服务器端和客户端实现调用,即:
在客户端调用test_1函数,服务器端调用test_1_svc函数处理并返回。
函数的类型是string,RPC语言中string即C里面的一个字符串。所以上述函数有一个字符串作为参数传递,同时要返回字符串。即:
char ** test_1(char **argp, CLIENT *clnt) 和 char **test_1_svc(char **argp, struct svc_req *rqstp)

同理,如果声明是这样的:
program RDICTPROG 
{
    version RDICTVERS 
    {
        int INITW ( void )     = 1; 
        int INSERTW ( string ) = 2; 
        int DELETEW ( string ) = 3; 
        int LOOKUPW ( string ) = 4; 
    } = 1; 
} = 0x30090949; 
则说明这个RPC中有四个函数可用,即客户端可以调用initw_1、insertw_1、deletew_1、lookupw_1四个函数来向服务端发送消息,服务端可以用initw_1_svc、insertw_1_svc、deletew_1_svc、lookupw_1_svc四个函数来处理请求并返回结果。

原任务
假设现在有这样一个程序,源代码如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXWORD 50       
#define DICTSIZ 100       
char dict[DICTSIZ][MAXWORD + 1];   
int nwords = 0;           

int nextin(char *cmd, char *word);
int initw(void);
int insertw(const char *word);
int deletew(const char *word);
int lookupw(const char *word);

int main(int argc, char *argv[])
{
    char word[MAXWORD + 1];   
    char cmd;
    int wordlen;       
    printf("Please input:\n");
    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;
             
                 
    return 0;
              


int nextin(char *cmd, char *word)
{
    int i, ch;
    ch = getc(stdin);
    while (isspace(ch)) {
    ch = getc(stdin);
                 
    if (ch == EOF) {
    return (-1);
    }
    *cmd = (char) ch;
    ch = getc(stdin);
    while (isspace(ch)) {
    ch = getc(stdin);
                 
    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++ = ch;
    ch = getc(stdin);
                 
    *word = '\0';       
    return i;
              


int initw(void)
{
    nwords = 0;
    return 1;
              


int insertw(const char *word)
{
    strcpy(dict[nwords], word);
    nwords++;
    return (nwords);
              


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);
    }
                 
    return (0);
              


int lookupw(const char *word)
{
    int i;
    for (i = 0; i < nwords; i++) {
    if (strcmp(word, dict[i]) == 0) {
        return (1);
    }
                 
    return (0);
              
这是一个简单的字典程序,即程序运行起来以后维护着一个字典库,用户可以向里面添加词语,也可以查询或删除词语。
当然,这个程序只能在同一台主机上运行。程序整个运行过程中,只需要完成如下几个步骤:
A、接受用户输入;
B、分析用户输入决定是否进行下面的步骤:
      1、初始化数据库;
      2、向数据库添加词语;
      3、查询或删除词语

任务分解
大家可以想到,对于一个大型系统,比如需要有很多人维护这个系统的数据。象上面这样独立的程序就不适用了,需要做成分布式系统:
即一个服务器维护着数据库,任何客户端都可以接受用户请求,客户端分析用户命令后提交给服务器去处理。
所以我们可能会把程序分成两部分:
客户端:接受用户输入,并判断用户输入内容的正确性,向服务器提交数据,等服务器返回消息
服务器端:维护数据,接受客户端命令并执行后返回结果。
所以我们把上面这个程序分解成下面两部分:

#include <stdio.h>
#include <stdlib.h>
#define MAXWORD 50       

int main(int argc, char *argv[])
{
    char word[MAXWORD + 1];   
    char cmd;
    int wordlen;       
    printf("Please input:\n");
    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;
             
                 
    return 0;
              


int nextin(char *cmd, char *word)
{
    int i, ch;
    ch = getc(stdin);
    while (isspace(ch)) {
    ch = getc(stdin);
                 
    if (ch == EOF) {
    return (-1);
    }
    *cmd = (char) ch;
    ch = getc(stdin);
    while (isspace(ch)) {
    ch = getc(stdin);
                 
    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++ = ch;
    ch = getc(stdin);
                 
    *word = '\0';
    return i;
              


#include <string.h>
#define MAXWORD 50       
#define DICTSIZ 100       
char dict[DICTSIZ][MAXWORD + 1];   
int nwords = 0;           

int initw(void)
{
    nwords = 0;
    return 1;
              


int insertw(const char *word)
{
    strcpy(dict[nwords], word);
    nwords++;
    return (nwords);
              


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);
    }
                 
    return (0);
              


int lookupw(const char *word)
{
    int i;
    for (i = 0; i < nwords; i++) {
    if (strcmp(word, dict[i]) == 0) {
        return (1);
    }
                 
    return (0);
              
这两部分代码只是在功能上实现了分离,显然实现通讯的部分还没有,下面我们利用RPC来快速实现通讯。

利用RPC实现分布式系统
首先,建立一个RPC源文件,源代码rdict.x如下:


const MAXWORD = 10;  
const DICTSIZ = 3; 
struct example     
{
    int  exfield1; 
    char exfield2; 
};

program RDICTPROG 
{
    version RDICTVERS 
    {
        int INITW ( void )     = 1; 
        int INSERTW ( string ) = 2; 
        int DELETEW ( string ) = 3; 
        int LOOKUPW ( string ) = 4; 
    } = 1; 
} = 0x30090949; 
然后用下列命令产生服务器端函数rdict_srv_func.c:
rpcgen -Ss -o rdict_srv_func.c rdict.x
然后用下列命令产生客户端程序rdict_client.c:
rpcgen -Sc -o rdict_client.c rdict.x

然后用下列命令产生Makefile:
 rpcgen -Sm rdict.x > Makefile
Makefile文件原内容如下:
# This is a template Makefile generated by rpcgen

# Parameters

CLIENT = rdict_client
SERVER = rdict_server

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

TARGETS_SVC.c = rdict_svc.c   rdict_xdr.c
TARGETS_CLNT.c = rdict_clnt.c   rdict_xdr.c
TARGETS = rdict.h rdict_xdr.c rdict_clnt.c rdict_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)
动手修改Makefile,修改后内容如下:
# This is a template Makefile generated by rpcgen

# Parameters

CLIENT = rdict_client
SERVER = rdict_server

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

TARGETS_SVC.c = rdict_svc.c   rdict_xdr.c rdict_srv_func.c
TARGETS_CLNT.c = rdict_clnt.c   rdict_xdr.c rdict_client.c
TARGETS = rdict.h rdict_xdr.c rdict_clnt.c rdict_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) *~
修改客户端源代码rdict_client.c,把接受用户输入并分析用户输入内容的部分加到程序中来。修改后的代码为:


#include "rdict.h"


int nextin(char *cmd, char *word)
{
    int i, ch;
    ch = getc(stdin);
    while (isspace(ch)) {
    ch = getc(stdin);
                 
    if (ch == EOF) {
    return (-1);
    }
    *cmd = (char) ch;
    ch = getc(stdin);
    while (isspace(ch)) {
    ch = getc(stdin);
                 
    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++ = ch;
    ch = getc(stdin);
                 
    *word = '\0';
    return i;
              

void rdictprog_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, RDICTPROG, RDICTVERS, "udp");
    if (clnt == NULL) {
    clnt_pcreateerror(host);
    exit(1);
    }
#endif               
    char word[MAXWORD + 1];   
    char cmd;
    int wordlen;       
    while (1) {
    printf("\nPlease input:");
    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 ==0) printf("Dictionary initialized to empty.\n");
        else printf("Dictionary have already initialized.\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
        printf("%s deleted.\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 ==0) printf("%s found.\n", word);
        else printf("%s not found.\n", word);
        break;
    case 'q':       
        printf("Program quits.\n");
        exit(0);
        break;
    default:       
        printf("Command %c(%s) invalid.\n", cmd, word);
        break;
             
                 

#ifndef    DEBUG
    clnt_destroy(clnt);
#endif               
}


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

    if (argc < 2) {
    printf("usage: %s server_host\n", argv[0]);
    exit(1);
    }
    host = argv[1];
    rdictprog_1(host);
    exit(0);
}
同时修改服务器端代码rdict_srv_func.c,修改后内容为:


#include "rdict.h"

char dict[DICTSIZ][MAXWORD + 1];   
int nwords = 0;           
char init_bool = 0;

int initw(void)
{
    if(init_bool) return 1;
    nwords = 0;
    init_bool = 1;
    return 0;
              


int insertw(const char *word)
{
    strcpy(dict[nwords%DICTSIZ], word);
    nwords++;
    return (nwords);
              


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);
    }
                 
    return (0);
              


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

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

   

    result = initw();

    return &result;
}

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

   
    result = insertw(*argp);

    return &result;
}

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

   

    result = deletew(*argp);

    return &result;
}

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

   

    result = lookupw(*argp);

    return &result;
}
至此,程序做好了。输入一个make命令就可以生成test_server和test_client这两个可执行程序了。
在一台机器上运行./test_server程序,在另外的客户机上运行./test_client server_ip就可以了。这里server_ip是运行着test_server程序的主机的IP地址。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值