rpcgen生成文件解读

 

对于rpc说明书文件test.x,其中定义服务器过程以及他们的参数和结果。

使用rpcgen必须要生成的几个文件:

 

rpcgen –C  -M test.x  //-C生成ANSI C的代码, -M

test.h:过程及其参数的说明

tes_xdr.c: 用于rpc外部数据表示

test_clnt.c :客户端存根

test_svc.c 服务器存根

 

对于说明书文件test.x(至于说明书文件能放那些东西,用什么格式表示,这里不做介绍)

 

const NAMELEN     =  256;  

typedef string PATHNAME<NAMELEN>;

const MAXCHUNK =  256;  

 

struct test_in

{

         PATHNAME pathname;

         uint32_t mode;

         uint64_t mtime;

};

 

struct test_out

{

         int res;

         uint64_t filesize;

         int isvalide;

         uint64_t mtime;

         chunkinfo chunks<MAXCHUNK>;

};

 

program CS  //程序号

{

         version CS_VERS  //版本号

         {

                   struct  test_out cs_open(struct test_in) = 1;  //过程号

         } = 2;

} = 0x31230000;

 

  

说明书文件内容与C头文件的对应关系

 

 

 

test.h分析

#ifdef __cplusplus 

extern "C" {  //ANSI C

#endif

 

#define NAMELEN 256

 

typedef char *PATHNAME;

#define MAXCHUNK 256

 

//说明书中结构的定义

struct test_in {

         PATHNAME pathname;

         uint32_t mode;

         uint64_t mtime;

};

typedef struct test_in test_in;

 

struct test_out {

         int res;

         uint64_t filesize;

         int isvalide;

         uint64_t mtime;

         struct {

                   u_int chunks_len;

                   chunkinfo *chunks_val;

         } chunks;

};

typedef struct test_out test_out;

 

 

// 对应说明书中的程序号和版本号

#define CS 0x31230000

#define CS_VERS 2

 

#if defined(__STDC__) || defined(__cplusplus)

#define cs_open 1    //方法号

//客户端,服务器过程的声明

extern  enum clnt_stat cs_open_2(struct test_in *, struct test_out *, CLIENT *);

extern  bool_t cs_open_2_svc(struct test_in *, struct test_out *, struct svc_req *);

extern int cs_2_freeresult (SVCXPRT *, xdrproc_t, caddr_t); //释放解码参数时分配的空间

……

 

 

 

test_xdr.c分析

bool_t

xdr_PATHNAME (XDR *xdrs, PATHNAME *objp) //C的数据转换成XDR格式

{

         register int32_t *buf;

    // rpc提供一系列方法如xdr_int(), xdr_string()用来编码C语言的基本类型

          if (!xdr_string (xdrs, objp, NAMELEN))

                    return FALSE;

         return TRUE;

}

 

bool_t

xdr_test_in (XDR *xdrs, test_in *objp)  //对于结构,将每个数据段进行逐一转换

{

         register int32_t *buf;

 

          if (!xdr_PATHNAME (xdrs, &objp->pathname))

                    return FALSE;

          if (!xdr_uint32_t (xdrs, &objp->mode))

                    return FALSE;

          if (!xdr_uint64_t (xdrs, &objp->mtime))

                    return FALSE;

         return TRUE;

 

 

XDR的编码格式如下:(注意charshort都是四字节的数据)

 

 

 

XDR流对象的操作

void xdrmem_create(XDR *xdrs, char *addr, unsigned int size, enum xdr_op op);

根据选项(XDR_ENCODE, XDR_DECODE, XDR_FREE),往xdrs流对象中(写,读,释放)数据,addr为流的缓冲区首地址,size为缓冲区长度(流中所有的数据不能超过这个长度)。

       

test_clnt.c分析

 

/* Default timeout can be changed using clnt_control() */

static struct timeval TIMEOUT = { 25, 0 };  //超时重连时间

 

enum clnt_stat

cs_open_2(struct test_in *argp, struct test_out *clnt_res, CLIENT *clnt)

{

    // 使用clnt_call调用客户端函数,需先将数据转化为xdr格式(xdr_test_in, xdr_test_out)作为参数传到clnt_call, 

 

         return (clnt_call(clnt, cs_open,

                   (xdrproc_t) xdr_test_in, (caddr_t) argp,

                   (xdrproc_t) xdr_test_out, (caddr_t) clnt_res,

                   TIMEOUT));

}

 

/*

     从客户端调用的实现上可以看出,输入参数,输出参数的空间必须提前分配好(包括结构中指针指向的空间 ,如test_outchunks的数据空间必须在调用前分配好,否则rpc调用返回后往该地址上写数据会引发段错误

*/

wcw师兄blog(http://blog.chinaunix.net/u1/37472/showart_1006758.html)上分析rpc输入,输出参数的文章讲的比较好,我在dnfs上也测试过了,用于rpc服务器端解码结果分配的数据空间,不能在过程中显示free,如果free掉,这些空间无法被sendreply传送给客户端,释放的工作交由RPC服务器端需要显式定义的额外方法(对于加了-M参数的)中的xdr_free完成,故如果没有加上该过程,会造成内存泄露。

int cs_2_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
{
     xdr_free(xdr_result, result);
     return 1;
}

如果在服务器调用方法内就free为输出参数分配空间,则server会挂掉,提示内容为double free。

 

 

 

test_svc.c分析

 

……

static void

cs_2(struct svc_req *rqstp, register SVCXPRT *transp)

{

    // 共用体存放输入参数及结构,根据不同的调用号,设置不同的值

         union {

                   struct test_in cs_open_2_arg;

         } argument;  

   /* 故在服务器端的输入输出参数的空间不用自己分配,但如果参数中包含指针,指针指向的空间需要实现分配好,rpc只在站上定义了参数结构,参数中只分配了一个指针变量的空间,如果不事先分配好空间,而往上面写数据,就会造成段错误 */

         union {

                   struct test_out cs_open_2_res;

         } result;

         bool_t retval;

    // 输入,输出参数转换方法

         xdrproc_t _xdr_argument, _xdr_result;

    // 调用的方法

         bool_t (*local)(char *, void *, struct svc_req *);

 

    // 根据调用方法号判断

         switch (rqstp->rq_proc) {

         case NULLPROC:

                   (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);

                   return;

    // 根据不同的调用,设置xdr方法,过程调用方法

         case cs_open:

                   _xdr_argument = (xdrproc_t) xdr_test_in;

                   _xdr_result = (xdrproc_t) xdr_test_out;

                   local = (bool_t (*) (char *, void *,  struct svc_req *))cs_open_2_svc;

                   break;

 

         default:

                   svcerr_noproc (transp);

                   return;

         }

    //解码输入的参数 svc_getargs

         memset ((char *)&argument, 0, sizeof (argument));

         if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {

                   svcerr_decode (transp);

                   return;

         }

   

    // 调用相应的方法

         retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp);

     //发送相应结果给客户端

         if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result)) {

                   svcerr_systemerr (transp);

         }

 

    // 释放使用svc_getargs解码xdr数据时分配的空间

         if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {

                   fprintf (stderr, "%s", "unable to free arguments");

                   exit (1);

         }

         if (!cs_2_freeresult (transp, _xdr_result, (caddr_t) &result))

                   fprintf (stderr, "%s", "unable to free results");

 

         return;

}

 

int

main (int argc, char **argv)  //服务器例程启动入口

{

         register SVCXPRT *transp;

 

         pmap_unset (CS, CS_VERS);

    //建立一个基于udp/ip的服务传输点

         transp = svcudp_create(RPC_ANYSOCK);

         if (transp == NULL) {

                   fprintf (stderr, "%s", "cannot create udp service.");

                   exit(1);

         }

    //将程序号,版本号与服务传输点关联

         if (!svc_register(transp, CS, CS_VERS, cs_2, IPPROTO_UDP)) {

                   fprintf (stderr, "%s", "unable to register (CS, CS_VERS, udp).");

                   exit(1);

         }

   

//建立一个基于tcp/ip的服务传输点

         transp = svctcp_create(RPC_ANYSOCK, 0, 0);

         if (transp == NULL) {

                   fprintf (stderr, "%s", "cannot create tcp service.");

                   exit(1);

         }

         if (!svc_register(transp, CS, CS_VERS, cs_2, IPPROTO_TCP)) {

                   fprintf (stderr, "%s", "unable to register (CS, CS_VERS, tcp).");

                   exit(1);

         }

 

         svc_run ();   //等待客户端请求

         fprintf (stderr, "%s", "svc_run returned");

         exit (1);

         /* NOTREACHED */

}

posted @ 2013-04-19 14:05  ydzhang  阅读(613)  评论(0编辑  收藏  举报