本文给出一个rpcgen自定义数据结构的使用例子。
msg.x文件内容如下:
/* msg.x: Remote msg printing protocol */
struct request {
int user;
char command[32];
};
struct calendar {
int Year;
char Month;
char Day;
char Hour;
char Min;
char Sec;
};
program MESSAGEPROG {
version PRINTMESSAGEVERS {
struct calendar GETTIME(struct request*) = 1;
} = 1;
} = 0x20000001;
运行命令rpcgen msg.x生成msg_clnt.c、msg.h、msg_svc.c、msg_xdr.c四个文件。其中msg.h和msg_xdr.c是客户端和服务器共用的。msg_clnt.c是客户端代码,msg_svc.c是服务器端代码。几个文件内容分别如下:
msg_clnt.c
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include <memory.h> /* for memset */
#include "msg.h"
/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };
struct calendar *
gettime_1(struct request *argp, CLIENT *clnt)
{
static struct calendar clnt_res;
memset((char *)&clnt_res, 0, sizeof(clnt_res));
if (clnt_call (clnt, GETTIME,
(xdrproc_t) xdr_request, (caddr_t) argp,
(xdrproc_t) xdr_calendar, (caddr_t) &clnt_res,
TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&clnt_res);
}
msg.h
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#ifndef _MSG_H_RPCGEN
#define _MSG_H_RPCGEN
#include <rpc/rpc.h>
#ifdef __cplusplus
extern "C" {
#endif
struct request {
int user;
char command[32];
};
typedef struct request request;
struct calendar {
int Year;
char Month;
char Day;
char Hour;
char Min;
char Sec;
};
typedef struct calendar calendar;
#define MESSAGEPROG 0x20000001
#define PRINTMESSAGEVERS 1
#if defined(__STDC__) || defined(__cplusplus)
#define GETTIME 1
extern struct calendar * gettime_1(struct request *, CLIENT *);
extern struct calendar * gettime_1_svc(struct request *, struct svc_req *);
extern int messageprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
#else /* K&R C */
#define GETTIME 1
extern struct calendar * gettime_1();
extern struct calendar * gettime_1_svc();
extern int messageprog_1_freeresult ();
#endif /* K&R C */
/* the xdr functions */
#if defined(__STDC__) || defined(__cplusplus)
extern bool_t xdr_request (XDR *, request*);
extern bool_t xdr_calendar (XDR *, calendar*);
#else /* K&R C */
extern bool_t xdr_request ();
extern bool_t xdr_calendar ();
#endif /* K&R C */
#ifdef __cplusplus
}
#endif
#endif /* !_MSG_H_RPCGEN */
msg_svc.c
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include "msg.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
messageprog_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
union {
struct request gettime_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 GETTIME:
_xdr_argument = (xdrproc_t) xdr_request;
_xdr_result = (xdrproc_t) xdr_calendar;
local = (char *(*)(char *, struct svc_req *)) gettime_1_svc;
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 (MESSAGEPROG, PRINTMESSAGEVERS);
transp = svcudp_create(RPC_ANYSOCK);
if (transp == NULL) {
fprintf (stderr, "%s", "cannot create udp service.");
exit(1);
}
if (!svc_register(transp, MESSAGEPROG, PRINTMESSAGEVERS, messageprog_1, IPPROTO_UDP)) {
fprintf (stderr, "%s", "unable to register (MESSAGEPROG, PRINTMESSAGEVERS, 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, MESSAGEPROG, PRINTMESSAGEVERS, messageprog_1, IPPROTO_TCP)) {
fprintf (stderr, "%s", "unable to register (MESSAGEPROG, PRINTMESSAGEVERS, tcp).");
exit(1);
}
svc_run ();
fprintf (stderr, "%s", "svc_run returned");
exit (1);
/* NOTREACHED */
}
msg_xdr.c
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include "msg.h"
bool_t
xdr_request (XDR *xdrs, request *objp)
{
register int32_t *buf;
int i;
if (!xdr_int (xdrs, &objp->user))
return FALSE;
if (!xdr_vector (xdrs, (char *)objp->command, 32,
sizeof (char), (xdrproc_t) xdr_char))
return FALSE;
return TRUE;
}
bool_t
xdr_calendar (XDR *xdrs, calendar *objp)
{
register int32_t *buf;
if (!xdr_int (xdrs, &objp->Year))
return FALSE;
if (!xdr_char (xdrs, &objp->Month))
return FALSE;
if (!xdr_char (xdrs, &objp->Day))
return FALSE;
if (!xdr_char (xdrs, &objp->Hour))
return FALSE;
if (!xdr_char (xdrs, &objp->Min))
return FALSE;
if (!xdr_char (xdrs, &objp->Sec))
return FALSE;
return TRUE;
}
对于客户端,我们自己编写的代码如下client.c:
#include <stdio.h>
#include "msg.h"
#define RMACHINE "localhost" /* name of remote machine */
CLIENT *handle; /* handle for remote procedure */
//gettime_1是rpcgen自动生成的函数。这里封装一下,使我们自己调用的函数原型保持不变
struct calendar * gettime(struct request *req)
{
return gettime_1(req, handle);
}
int main(char *argc, char **argv)
{
struct request req;
struct calendar *calendar;
int ret;
req.user = 2;
sprintf(req.command, "gettime");
//create handle
handle = clnt_create(RMACHINE, MESSAGEPROG, PRINTMESSAGEVERS, "tcp");
if (handle == (CLIENT *)NULL) {
perror("clnt_create");
return 0;
}
calendar = gettime(&req);
printf("time %d-%d-%d %d:%d:%d\n", calendar->Year, calendar->Month, calendar->Day,
calendar->Hour, calendar->Min, calendar->Sec);
clnt_destroy(handle);
exit(0);
}
对于服务器端主函数的流程rpcgen已经帮我自动实现了,我们只需要实现对应客户端请求的响应函数就可以了。我们自己编写的服务器端代码如下,msg_sif.c:
/* Server-side stub inteface routines written by hand */
#include <rpc/rpc.h>
#define RPC_SVC
#include "msg.h"
struct calendar * gettime_1_svc(struct request *req, struct svc_req *rqstp)
{
static struct calendar cal;
printf("user=%d,command=%s\n", req->user, req->command);
cal.Year = 2021;
cal.Month = 8;
cal.Day = 13;
cal.Hour = 19;
cal.Min = 04;
cal.Sec = 30;
return &cal;
}
方便编译,自己写了个Makefile如下:
all:
cc msg_clnt.c msg.h client.c msg_xdr.c -o client -lnsl
cc msg_svc.c msg.h msg_xdr.c msg_sif.c -o server -lnsl
测试效果如下。先运行服务器:
./server
接着运行客户端:
$ ./client
time 2021-8-13 19:4:30
服务器端打印:
$ ./server
user=2,command=gettime