首先博主抛出一个疑问,如何实现windows和linux之间的rpc通信,这两个异构的系统如何才能建立变量传递通道,这相当困扰博主,如有好心人指点,将不胜感激。
好了,现在切入正题:今天博主在linux环境下实现了一个小小RPC通信,按照惯例,做一下总结吧。
原先博主用的是Red hat9,安装完red hat后不想系统不带GCC,然后博主跑遍各大linux论坛搜寻装GCC的步骤,虽然取得些微的进步,对linux也有一些初步的认识了,但是系统还是不能用。无奈,博主就跑去问老师如何解决linux安装C编译器,不想linux老师来了一句这个red hat他已经不玩好多年了,然后建议我去玩ubuntu。于是,博主就屁颠屁颠地跑去下载最新的ubuntu 10.4,然后安装。出乎意料的是,ubuntu安装要比red hat方便多了,基本属于傻瓜式操作。
所以,接下来博主要讲的基本都是基于ubuntu 10.4平台的。
虽然是新平台,但是ubuntu用的还是linux内核,所以基本和red hat差不多,只有些微差别。
这次操作比上次在red hat上操作顺利多了,博主是参照下文的步骤一步一步实现rpc的:
1. 根据rpc调用的功能,先不考虑rpc调用,编写一个平常的实现相应功能的程序。
如一个远程的文件传输的rpc调用,平常程序便是考虑文件存储在本地,直接打开读便可,如下:
#include <stdio.h>
#include <stdlib.h>
#define MAXNAME 20
#define MAXLENGTH 1024
char * readfile(char *);
int main()
{
char name[MAXNAME];
printf("Enter File Name: ");
scanf("%s", name);
printf("%s", readfile(name));
}
char * readfile(char * name)
{
FILE *file = fopen(name, "r");
char * buf = (char *)malloc(sizeof(char)*MAXLENGTH);
if (file == NULL)
{
printf("File Cann't Be Open!");
return 0;
}
printf("The File Content is : \n");
while (fgets(buf, MAXLENGTH-1, file) != NULL)
{
return buf;
}
return NULL;
}
2. 把程序拆分为两部分,main函数和readfile函数,带有main的一部分是主动发起调用的,在rpc中相当于客户端,带有readfile函数的部分是提供相应的功能的,相当于服务器端。将代码拆分后要在客户端添加相应的rpc调用函数,clnt_create(RMACHINE, FILETRANSPROG, FILETRANSVERS,"tcp");
FILETRANSPROG便是trans.x中的程序名,FILETRANSVERS是版本名,使用tcp进行rpc调用。
拆分后代码如下:
客户端client.c:
#include <rpc/rpc.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include "trans.h"
#define WSVERS MAKEWORD(0, 2)
#define RMACHINE "localhost"
CLIENT *handle;
#define MAXNAME 20
#define MAXLENGTH 1024
char * readfile(char *);
int main()
{
char name[MAXNAME];
char * buf;
printf("Enter File Name: ");
scanf("%s", name);
handle = clnt_create(RMACHINE, FILETRANSPROG, FILETRANSVERS,"tcp");
if (handle == 0) {
printf("Could Not Connect To Remote Server.\n");
exit(1);
}
buf = readfile(name);
printf("%s", buf);
return 0;
}
服务器端server.c:
#include <rpc/rpc.h>
#include <stdio.h>
#include <stdlib.h>
#include "trans.h"
#define MAXNAME 20
#define MAXLENGTH 1024
char * readfile(char * name)
{
FILE *file = fopen(name, "r");
char * buf = (char *)malloc(sizeof(char)*MAXLENGTH);
if (file == NULL)
{
printf("File Cann't Be Open!");
return 0;
}
printf("The File Content is : \n");
while (fgets(buf, MAXLENGTH-1, file) != NULL)
{
return buf;
}
return NULL;
}
3. 编写***.x文件。具体步骤可以参考Douglas的那本Internetworking With TCP/IP 的第三卷,客户端-服务器端编程与应用。
本程序的.x文件命名为trans.x内容如下:
const MAXLENGTH = 1024;
const MAXNAME = 20;
program FILETRANSPROG //程序名
{
version FILETRANSVERS //版本名
{
string READFILE(string) = 1; //调用的方法名
} = 1;
} = 99;
4. 使用rpcgen编辑.x文件,在linux下输入命令
rpcgen trans.x
若格式正确,编译无错误则产生三个文件trans.h,trans_svc.c(服务器端),trans_clnt.c(客户端)。因为上述trans.x中无自定义数据结构,所以没有xdr文件产生。
trans.h代码:
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#ifndef _TRANS_H_RPCGEN
#define _TRANS_H_RPCGEN
#include <rpc/rpc.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MAXLENGTH 1024
#define MAXNAME 20
#define FILETRANSPROG 99
#define FILETRANSVERS 1
#if defined(__STDC__) || defined(__cplusplus)
#define READFILE 1
extern char ** readfile_1(char **, CLIENT *);
extern char ** readfile_1_svc(char **, struct svc_req *);
extern int filetransprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
#else /* K&R C */
#define READFILE 1
extern char ** readfile_1();
extern char ** readfile_1_svc();
extern int filetransprog_1_freeresult ();
#endif /* K&R C */
#ifdef __cplusplus
}
#endif
#endif /* !_TRANS_H_RPCGEN */
trans_svc.c代码:
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include "trans.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
filetransprog_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
union {
char *readfile_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 READFILE:
_xdr_argument = (xdrproc_t) xdr_wrapstring;
_xdr_result = (xdrproc_t) xdr_wrapstring;
local = (char *(*)(char *, struct svc_req *)) readfile_1;
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 (FILETRANSPROG, FILETRANSVERS);
transp = svcudp_create(RPC_ANYSOCK);
if (transp == NULL) {
fprintf (stderr, "%s", "cannot create udp service.");
exit(1);
}
if (!svc_register(transp, FILETRANSPROG, FILETRANSVERS, filetransprog_1, IPPROTO_UDP)) {
fprintf (stderr, "%s", "unable to register (FILETRANSPROG, FILETRANSVERS, 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, FILETRANSPROG, FILETRANSVERS, filetransprog_1, IPPROTO_TCP)) {
fprintf (stderr, "%s", "unable to register (FILETRANSPROG, FILETRANSVERS, tcp).");
exit(1);
}
svc_run ();
fprintf (stderr, "%s", "svc_run returned");
exit (1);
/* NOTREACHED */
}
trans_clnt.c代码:
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include <memory.h> /* for memset */
#include "trans.h"
/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };
char **
readfile_1(char **argp, CLIENT *clnt)
{
static char *clnt_res;
memset((char *)&clnt_res, 0, sizeof(clnt_res));
if (clnt_call (clnt, READFILE,
(xdrproc_t) xdr_wrapstring, (caddr_t) argp,
(xdrproc_t) xdr_wrapstring, (caddr_t) &clnt_res,
TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&clnt_res);
}
5. 编写客户端和服务器端接口。此部分可以说是最麻烦的部分,稍不注意便会出错,同样可以参考Douglas的那本书,但要注意的是他的服务器接口例程代码中的每个函数的第二个参数应该是CLIENT *clnt, 而非struct svc_req * rqstp
为本程序编写的代码如下:
客户端接口文件trans_cif.c:
#include <rpc/rpc.h>
#include <stdio.h>
#include "trans.h"/* Client-side stub interface routines written by programmer */
extern CLIENT * handle;
static char **ret;
char * readfile(char * name)
{
char ** arg;
arg = &name;
ret = readfile_1(arg, handle);
return ret==NULL ? NULL : *ret;
}
服务器端接口文件trans_sif.c:
#include <rpc/rpc.h>
#include <stdio.h>
#include "trans.h"
char * readfile(char *);
static char * retcode;
char ** readfile_1(char ** w, CLIENT *clnt)
{
retcode = readfile(*(char**)w);
return &retcode;
}
6. 编译链接客户端和服务器端程序
不管是客户端还是服务器端,都要链接三个文件,
客户端:程序文件+*** _clnt.c+客户端接口文件。
服务器端:程序文件+*** _svc.c+服务器端接口文件
同时每一段的三个文件都是互相关联的,编译出现错误时,可以根据提示查看三个文件进行debug
命令如下:
gcc -Wall -o trans_client client.c trans_clnt.c trans_cif.c
gcc -Wall -o trans_server server.c trans_svc.c trans_sif.c
7. 启动服务器端和客户端,大功告成。要先运行服务器端程序,再运行客户端程序。命令如下:
./trans_server
./trans_client
client启动后,提示输入要传输的文件名,输入后,server将文件的第一行传回,大功告成!
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/symbol89/archive/2009/06/21/4285142.aspx
----------------------------------------------------------------------------------------------------------------------------------------------------
博主按照上面的指示一步一步做下来,还算顺利。但是在链接服务端文件时爆出一个错误,意思是trans_svc.c这个文件有个undefined reference to 'readfile_1_svc'错误,经本人亲测,是由于trans_svc.c文件第37行原本应该是local = (char*(*)(char *,struct svc_reg *))readfile_1;的,但是rpc编译器翻译成local = (char*(*)(char *,struct svc_reg *))readfile_1_svc ,所以把_svc去掉就好了。
这里不得不提的是ubuntu的vi编辑器相当不好用。即没有装vim,操作起来也没有red hat的vi编译器那样用的那么顺手。这里强烈推荐鸟哥的vi编辑器入门手册。把vi编译器介绍的很详细,附上地址:http://linux.vbird.org/linux_basic/0310vi.php。
vi编辑器模式图(转载自鸟哥的私房菜)
vi模式下指令汇总(转载自鸟哥的私房菜)
最后强烈推荐《鸟哥的私房菜》这本书,相当棒的讲解linux的一套书(有专门的网站)。分上下册,上册讲解linux的基本知识和指令,下册讲linux架站的知识。很实用。
-----------------------------------------------------------------------------------------------------------------------------------------------------
最后运行./trans_server时系统会爆出cannot connect to...的错误(具体的我也忘了),
Cannot register service: RPC: Unable to receive; errno = Connection refused
unable to register (FILETRANSPROG, FILETRANSVERS, udp)
这是由于linux默认把端口映射服务关闭的缘故。这时可以参照下面的解决方法:
centos 6(在CentOS 6.3当中,portmap服务由rpcbind负责) :
yum -y install nfs-utils rpcbind
我们可以看出NFS的安装在Centos 5下和Centos 6下还是有比较大的差异,做个记录方便以后安装。
$ sudo apt-get install nfs-kernel-server nfs-common portmap
$ sudo dpkg-reconfigure portmap
--------------------------------------------------------------------------------------------------------
如果启动还有问题,那有可能是rpcbind服务没有起得来:
[root@iZ25dcp92ckZ rpc]# service rpcbind start
Redirecting to /bin/systemctl start rpcbind.service
CeontOS参考:http://blog.chinaunix.net/uid-20639775-id-3399961.html
在出现如下提示的时候,注意选定“不将portmap 绑定在loopback 地址”
之后,系统会有如下提示:
Current registered services:
------------------------------------------------
100003 2 udp 2049 nfs
100003 3 udp 2049 nfs
100003 4 udp 2049 nfs
…
100003 2 tcp 2049 nfs
100003 3 tcp 2049 nfs
100003 4 tcp 2049 nfs
之后通过查看 /etc/default/portmap, 确保
#OPTIONS="-i 127.0.0.1"
前面的#号 被添加了
重启portmap服务:
$ sudo /etc/init.d/portmap restart
这样就可以了,但是要用root权限执行。
到这里所有我所遇到的问题都解决了,但是博主仍有疑问,如何实现windows和linux之间的rpc通信,这两个异构的系统如何才能建立变量传递通道,这相当困扰博主,如有好心人指点,将不胜感激。
======Below Add Time:2016-01-22=========
启动:
[root@iZ25dcp92ckZ rpc]# ./trans_server
连接:
[root@iZ25dcp92ckZ rpc]# ./trans_client
Enter File Name: /etc/hosts
127.0.0.1 iZ25dcp92ckZ Jack'sAliYunVPS jackxiang localhost
本文出自 “只争朝夕” 博客,请务必保留此出处http://xiaovfight.blog.51cto.com/1625426/398745
好了,现在切入正题:今天博主在linux环境下实现了一个小小RPC通信,按照惯例,做一下总结吧。
原先博主用的是Red hat9,安装完red hat后不想系统不带GCC,然后博主跑遍各大linux论坛搜寻装GCC的步骤,虽然取得些微的进步,对linux也有一些初步的认识了,但是系统还是不能用。无奈,博主就跑去问老师如何解决linux安装C编译器,不想linux老师来了一句这个red hat他已经不玩好多年了,然后建议我去玩ubuntu。于是,博主就屁颠屁颠地跑去下载最新的ubuntu 10.4,然后安装。出乎意料的是,ubuntu安装要比red hat方便多了,基本属于傻瓜式操作。
所以,接下来博主要讲的基本都是基于ubuntu 10.4平台的。
虽然是新平台,但是ubuntu用的还是linux内核,所以基本和red hat差不多,只有些微差别。
这次操作比上次在red hat上操作顺利多了,博主是参照下文的步骤一步一步实现rpc的:
1. 根据rpc调用的功能,先不考虑rpc调用,编写一个平常的实现相应功能的程序。
如一个远程的文件传输的rpc调用,平常程序便是考虑文件存储在本地,直接打开读便可,如下:
#include <stdio.h>
#include <stdlib.h>
#define MAXNAME 20
#define MAXLENGTH 1024
char * readfile(char *);
int main()
{
char name[MAXNAME];
printf("Enter File Name: ");
scanf("%s", name);
printf("%s", readfile(name));
}
char * readfile(char * name)
{
FILE *file = fopen(name, "r");
char * buf = (char *)malloc(sizeof(char)*MAXLENGTH);
if (file == NULL)
{
printf("File Cann't Be Open!");
return 0;
}
printf("The File Content is : \n");
while (fgets(buf, MAXLENGTH-1, file) != NULL)
{
return buf;
}
return NULL;
}
2. 把程序拆分为两部分,main函数和readfile函数,带有main的一部分是主动发起调用的,在rpc中相当于客户端,带有readfile函数的部分是提供相应的功能的,相当于服务器端。将代码拆分后要在客户端添加相应的rpc调用函数,clnt_create(RMACHINE, FILETRANSPROG, FILETRANSVERS,"tcp");
FILETRANSPROG便是trans.x中的程序名,FILETRANSVERS是版本名,使用tcp进行rpc调用。
拆分后代码如下:
客户端client.c:
#include <rpc/rpc.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include "trans.h"
#define WSVERS MAKEWORD(0, 2)
#define RMACHINE "localhost"
CLIENT *handle;
#define MAXNAME 20
#define MAXLENGTH 1024
char * readfile(char *);
int main()
{
char name[MAXNAME];
char * buf;
printf("Enter File Name: ");
scanf("%s", name);
handle = clnt_create(RMACHINE, FILETRANSPROG, FILETRANSVERS,"tcp");
if (handle == 0) {
printf("Could Not Connect To Remote Server.\n");
exit(1);
}
buf = readfile(name);
printf("%s", buf);
return 0;
}
服务器端server.c:
#include <rpc/rpc.h>
#include <stdio.h>
#include <stdlib.h>
#include "trans.h"
#define MAXNAME 20
#define MAXLENGTH 1024
char * readfile(char * name)
{
FILE *file = fopen(name, "r");
char * buf = (char *)malloc(sizeof(char)*MAXLENGTH);
if (file == NULL)
{
printf("File Cann't Be Open!");
return 0;
}
printf("The File Content is : \n");
while (fgets(buf, MAXLENGTH-1, file) != NULL)
{
return buf;
}
return NULL;
}
3. 编写***.x文件。具体步骤可以参考Douglas的那本Internetworking With TCP/IP 的第三卷,客户端-服务器端编程与应用。
本程序的.x文件命名为trans.x内容如下:
const MAXLENGTH = 1024;
const MAXNAME = 20;
program FILETRANSPROG //程序名
{
version FILETRANSVERS //版本名
{
string READFILE(string) = 1; //调用的方法名
} = 1;
} = 99;
4. 使用rpcgen编辑.x文件,在linux下输入命令
rpcgen trans.x
若格式正确,编译无错误则产生三个文件trans.h,trans_svc.c(服务器端),trans_clnt.c(客户端)。因为上述trans.x中无自定义数据结构,所以没有xdr文件产生。
trans.h代码:
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#ifndef _TRANS_H_RPCGEN
#define _TRANS_H_RPCGEN
#include <rpc/rpc.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MAXLENGTH 1024
#define MAXNAME 20
#define FILETRANSPROG 99
#define FILETRANSVERS 1
#if defined(__STDC__) || defined(__cplusplus)
#define READFILE 1
extern char ** readfile_1(char **, CLIENT *);
extern char ** readfile_1_svc(char **, struct svc_req *);
extern int filetransprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
#else /* K&R C */
#define READFILE 1
extern char ** readfile_1();
extern char ** readfile_1_svc();
extern int filetransprog_1_freeresult ();
#endif /* K&R C */
#ifdef __cplusplus
}
#endif
#endif /* !_TRANS_H_RPCGEN */
trans_svc.c代码:
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include "trans.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
filetransprog_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
union {
char *readfile_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 READFILE:
_xdr_argument = (xdrproc_t) xdr_wrapstring;
_xdr_result = (xdrproc_t) xdr_wrapstring;
local = (char *(*)(char *, struct svc_req *)) readfile_1;
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 (FILETRANSPROG, FILETRANSVERS);
transp = svcudp_create(RPC_ANYSOCK);
if (transp == NULL) {
fprintf (stderr, "%s", "cannot create udp service.");
exit(1);
}
if (!svc_register(transp, FILETRANSPROG, FILETRANSVERS, filetransprog_1, IPPROTO_UDP)) {
fprintf (stderr, "%s", "unable to register (FILETRANSPROG, FILETRANSVERS, 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, FILETRANSPROG, FILETRANSVERS, filetransprog_1, IPPROTO_TCP)) {
fprintf (stderr, "%s", "unable to register (FILETRANSPROG, FILETRANSVERS, tcp).");
exit(1);
}
svc_run ();
fprintf (stderr, "%s", "svc_run returned");
exit (1);
/* NOTREACHED */
}
trans_clnt.c代码:
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include <memory.h> /* for memset */
#include "trans.h"
/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };
char **
readfile_1(char **argp, CLIENT *clnt)
{
static char *clnt_res;
memset((char *)&clnt_res, 0, sizeof(clnt_res));
if (clnt_call (clnt, READFILE,
(xdrproc_t) xdr_wrapstring, (caddr_t) argp,
(xdrproc_t) xdr_wrapstring, (caddr_t) &clnt_res,
TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&clnt_res);
}
5. 编写客户端和服务器端接口。此部分可以说是最麻烦的部分,稍不注意便会出错,同样可以参考Douglas的那本书,但要注意的是他的服务器接口例程代码中的每个函数的第二个参数应该是CLIENT *clnt, 而非struct svc_req * rqstp
为本程序编写的代码如下:
客户端接口文件trans_cif.c:
#include <rpc/rpc.h>
#include <stdio.h>
#include "trans.h"/* Client-side stub interface routines written by programmer */
extern CLIENT * handle;
static char **ret;
char * readfile(char * name)
{
char ** arg;
arg = &name;
ret = readfile_1(arg, handle);
return ret==NULL ? NULL : *ret;
}
服务器端接口文件trans_sif.c:
#include <rpc/rpc.h>
#include <stdio.h>
#include "trans.h"
char * readfile(char *);
static char * retcode;
char ** readfile_1(char ** w, CLIENT *clnt)
{
retcode = readfile(*(char**)w);
return &retcode;
}
6. 编译链接客户端和服务器端程序
不管是客户端还是服务器端,都要链接三个文件,
客户端:程序文件+*** _clnt.c+客户端接口文件。
服务器端:程序文件+*** _svc.c+服务器端接口文件
同时每一段的三个文件都是互相关联的,编译出现错误时,可以根据提示查看三个文件进行debug
命令如下:
gcc -Wall -o trans_client client.c trans_clnt.c trans_cif.c
gcc -Wall -o trans_server server.c trans_svc.c trans_sif.c
7. 启动服务器端和客户端,大功告成。要先运行服务器端程序,再运行客户端程序。命令如下:
./trans_server
./trans_client
client启动后,提示输入要传输的文件名,输入后,server将文件的第一行传回,大功告成!
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/symbol89/archive/2009/06/21/4285142.aspx
----------------------------------------------------------------------------------------------------------------------------------------------------
博主按照上面的指示一步一步做下来,还算顺利。但是在链接服务端文件时爆出一个错误,意思是trans_svc.c这个文件有个undefined reference to 'readfile_1_svc'错误,经本人亲测,是由于trans_svc.c文件第37行原本应该是local = (char*(*)(char *,struct svc_reg *))readfile_1;的,但是rpc编译器翻译成local = (char*(*)(char *,struct svc_reg *))readfile_1_svc ,所以把_svc去掉就好了。
这里不得不提的是ubuntu的vi编辑器相当不好用。即没有装vim,操作起来也没有red hat的vi编译器那样用的那么顺手。这里强烈推荐鸟哥的vi编辑器入门手册。把vi编译器介绍的很详细,附上地址:http://linux.vbird.org/linux_basic/0310vi.php。
vi编辑器模式图(转载自鸟哥的私房菜)
vi模式下指令汇总(转载自鸟哥的私房菜)
最后强烈推荐《鸟哥的私房菜》这本书,相当棒的讲解linux的一套书(有专门的网站)。分上下册,上册讲解linux的基本知识和指令,下册讲linux架站的知识。很实用。
-----------------------------------------------------------------------------------------------------------------------------------------------------
最后运行./trans_server时系统会爆出cannot connect to...的错误(具体的我也忘了),
Cannot register service: RPC: Unable to receive; errno = Connection refused
unable to register (FILETRANSPROG, FILETRANSVERS, udp)
这是由于linux默认把端口映射服务关闭的缘故。这时可以参照下面的解决方法:
centos 6(在CentOS 6.3当中,portmap服务由rpcbind负责) :
yum -y install nfs-utils rpcbind
我们可以看出NFS的安装在Centos 5下和Centos 6下还是有比较大的差异,做个记录方便以后安装。
$ sudo apt-get install nfs-kernel-server nfs-common portmap
$ sudo dpkg-reconfigure portmap
--------------------------------------------------------------------------------------------------------
如果启动还有问题,那有可能是rpcbind服务没有起得来:
[root@iZ25dcp92ckZ rpc]# service rpcbind start
Redirecting to /bin/systemctl start rpcbind.service
CeontOS参考:http://blog.chinaunix.net/uid-20639775-id-3399961.html
在出现如下提示的时候,注意选定“不将portmap 绑定在loopback 地址”
之后,系统会有如下提示:
Current registered services:
------------------------------------------------
100003 2 udp 2049 nfs
100003 3 udp 2049 nfs
100003 4 udp 2049 nfs
…
100003 2 tcp 2049 nfs
100003 3 tcp 2049 nfs
100003 4 tcp 2049 nfs
之后通过查看 /etc/default/portmap, 确保
#OPTIONS="-i 127.0.0.1"
前面的#号 被添加了
重启portmap服务:
$ sudo /etc/init.d/portmap restart
这样就可以了,但是要用root权限执行。
到这里所有我所遇到的问题都解决了,但是博主仍有疑问,如何实现windows和linux之间的rpc通信,这两个异构的系统如何才能建立变量传递通道,这相当困扰博主,如有好心人指点,将不胜感激。
======Below Add Time:2016-01-22=========
启动:
[root@iZ25dcp92ckZ rpc]# ./trans_server
连接:
[root@iZ25dcp92ckZ rpc]# ./trans_client
Enter File Name: /etc/hosts
127.0.0.1 iZ25dcp92ckZ Jack'sAliYunVPS jackxiang localhost
本文出自 “只争朝夕” 博客,请务必保留此出处http://xiaovfight.blog.51cto.com/1625426/398745
fms在centos linux 4.x上可以正常安装,装完了就可以自已自动启来了。但是在centos linux 5.x上,装完后启动不了。日志内也没有任何错误信息。
安装:
./installFMS -platformWarnOnly
安装完成后FMS服务没有自动启来,日志内也没有任何错误信息。
我们通过看/etc/init.d/fms这个启动脚本,发现FMS启动主要是用到了fmsmaster start。手动跑fmsmaster start发现出错。
[root@fms1 fms]# ./fmsmaster start
./fmsmaster: error while loading shared libraries: libssl.so.4: cannot open shared object file: No such file or directory
libssl.so.4是这个链接,在5.x上版本上没有建,手动建立:
[root@fms1 fms]# ln -s /usr/lib/libssl.so /usr/lib/libssl.so.4
再启动:
[root@fms1 fms]# ./fmsmaster start
./fmsmaster: error while loading shared libraries: libcrypto.so.4: cannot open shared object file: No such file or directory
再建链接:
[root@fms1 fms]# ln -s /usr/lib/libcrypto.so /usr/lib/libcrypto.so.4
OK,FMS可以正常启动了。
来源:http://hi.baidu.com/farmerluo/blog/item/32bfd42a7790d398023bf65e.html
参考:http://mimieye.iteye.com/blog/103755
http://blog.bj50.net/black/?p=184
下载:http://blog.csdn.net/xiang08/article/details/4136670
https://www.adobe.com/cfusion/tdrc/index.cfm?loc=zh_cn&product=flashmediaserver
安装:
./installFMS -platformWarnOnly
安装完成后FMS服务没有自动启来,日志内也没有任何错误信息。
我们通过看/etc/init.d/fms这个启动脚本,发现FMS启动主要是用到了fmsmaster start。手动跑fmsmaster start发现出错。
[root@fms1 fms]# ./fmsmaster start
./fmsmaster: error while loading shared libraries: libssl.so.4: cannot open shared object file: No such file or directory
libssl.so.4是这个链接,在5.x上版本上没有建,手动建立:
[root@fms1 fms]# ln -s /usr/lib/libssl.so /usr/lib/libssl.so.4
再启动:
[root@fms1 fms]# ./fmsmaster start
./fmsmaster: error while loading shared libraries: libcrypto.so.4: cannot open shared object file: No such file or directory
再建链接:
[root@fms1 fms]# ln -s /usr/lib/libcrypto.so /usr/lib/libcrypto.so.4
OK,FMS可以正常启动了。
来源:http://hi.baidu.com/farmerluo/blog/item/32bfd42a7790d398023bf65e.html
参考:http://mimieye.iteye.com/blog/103755
http://blog.bj50.net/black/?p=184
下载:http://blog.csdn.net/xiang08/article/details/4136670
https://www.adobe.com/cfusion/tdrc/index.cfm?loc=zh_cn&product=flashmediaserver
为了省却不必要的麻烦,请尽量在编译安装时直接加上 --enable-deflate --enable-headers 参数
阅读全文
阅读全文
最近发现一个问题:
window.location.href在IE6下面竟然不跳转,这是为什么呢?
这个是我写的语句:
<a href="javascript:void(0);" ;onclick="javascript:window.location.href='http://www.sina.com';">转到新浪</a>
后面是网上找到了解决方案(URL:http://www.cnblogs.com/kaima/archive/2008/08/22/1273808.html):
<script type="text/javascript">
function goUrl(x)
{
window.location.href=x;
}
</script>
<a href="javascript:;" onclick="javascript:goUrl('http://www.sina.com');">跳转1</a>
<a href="javascript:void(0);" onclick="javascript:goUrl('http://www.sina.com');">跳转2</a>
<a href="javascript:void(0);" onclick="javascript:goUrl('http://www.sina.com');return false;">跳转3</a>
<a href="#" onclick="javascript:goUrl('http://www.sina.com');">跳转4</a>
<a href="###" onclick="javascript:goUrl('http://www.sina.com');">跳转5</a>
测试环境IE6,IE7,Firefox 3。
跳转1和2在IE6环境下无效,3、4、5在IE6,IE7,Firefox3.01下测试均能 通过,。
跳转4和5最简洁。
关键在于<a>的href属性,空链接用"#","###"。
为了不返回网页顶端。
空链接推荐用"###"。
我采用了第三种方法,在后面加了return false;这样,IE6、IE7、FF3下面全部通过。
如果这样:
注:"this.blur()"来消除链接后的焦点虚线框
来源:http://blog.163.com/leaf-shi/blog/static/12749133020098109215329/
window.location.href在IE6下面竟然不跳转,这是为什么呢?
这个是我写的语句:
<a href="javascript:void(0);" ;onclick="javascript:window.location.href='http://www.sina.com';">转到新浪</a>
后面是网上找到了解决方案(URL:http://www.cnblogs.com/kaima/archive/2008/08/22/1273808.html):
<script type="text/javascript">
function goUrl(x)
{
window.location.href=x;
}
</script>
<a href="javascript:;" onclick="javascript:goUrl('http://www.sina.com');">跳转1</a>
<a href="javascript:void(0);" onclick="javascript:goUrl('http://www.sina.com');">跳转2</a>
<a href="javascript:void(0);" onclick="javascript:goUrl('http://www.sina.com');return false;">跳转3</a>
<a href="#" onclick="javascript:goUrl('http://www.sina.com');">跳转4</a>
<a href="###" onclick="javascript:goUrl('http://www.sina.com');">跳转5</a>
测试环境IE6,IE7,Firefox 3。
跳转1和2在IE6环境下无效,3、4、5在IE6,IE7,Firefox3.01下测试均能 通过,。
跳转4和5最简洁。
关键在于<a>的href属性,空链接用"#","###"。
为了不返回网页顶端。
空链接推荐用"###"。
我采用了第三种方法,在后面加了return false;这样,IE6、IE7、FF3下面全部通过。
如果这样:
注:"this.blur()"来消除链接后的焦点虚线框
来源:http://blog.163.com/leaf-shi/blog/static/12749133020098109215329/
Smarty最大的功能是做模版的页面缓存。也就是通过Smarty可以完成两个步骤:编译+解析
第一步:编译。是指把模版文件的标签替换为纯php,再保存在缓存位置,保存的文件扩展名是PHP,我把这个步骤叫做编译(这是我自己的叫法,不是官方的)
第二步:解析。也就是把刚才编译的PHP文件解析执行而已~~这个就不用多做解释了
切入正题,在Smarty.class.php文件中加入如下代码
这个函数的作用就是保存文件~~
调用方法如下
来源:http://blog.163.com/junsheng_zheng/blog/static/110710882200911211162510/
第一步:编译。是指把模版文件的标签替换为纯php,再保存在缓存位置,保存的文件扩展名是PHP,我把这个步骤叫做编译(这是我自己的叫法,不是官方的)
第二步:解析。也就是把刚才编译的PHP文件解析执行而已~~这个就不用多做解释了
切入正题,在Smarty.class.php文件中加入如下代码
function MakeHtmlFile($file_name, $content)
{ //目录不存在就创建
if (!file_exists (dirname($file_name))) {
if (!@mkdir (dirname($file_name), 0777)) {
die($file_name."目录创建失败!");
}
}
if(!$fp = fopen($file_name, "w")){
echo "文件打开失败!";
return false;
}
if(!fwrite($fp, $content)){
echo "文件写入失败!";
fclose($fp);
return false;
}
fclose($fp);
chmod($file_name,0666);
}
{ //目录不存在就创建
if (!file_exists (dirname($file_name))) {
if (!@mkdir (dirname($file_name), 0777)) {
die($file_name."目录创建失败!");
}
}
if(!$fp = fopen($file_name, "w")){
echo "文件打开失败!";
return false;
}
if(!fwrite($fp, $content)){
echo "文件写入失败!";
fclose($fp);
return false;
}
fclose($fp);
chmod($file_name,0666);
}
这个函数的作用就是保存文件~~
调用方法如下
require '../libs/Smarty.class.php';
$smarty = new Smarty;
//…………省略变量定义和赋值
//$smarty->display('index.tpl');
$content=$smarty->fetch("index.tpl");
$smarty->MakeHtmlFile('./index.html',$content);//生成
$smarty = new Smarty;
//…………省略变量定义和赋值
//$smarty->display('index.tpl');
$content=$smarty->fetch("index.tpl");
$smarty->MakeHtmlFile('./index.html',$content);//生成
来源:http://blog.163.com/junsheng_zheng/blog/static/110710882200911211162510/
一:man 1,2,3,4,5,6
区段1:用户指令
区段2:系统调用
区段3:程序库调用
区段4:设备
区段5:文件格式
区段6:游戏
三:info 命令,网页形式,更详细。
二:/usr/share/doc
三:google,别百度。
阅读全文
区段1:用户指令
区段2:系统调用
区段3:程序库调用
区段4:设备
区段5:文件格式
区段6:游戏
三:info 命令,网页形式,更详细。
二:/usr/share/doc
三:google,别百度。
阅读全文
关于“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 /* name of remote program ( not used ) */
{
version RDICTVERS /* declaration of version ( see below ) */
{
int INITW ( void ) = 1; /* first procedure in this program */
int INSERTW ( string ) = 2; /* second procedure in this program */
int DELETEW ( string ) = 3; /* third procedure in this program */
int LOOKUPW ( string ) = 4; /* fourth procedure in this program */
} = 1; /* definition of the program version */
} = 0x30090949; /* remote program number ( must be unique ) */
则说明这个RPC中有四个函数可用,即客户端可以调用initw_1、insertw_1、deletew_1、lookupw_1四个函数来向服务端发送消息,服务端可以用initw_1_svc、insertw_1_svc、deletew_1_svc、lookupw_1_svc四个函数来处理请求并返回结果。
原任务
假设现在有这样一个程序,源代码如下:
/* 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("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;
} /* end of switch */
} /* 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;
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++ = ch;
ch = getc(stdin);
} /* end of while */
*word = '\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);
}
} /* end of for */
return (0);
} /* end of lookupw */
这是一个简单的字典程序,即程序运行起来以后维护着一个字典库,用户可以向里面添加词语,也可以查询或删除词语。
当然,这个程序只能在同一台主机上运行。程序整个运行过程中,只需要完成如下几个步骤:
A、接受用户输入;
B、分析用户输入决定是否进行下面的步骤:
1、初始化数据库;
2、向数据库添加词语;
3、查询或删除词语
任务分解
大家可以想到,对于一个大型系统,比如需要有很多人维护这个系统的数据。象上面这样独立的程序就不适用了,需要做成分布式系统:
即一个服务器维护着数据库,任何客户端都可以接受用户请求,客户端分析用户命令后提交给服务器去处理。
所以我们可能会把程序分成两部分:
客户端:接受用户输入,并判断用户输入内容的正确性,向服务器提交数据,等服务器返回消息
服务器端:维护数据,接受客户端命令并执行后返回结果。
所以我们把上面这个程序分解成下面两部分:
/* dict1.c -- main, nextin */
#include <stdio.h>
#include <stdlib.h>
#define MAXWORD 50 /* maximum length of a command or 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("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;
} /* end of switch */
} /* 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;
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++ = ch;
ch = getc(stdin);
} /* end of while */
*word = '\0';
return i;
} /* end of nextin */
和
/* dict2.c -- initw, insertw, deletew, lookupw */
#include <string.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 */
/* ------------------------------------------------------------------
* 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);
}
} /* end of for */
return (0);
} /* end of lookupw */
这两部分代码只是在功能上实现了分离,显然实现通讯的部分还没有,下面我们利用RPC来快速实现通讯。
利用RPC实现分布式系统
首先,建立一个RPC源文件,源代码rdict.x如下:
/* rdict.x */
/* RPC declarations for dictionary program */
const MAXWORD = 10; /* maximum length of a command or word */
const DICTSIZ = 3; /* number of entries in dictionary */
struct example /* unused structure declared here to */
{
int exfield1; /* illustrate how rpcgen builds XDR */
char exfield2; /* routines to convert structures */
};
/* ------------------------------------------------------------------
* RDICTPROG -- remote program that provides insert, delete, and lookup
* ------------------------------------------------------------------ */
program RDICTPROG /* name of remote program ( not used ) */
{
version RDICTVERS /* declaration of version ( see below ) */
{
int INITW ( void ) = 1; /* first procedure in this program */
int INSERTW ( string ) = 2; /* second procedure in this program */
int DELETEW ( string ) = 3; /* third procedure in this program */
int LOOKUPW ( string ) = 4; /* fourth procedure in this program */
} = 1; /* definition of the program version */
} = 0x30090949; /* remote program number ( must be unique ) */
然后用下列命令产生服务器端函数rdict_srv_func.c:
rpcgen -Ss -o rdict_srv_func.c rdict.x
然后用下列命令产生客户端程序rdict_client.c:
rpcgen -Sc -o rdict_client.c rdict.x
/************关于本文档********************************************
*filename: 我是这样学习Linux下C语言编程的-利用RPC快速实现分布式系统
*purpose: 说明如何利用RPC快速进行客户端-服务器端C-S结构编程
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-02-27 19:20
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to:
* Ubuntu 本程序在Ubuntu 6.10系统上测试完全正常
* Google.com 我通过google搜索并参考了RPC编程相关的许多文章
* 网络安全焦点(www.xfocus.net) 我主要借鉴了此文 http://www.xfocus.net/articles/200009/10.html
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
然后用下列命令产生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,把接受用户输入并分析用户输入内容的部分加到程序中来。修改后的代码为:
/*
* 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 "rdict.h"
/* ------------------------------------------------------------------
* 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;
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++ = ch;
ch = getc(stdin);
} /* end of while */
*word = '\0';
return i;
} /* end of nextin */
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 /* DEBUG */
char word[MAXWORD + 1]; /* space to hold word from input line */
char cmd;
int wordlen; /* length of input word */
while (1) {
printf("\nPlease input:");
wordlen = nextin(&cmd, word);
if (wordlen < 0) {
exit(0);
}
/* printf("\nYour cmd is:%c, your word is:%s\n", cmd, word); */
switch (cmd) {
case 'I': /* 初始化 */
result_1 = initw_1((void *) &initw_1_arg, clnt);
/* printf("\nYour result is:%d\n", *result_1); */
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);
/* printf("\nYour result is:%d, your string is:%s(%d)\n", *result_2, insertw_1_arg, strlen(insertw_1_arg)); */
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);
/* printf("\nYour result is:%d, your string is:%s(%d)\n", *result_3, deletew_1_arg, strlen(deletew_1_arg)); */
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);
/* printf("\nYour result is:%d, your string is:%s(%d)\n", *result_4, lookupw_1_arg, strlen(lookupw_1_arg)); */
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;
} /* end of switch */
} /* 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];
rdictprog_1(host);
exit(0);
}
同时修改服务器端代码rdict_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 "rdict.h"
char dict[DICTSIZ][MAXWORD + 1]; /* storage for a dictionary of words */
int nwords = 0; /* number of words in the dictionary */
char init_bool = 0;
int initw(void)
{
if(init_bool) return 1;
nwords = 0;
init_bool = 1;
return 0;
} /* end of initw */
/* ------------------------------------------------------------------
* insertw -- insert a word in the dictionary
* ------------------------------------------------------------------ */
int insertw(const char *word)
{
strcpy(dict[nwords%DICTSIZ], 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 0;
}
} /* end of for */
return 1;
} /* end of lookupw */
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
*/
result = insertw(*argp);
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;
}
至此,程序做好了。输入一个make命令就可以生成test_server和test_client这两个可执行程序了。
在一台机器上运行./test_server程序,在另外的客户机上运行./test_client server_ip就可以了。这里server_ip是运行着test_server程序的主机的IP地址。
阅读全文
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 /* name of remote program ( not used ) */
{
version RDICTVERS /* declaration of version ( see below ) */
{
int INITW ( void ) = 1; /* first procedure in this program */
int INSERTW ( string ) = 2; /* second procedure in this program */
int DELETEW ( string ) = 3; /* third procedure in this program */
int LOOKUPW ( string ) = 4; /* fourth procedure in this program */
} = 1; /* definition of the program version */
} = 0x30090949; /* remote program number ( must be unique ) */
则说明这个RPC中有四个函数可用,即客户端可以调用initw_1、insertw_1、deletew_1、lookupw_1四个函数来向服务端发送消息,服务端可以用initw_1_svc、insertw_1_svc、deletew_1_svc、lookupw_1_svc四个函数来处理请求并返回结果。
原任务
假设现在有这样一个程序,源代码如下:
/* 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("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;
} /* end of switch */
} /* 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;
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++ = ch;
ch = getc(stdin);
} /* end of while */
*word = '\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);
}
} /* end of for */
return (0);
} /* end of lookupw */
这是一个简单的字典程序,即程序运行起来以后维护着一个字典库,用户可以向里面添加词语,也可以查询或删除词语。
当然,这个程序只能在同一台主机上运行。程序整个运行过程中,只需要完成如下几个步骤:
A、接受用户输入;
B、分析用户输入决定是否进行下面的步骤:
1、初始化数据库;
2、向数据库添加词语;
3、查询或删除词语
任务分解
大家可以想到,对于一个大型系统,比如需要有很多人维护这个系统的数据。象上面这样独立的程序就不适用了,需要做成分布式系统:
即一个服务器维护着数据库,任何客户端都可以接受用户请求,客户端分析用户命令后提交给服务器去处理。
所以我们可能会把程序分成两部分:
客户端:接受用户输入,并判断用户输入内容的正确性,向服务器提交数据,等服务器返回消息
服务器端:维护数据,接受客户端命令并执行后返回结果。
所以我们把上面这个程序分解成下面两部分:
/* dict1.c -- main, nextin */
#include <stdio.h>
#include <stdlib.h>
#define MAXWORD 50 /* maximum length of a command or 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("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;
} /* end of switch */
} /* 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;
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++ = ch;
ch = getc(stdin);
} /* end of while */
*word = '\0';
return i;
} /* end of nextin */
和
/* dict2.c -- initw, insertw, deletew, lookupw */
#include <string.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 */
/* ------------------------------------------------------------------
* 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);
}
} /* end of for */
return (0);
} /* end of lookupw */
这两部分代码只是在功能上实现了分离,显然实现通讯的部分还没有,下面我们利用RPC来快速实现通讯。
利用RPC实现分布式系统
首先,建立一个RPC源文件,源代码rdict.x如下:
/* rdict.x */
/* RPC declarations for dictionary program */
const MAXWORD = 10; /* maximum length of a command or word */
const DICTSIZ = 3; /* number of entries in dictionary */
struct example /* unused structure declared here to */
{
int exfield1; /* illustrate how rpcgen builds XDR */
char exfield2; /* routines to convert structures */
};
/* ------------------------------------------------------------------
* RDICTPROG -- remote program that provides insert, delete, and lookup
* ------------------------------------------------------------------ */
program RDICTPROG /* name of remote program ( not used ) */
{
version RDICTVERS /* declaration of version ( see below ) */
{
int INITW ( void ) = 1; /* first procedure in this program */
int INSERTW ( string ) = 2; /* second procedure in this program */
int DELETEW ( string ) = 3; /* third procedure in this program */
int LOOKUPW ( string ) = 4; /* fourth procedure in this program */
} = 1; /* definition of the program version */
} = 0x30090949; /* remote program number ( must be unique ) */
然后用下列命令产生服务器端函数rdict_srv_func.c:
rpcgen -Ss -o rdict_srv_func.c rdict.x
然后用下列命令产生客户端程序rdict_client.c:
rpcgen -Sc -o rdict_client.c rdict.x
/************关于本文档********************************************
*filename: 我是这样学习Linux下C语言编程的-利用RPC快速实现分布式系统
*purpose: 说明如何利用RPC快速进行客户端-服务器端C-S结构编程
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-02-27 19:20
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to:
* Ubuntu 本程序在Ubuntu 6.10系统上测试完全正常
* Google.com 我通过google搜索并参考了RPC编程相关的许多文章
* 网络安全焦点(www.xfocus.net) 我主要借鉴了此文 http://www.xfocus.net/articles/200009/10.html
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
然后用下列命令产生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,把接受用户输入并分析用户输入内容的部分加到程序中来。修改后的代码为:
/*
* 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 "rdict.h"
/* ------------------------------------------------------------------
* 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;
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++ = ch;
ch = getc(stdin);
} /* end of while */
*word = '\0';
return i;
} /* end of nextin */
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 /* DEBUG */
char word[MAXWORD + 1]; /* space to hold word from input line */
char cmd;
int wordlen; /* length of input word */
while (1) {
printf("\nPlease input:");
wordlen = nextin(&cmd, word);
if (wordlen < 0) {
exit(0);
}
/* printf("\nYour cmd is:%c, your word is:%s\n", cmd, word); */
switch (cmd) {
case 'I': /* 初始化 */
result_1 = initw_1((void *) &initw_1_arg, clnt);
/* printf("\nYour result is:%d\n", *result_1); */
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);
/* printf("\nYour result is:%d, your string is:%s(%d)\n", *result_2, insertw_1_arg, strlen(insertw_1_arg)); */
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);
/* printf("\nYour result is:%d, your string is:%s(%d)\n", *result_3, deletew_1_arg, strlen(deletew_1_arg)); */
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);
/* printf("\nYour result is:%d, your string is:%s(%d)\n", *result_4, lookupw_1_arg, strlen(lookupw_1_arg)); */
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;
} /* end of switch */
} /* 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];
rdictprog_1(host);
exit(0);
}
同时修改服务器端代码rdict_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 "rdict.h"
char dict[DICTSIZ][MAXWORD + 1]; /* storage for a dictionary of words */
int nwords = 0; /* number of words in the dictionary */
char init_bool = 0;
int initw(void)
{
if(init_bool) return 1;
nwords = 0;
init_bool = 1;
return 0;
} /* end of initw */
/* ------------------------------------------------------------------
* insertw -- insert a word in the dictionary
* ------------------------------------------------------------------ */
int insertw(const char *word)
{
strcpy(dict[nwords%DICTSIZ], 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 0;
}
} /* end of for */
return 1;
} /* end of lookupw */
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
*/
result = insertw(*argp);
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;
}
至此,程序做好了。输入一个make命令就可以生成test_server和test_client这两个可执行程序了。
在一台机器上运行./test_server程序,在另外的客户机上运行./test_client server_ip就可以了。这里server_ip是运行着test_server程序的主机的IP地址。
阅读全文
nginx现在正在以光的速度蔓延开来,他以其稳定性和高性能等众多优点迅速扩大市场,大家都知道,nginx是以单线程为基础的,那么他怎么能在并发性上取得优势的呢?会不会因为网络阻塞而导致主线程阻塞呢?下面就相关问题作一些概念性的阐述。
这篇文章写得好还有代码从自己角度来说nginx的epoll架构,AddTime:2015-02-03
http://blog.csdn.net/zhaoxy_thu/article/details/24624729
阅读全文
这篇文章写得好还有代码从自己角度来说nginx的epoll架构,AddTime:2015-02-03
http://blog.csdn.net/zhaoxy_thu/article/details/24624729
阅读全文