生成 XDR 例程示例
本节说明生成 XDR 例程示例的过程。
将本地过程转换为远程过程示例 演示自动生成客户机和服务器远程过程调用 (RPC) 代码。 rpcgen 协议编译器还可用于生成 eXternal 数据表示 (XDR) 例程,这些例程将本地数据结构转换为网络格式,反之亦然。 以下协议描述文件提供了一个完整的 RPC 服务,该服务是一个远程目录列表服务,该服务使用 rpcgen 协议编译器不仅生成存根例程,还生成 XDR 例程。
/*
* dir.x: Remote directory listing protocol
*/
const MAXNAMELEN = 255;/* maximum length of a directory entry */
typedef string nametype<MAXNAMELEN>; /* a directory entry */
typedef struct namenode *namelist; /* a link in the listing */
/*
* A node in the directory listing
*/
struct namenode {
nametype name; /* name of directory entry */
namelist next; /* next entry */
};
/*
* The result of a READDIR operation.
*/
union readdir_res switch (int errno) {
case 0:
namelist list; /* no error: return directory listing */
default:
void; /* error occurred: nothing else to return */
};
/*
* The directory program definition
*/
program DIRPROG {
version DIRVERS {
readdir_res
READDIR(nametype) = 1;
} = 1;
} = 76;
注: 类型 (如readdir_res在先前示例中,可以使用 struct, union 和 enum 关键字来定义) ,但不要在这些类型的变量的后续声明中使用这些关键字。 例如,如果定义并集,foo,仅使用foo而不是union foo. 实际上, rpcgen 协议编译器将 RPC 并集编译为 C 结构,在这种情况下,使用 并集 关键字声明这些并集是错误的。
在 dir.x 文件上运行 rpcgen 协议编译器将创建四个输出文件。 三个与之前相同: 头文件,客户机存根例程和服务器框架。 第四个文件包含将指定的数据类型转换为 XDR 格式所需的 XDR 例程,反之亦然。 这些是 dir_xdr.c 文件中的输出。
以下是 READDIR 过程的实现:
/*
* dir_proc.c: remote readdir implementation
*/
#include <rpc/rpc.h>
#include <sys/dir.h>
#include "dir.h"
extern int errno;
extern char *malloc();
extern char *strdup();
readdir_res *
readdir_1(dirname)
nametype *dirname;
{
DIR *dirp;
struct direct *d;
namelist nl;
namelist *nlp;
static readdir_res res; /* must be static */
/*
* Open directory
*/
dirp = opendir(*dirname);
if (dirp == NULL) {
res.errno = errno;
return (&res);
}
/*
* Free previous result
*/
xdr_free(xdr_readdir_res, &res);
/*
* Collect directory entries.
* Memory allocated here will be freed by xdr_free
* next time readdir_1 is called
*/
nlp = &res.readdir_res_u.list;
while (d = readdir(dirp)) {
nl = *nlp = (namenode *) malloc(sizeof(namenode));
nl->name = strdup(d->d_name);
nlp = &nl->next;
}
*nlp = NULL;
/*
* Return the result
*/
res.errno = 0;
closedir(dirp);
return (&res);
}
客户机端程序按如下所示调用服务器:
/*
* rls.c: Remote directory listing client
*/
#include <stdio.h>
#include <rpc/rpc.h> /* always need this */
#include "dir.h" /* will be generated by rpcgen */
extern int errno;
main(argc, argv)
int argc;
char *argv[];
{
CLIENT *cl;
char *server;
char *dir;
readdir_res *result;
namelist nl;
if (argc != 3) {
fprintf(stderr, "usage: %s host directory\n",
argv[0]);
exit(1);
}
/*
* Remember what our command line arguments refer to
*/
server = argv[1];
dir = argv[2];
/*
* Create client "handle" used for calling MESSAGEPROG
* on the server designated on the command line. We
* tell the RPC package to use the "tcp" protocol
* when contacting the server.
*/
cl = clnt_create(server, DIRPROG, DIRVERS, "tcp");
if (cl == NULL) {
/*
* Could not establish connection with server.
* Print error message and die.
*/
clnt_pcreateerror(server);
exit(1);
}
/*
* Call the remote procedure readdir on the server
*/
result = readdir_1(&dir, cl);
if (result == NULL) {
/*
* An error occurred while calling the server.
* Print error message and die.
*/
clnt_perror(cl, server);
exit(1);
}
/*
* Okay, we successfully called the remote procedure.
*/
if (result->errno != 0) {
/*
* A remote system error occurred.
* Print error message and die.
*/
errno = result->errno;
perror(dir);
exit(1);
}
/*
* Successfully got a directory listing.
* Print it out.
*/
for (nl = result->readdir_res_u.list; nl != NULL;
nl = nl->next) {
printf("%s\en", nl->name);
}
exit(0);
}
最后,对于 rpcgen 协议编译器,可以将客户机程序和服务器过程作为单个程序一起测试,方法是将它们相互链接,而不是与客户机和服务器存根链接。 过程调用作为普通本地过程调用执行,并且可以使用本地调试器 (例如 dbx) 来调试程序。 当程序工作时,可以将客户机程序链接到由 rpcgen 协议编译器生成的客户机存根。 可以将服务器过程链接到 rpcgen 协议编译器生成的服务器存根。
注: 如果执行此操作,那么您可能想要注释掉对 RPC 库例程的调用,并让客户机端例程直接调用服务器例程。