C语言进行rpc编程时可以使用rpcgen自动生成分布式的程序代码文件,自己之需要编写***.x,客户端、服务器端程序以及相应的接口就可以了。下面根据一个实现远程文件传输的rpc调用程序(客户端根据文件名请求文件,服务器端传回相应的文件),具体介绍使用rpcgen编写rpc调用的过程,最后保证调用成功必须有8个源文件,rpcgen能自动生成3个,过程比较复杂,共分为7步:
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将文件的第一行传回,大功告成!
相关:
http://hi.baidu.com/%E2%C8%C2%F8%CD%B7%C9%E7%C7%F8/blog/item/6d50b86752957dfef63654cc.html