TI-RPC客户端服务端接口实例,cygwin libtirpc,
- 如何传递参数和结果?
- 绑定如何进行?
- 传输协议如何处理?
- 调用语义是什么?
- 使用什么数据表示形式?
参数传递
TI-RPC允许将单个参数从客户端传递到服务器。如果需要多个参数,则可以将这些组件组合成一个被视为单个元素的结构。从服务器传递到客户端的信息作为函数的返回值传递。无法通过参数列表将信息从服务器传递回客户端。
捆绑
客户必须知道如何联系服务。两个必要的方面是找出服务器所在的主机,然后连接到实际的服务器进程。在每个主机上,一个名为rpcbind的服务管理RPC服务。TI-RPC使用可用的主机命名服务(例如主机文件,NIS +和DNS)来查找主机。
传输协议
传输协议指定如何在客户端和服务器之间传输呼叫消息和回复消息。TS-RPC使用TCP和UDP作为传输协议,但是TI-RPC的当前版本与传输无关,因此可以与任何传输协议一起使用。
呼叫语义
调用语义定义了客户端可以对远程过程的执行承担什么;特别是该过程执行了多少次。这些语义对于处理错误条件很重要。这三种选择分别是一次,最多一次和 至少一次。ONC +提供至少一次语义。远程调用的过程是幂等的:每次调用它们都应返回相同的结果,即使经过多次迭代也是如此。
数据表示
数据表示形式描述了参数和结果在流程之间传递时所使用的格式。为了在各种系统体系结构上起作用,RPC需要标准的数据表示形式。TI-RPC使用外部数据表示(XDR)。XDR是与机器无关的数据描述和编码协议。使用XDR,RPC可以处理任意数据结构,而不管不同主机的字节顺序或结构布局约定如何。有关XDR的详细讨论,请参见附录A,XDR技术说明和附录C,XDR协议规范。
————————————-
程序,版本和过程编号
- 程序编号
- 版本号
- 程序编号
该节目号标识一组相关的远程程序,其中的每一个都有一个唯一的过程号。
一个程序可以包含一个或多个版本。每个版本都包含一组可远程调用的过程。版本号使RPC协议的多个版本可以同时可用。
每个版本都包含许多可以远程调用的过程。每个过程都有一个过程号。
程序和过程号列出了值的范围及其含义,并告诉您如何为您的RPC程序分配一个程序号。RPC网络数据库/ etc / rpc中提供了RPC服务名到程序号的映射列表。
———————————-
接口例程概述
RPC具有多个服务级别的应用程序接口。这些级别提供不同程度的控制,并以不同数量的接口代码来实现,以增加控制和复杂性的顺序。本节总结了每个级别上可用的例程。
简化的接口程序
简化的接口用于对其他计算机上的例程进行远程过程调用,并仅指定要使用的传输类型。此级别的例程用于大多数应用程序。描述和代码示例在简化接口一节中。
表2-1 RPC例程-简化级别
|
标准接口例程
标准接口分为顶层,中间层,专家层和底层。这些接口使程序员可以更好地控制通信参数,例如正在使用的传输,对错误做出响应并重新发送请求之前要等待多长时间等。
顶级例程
在顶层,接口仍然很简单,但是程序必须在拨打电话之前创建客户端句柄,或者在接收呼叫之前创建服务器句柄。如果希望应用程序在所有传输上运行,请使用此接口。您可以在“顶层接口”中找到这些例程和代码示例的用法。
表2-2 RPC例程-顶层
|
中级例程
RPC的中间级别接口使您可以控制详细信息。在这些较低级别编写的程序更加复杂,但运行效率更高。中间级别使您可以指定要使用的传输方式。中级接口描述了这些例程和代码示例的用法。
表2-3 RPC例程-中级
|
专家级例程
专家级别包含一组较大的例程,可用于指定与运输相关的参数。专家级接口描述了这些例程和代码示例的用法。
表2-4 RPC例程-专家级别
|
底层例程
底层包含用于完全控制运输选项的例程。底层接口 描述了这些例程。
表2-5 RPC例程-底层
|
———————————
标准接口
与RPC软件包的标准级别的接口可增强对RPC通信的控制。使用此控件的程序更加复杂。在这些较低级别上进行有效的编程需要对计算机网络基础知识有更多的了解。顶层,中间层,专家层和底层层是标准界面的一部分。
本节介绍如何通过使用较低级别的RPC库来控制RPC详细信息。例如,您可以选择传输协议,该协议只能通过NETPATH变量在简化的接口级别上完成 。为了使用这些例程,您应该熟悉顶层接口(TLI)。
下面显示的例程无法通过简化接口使用,因为它们需要传输句柄。例如,在简化接口上使用XDR例程进行序列化或反序列化时,无法分配和释放内存。
|
顶层介面
在顶层,应用程序可以指定要使用的传输类型,但不能指定特定的传输。此级别与简化接口的不同之处在于,应用程序在客户端和服务器中都创建了自己的传输句柄。
顶层界面的客户端
在下面的代码示例中假定头文件。
示例4-7 time_prot.h头文件
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* time_prot.h */ #include <rpc/rpc.h> #include <rpc/types.h> struct timev { int second; int minute; int hour; }; typedef struct timev timev; bool_t xdr_timev(); #define TIME_PROG 0x40000001 #define TIME_VERS 1 #define TIME_GET 1 |
以下示例显示了使用顶级服务例程的简单日期服务的客户端。传输类型被指定为程序的调用参数。
示例4-8普通日期服务的客户端
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <stdio.h> #include "time_prot.h" #define TOTAL (30) /* * Caller of trivial date service * usage: calltime hostname */ main(argc, argv) int argc; char *argv[]; { struct timeval time_out; CLIENT *client; enum clnt_stat stat; struct timev timev; char *nettype; if (argc != 2 && argc != 3) { fprintf(stderr,”usage:%s host[nettype]\n” ,argv[0]); exit(1); } if (argc == 2) nettype = "netpath"; /* Default */ else nettype = argv[2]; client = clnt_create(argv[1], TIME_PROG, TIME_VERS, nettype); if (client == (CLIENT *) NULL) { clnt_pcreateerror(“Couldn't create client”); exit(1); } time_out.tv_sec = TOTAL; time_out.tv_usec = 0; stat = clnt_call( client, TIME_GET, xdr_void, (caddr_t)NULL, xdr_timev, (caddr_t)&timev, time_out); if (stat != RPC_SUCCESS) { clnt_perror(client, "Call failed"); exit(1); } fprintf(stderr,"%s: %02d:%02d:%02d GMT\n", nettype timev.hour, timev.minute, timev.second); (void) clnt_destroy(client); exit(0); } |
如果NETTYPE在程序的调用未指定,则该字符串NETPATH被取代。当RPC库例程遇到此字符串时,NETPATH环境变量的值将控制传输选择。
如果无法创建客户端句柄,请使用clnt_pcreateerror()显示失败的原因。您还可以通过读取全局变量rpc_createerr的内容来获取错误状态。
创建客户端句柄后,clnt_call()用于进行远程调用。它的参数是远程过程号,用于输入参数的XDR过滤器,参数指针,用于结果的XDR过滤器,结果指针以及调用的超时时间。该程序没有参数,因此指定了xdr_void()。通过调用clnt_destroy()进行清理 。
到结合在前面的例子中允许客户创建句柄到30秒的时间,更换调用clnt_create()与对呼叫clnt_create_timed()作为显示在下面的代码段:
0 1 2 3 4 5 |
struct timeval timeout; timeout.tv_sec = 30; /* 30 seconds */ timeout.tv_usec = 0; client = clnt_create_timed(argv[1], TIME_PROG, TIME_VERS, nettype, &timeout); |
示例4-9用于简单日期服务的服务器
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#include <stdio.h> #include <rpc/rpc.h> #include "time_prot.h" static void time_prog(); main(argc,argv) int argc; char *argv[]; { int transpnum; char *nettype; if (argc > 2) { fprintf(stderr, "usage: %s [nettype]\n", argv[0]); exit(1); } if (argc == 2) nettype = argv[1]; else nettype = "netpath"; /* Default */ transpnum = svc_create(time_prog,TIME_PROG,TIME_VERS,nettype); if (transpnum == 0) { fprintf(stderr,”%s: cannot create %s service.\n”, argv[0], nettype); exit(1); } svc_run(); } /* * The server dispatch function */ static void time_prog(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { struct timev rslt; time_t thetime; switch(rqstp->rq_proc) { case NULLPROC: svc_sendreply(transp, xdr_void, NULL); return; case TIME_GET: break; default: svcerr_noproc(transp); return; } thetime = time((time_t *) 0); rslt.second = thetime % 60; thetime /= 60; rslt.minute = thetime % 60; thetime /= 60; rslt.hour = thetime % 24; if (!svc_sendreply( transp, xdr_timev, &rslt)) { svcerr_systemerr(transp); } } |
svc_create()返回其创建服务器处理的传输数。 time_prog()是由被称为服务功能()svc_run当一个请求指定其程序和版本号。服务器通过svc_sendreply()将结果返回给客户端。
当您使用rpcgen生成分派函数时,该过程返回后将调用svc_sendreply()。因此,在本示例中,必须在实际过程中将rslt声明为 静态。svc_sendreply()是从调度函数内部调用的,因此rslt不声明为static。
在此示例中,远程过程不接受任何参数。当必须传递参数时,下面列出的调用将获取,反序列化(XDR解码)并释放参数。
0 1 |
svc_getargs( SVCXPRT_handle, XDR_filter, argument_pointer); svc_freeargs( SVCXPRT_handle, XDR_filter argument_pointer ); |
中级界面
中间层接口的客户端
以下示例显示了顶层接口中的时间服务的客户端 ,它是在RPC的中间层编写的。在此示例中,用户必须在命令行上命名进行呼叫的传输方式。
示例4-10中级时间服务客户端
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <stdio.h> #include <rpc/rpc.h> #include <netconfig.h> /* For netconfig structure */ #include "time_prot.h" #define TOTAL (30) main(argc,argv) int argc; char *argv[]; { CLIENT *client; struct netconfig *nconf; char *netid; /* Declarations from previous example */ if (argc != 3) { fprintf(stderr, "usage: %s host netid\n”, argv[0]); exit(1); } netid = argv[2]; if ((nconf = getnetconfigent( netid)) == (struct netconfig *) NULL) { fprintf(stderr, "Bad netid type: %s\n", netid); exit(1); } client = clnt_tp_create(argv[1], TIME_PROG, TIME_VERS, nconf); if (client == (CLIENT *) NULL) { clnt_pcreateerror("Could not create client"); exit(1); } freenetconfigent(nconf); /* Same as previous example after this point */ } |
在此示例中,netconfig结构是通过调用 getnetconfigent(netid)获得的。有关更多详细信息,请参见getnetconfig(3NSL)手册页和《编程接口指南》。在此级别上,程序将明确选择网络。
到结合在前面的例子中允许客户创建句柄到30秒的时间,更换调用clnt_tp_create()与对呼叫clnt_tp_create_timed()作为显示在下面的代码段:
0 1 2 3 4 5 6 |
struct timeval timeout; timeout.tv_sec = 30; /* 30 seconds */ timeout.tv_usec = 0; client = clnt_tp_create_timed(argv[1], TIME_PROG, TIME_VERS, nconf, &timeout); |
中级接口的服务器端
以下示例显示了相应的服务器。启动服务的命令行必须指定通过其提供服务的传输方式。
示例4-11中级时间服务服务器
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
/* * This program supplies Greenwich mean * time to the client that invokes it. * The call format is: server netid */ #include <stdio.h> #include <rpc/rpc.h> #include <netconfig.h> /* For netconfig structure */ #include "time_prot.h" static void time_prog(); main(argc, argv) int argc; char *argv[]; { SVCXPRT *transp; struct netconfig *nconf; if (argc != 2) { fprintf(stderr, "usage: %s netid\n", argv[0]); exit(1); } if ((nconf = getnetconfigent( argv[1])) == (struct netconfig *) NULL) { fprintf(stderr, "Could not find info on %s\n", argv[1]); exit(1); } transp = svc_tp_create(time_prog, TIME_PROG, TIME_VERS, nconf); if (transp == (SVCXPRT *) NULL) { fprintf(stderr, "%s: cannot create %s service\n", argv[0], argv[1]); exit(1) } freenetconfigent(nconf); svc_run(); } static void time_prog(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { /* Code identical to Top Level version */ |
专家级界面
在专家级别,网络选择与在中间级别相同。唯一的区别是应用程序对CLIENT和 SVCXPRT句柄的详细信息的控制级别有所提高。这些示例说明了使用clnt_tli_create()和svc_tli_create()例程执行的此控件 。有关TLI的更多信息,请参见《编程接口指南》。
专家级界面的客户端
示例4-12显示了clntudp_create()的版本,该版本是使用clnt_tli_create()进行UDP传输的客户端创建例程。该示例显示了如何根据所选传输类型来选择网络。clnt_tli_create()用于创建客户端句柄并执行以下操作:
示例4-12 RPC较低级别的客户端
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
#include <stdio.h> #include <rpc/rpc.h> #include <netconfig.h> #include <netinet/in.h> /* * In earlier implementations of RPC, * only TCP/IP and UDP/IP were supported. * This version of clntudp_create() * is based on TLI/Streams. */ CLIENT * clntudp_create(raddr, prog, vers, wait, sockp) struct sockaddr_in *raddr; /* Remote address */ rpcprog_t prog; /* Program number */ prcvers_t vers; /* Version number */ struct timeval wait; /* Time to wait */ int *sockp; /* fd pointer */ { CLIENT *cl; /* Client handle */ int madefd = FALSE; /* Is fd opened here */ int fd = *sockp; /* TLI fd */ struct t_bind *tbind; /* bind address */ struct netconfig *nconf; /* netconfig structure */ void *handlep; if ((handlep = setnetconfig() ) == (void *) NULL) { /* Error starting network configuration */ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return((CLIENT *) NULL); } /* * Try all the transports until it gets one that is * connectionless, family is INET, and preferred name is UDP */ while (nconf = getnetconfig( handlep)) { if ((nconf->nc_semantics == NC_TPI_CLTS) && (strcmp( nconf->nc_protofmly, NC_INET ) == 0) && (strcmp( nconf->nc_proto, NC_UDP ) == 0)) break; } if (nconf == (struct netconfig *) NULL) rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; goto err; } if (fd == RPC_ANYFD) { fd = t_open(nconf->nc_device, O_RDWR, &tinfo); if (fd == -1) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; goto err; } } if (raddr->sin_port == 0) { /* remote addr unknown */ u_short sport; /* * rpcb_getport() is a user-provided routine that calls * rpcb_getaddr and translates the netbuf address to port * number in host byte order. */ sport = rpcb_getport(raddr, prog, vers, nconf); if (sport == 0) { rpc_createerr.cf_stat = RPC_PROGUNAVAIL; goto err; } raddr->sin_port = htons(sport); } /* Transform sockaddr_in to netbuf */ tbind = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR); if (tbind == (struct t_bind *) NULL) rpc_createerr.cf_stat = RPC_SYSTEMERROR; goto err; } if (t_bind->addr.maxlen < sizeof( struct sockaddr_in)) goto err; (void) memcpy( tbind->addr.buf, (char *)raddr, sizeof(struct sockaddr_in)); tbind->addr.len = sizeof(struct sockaddr_in); /* Bind fd */ if (t_bind( fd, NULL, NULL) == -1) { rpc_createerr.ct_stat = RPC_TLIERROR; goto err; } cl = clnt_tli_create(fd, nconf, &(tbind->addr), prog, vers, tinfo.tsdu, tinfo.tsdu); /* Close the netconfig file */ (void) endnetconfig( handlep); (void) t_free((char *) tbind, T_BIND); if (cl) { *sockp = fd; if (madefd == TRUE) { /* fd should be closed while destroying the handle */ (void)clnt_control(cl,CLSET_FD_CLOSE, (char *)NULL); } /* Set the retry time */ (void) clnt_control( l, CLSET_RETRY_TIMEOUT, (char *) &wait); return(cl); } err: if (madefd == TRUE) (void) t_close(fd); (void) endnetconfig(handlep); return((CLIENT *) NULL); } |
使用setnetconfig(),getnetconfig()和endnetconfig()选择网络。endnetconfig()不被调用,直到调用后clnt_tli_create(),附近的例子的端部。
可以将clntudp_create()传递给打开的TLI fd。如果未传递任何内容(fd == RPC_ANYFD), clntudp_create()将使用UDP的netconfig结构打开自己的名称,以查找要传递给t_open()的设备的名称。
如果远程地址未知(raddr-> sin_port == 0),则从远程rpcbind获取。
创建客户端句柄后,可以使用对clnt_control()的调用对其进行修改。RPC库在销毁句柄时会关闭文件描述符,就像打开fd本身时调用clnt_destroy()一样。然后,RPC库设置重试超时期限。
专家级界面的服务器端
例4-13显示了例4-12的服务器端。它称为svcudp_create()。服务器端使用svc_tli_create()。
svc_tli_create()在应用程序需要精细控制时使用,尤其是在:
- 将打开的文件描述符传递给应用程序。
- 传递用户的绑定地址。
- 设置发送和接收缓冲区的大小。在FD传入时参数可以是自由的。然后将其绑定到一个给定的地址,地址存储在一个手柄。如果绑定地址设置为NULL,并且fd最初未绑定,则它将绑定到任何合适的地址。
使用rpcb_set()向rpcbind注册服务。
示例4-13 RPC较低级别的服务器
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#include <stdio.h> #include <rpc/rpc.h> #include <netconfig.h> #include <netinet/in.h> SVCXPRT * svcudp_create(fd) register int fd; { struct netconfig *nconf; SVCXPRT *svc; int madefd = FALSE; int port; void *handlep; struct t_info tinfo; /* If no transports available */ if ((handlep = setnetconfig() ) == (void *) NULL) { nc_perror("server"); return((SVCXPRT *) NULL); } /* * Try all the transports until it gets one which is * connectionless, family is INET and, name is UDP */ while (nconf = getnetconfig( handlep)) { if ((nconf->nc_semantics == NC_TPI_CLTS) && (strcmp( nconf->nc_protofmly, NC_INET) == 0 )&& (strcmp( nconf->nc_proto, NC_UDP) == 0 )) break; } if (nconf == (struct netconfig *) NULL) { endnetconfig(handlep); return((SVCXPRT *) NULL); } if (fd == RPC_ANYFD) { fd = t_open(nconf->nc_device, O_RDWR, &tinfo); if (fd == -1) { (void) endnetconfig(); return((SVCXPRT *) NULL); } madefd = TRUE; } else t_getinfo(fd, &tinfo); svc = svc_tli_create(fd, nconf, (struct t_bind *) NULL, tinfo.tsdu, tinfo.tsdu); (void) endnetconfig(handlep); if (svc == (SVCXPRT *) NULL) { if (madefd) (void) t_close(fd); return((SVCXPRT *)NULL); } return (svc); } |
此处的网络选择类似于clntudp_create()来完成。文件描述符未显式绑定到传输地址,因为svc_tli_create()会这样做。
svcudp_create()可以使用打开的fd。如果没有提供,它将使用选定的netconfig结构打开一个自身。
底层界面
RPC的底层接口使应用程序可以控制所有选项。 clnt_tli_create()和其他专家级RPC接口例程均基于这些例程。您很少使用这些低级例程。
底层例程创建内部数据结构,缓冲区管理,RPC头等。这些例程的调用者(例如专家级例程clnt_tli_create())必须在客户端句柄中初始化cl_netid和cl_tp字段。对于创建的句柄,cl_netid是传输的网络标识符(例如udp),而cl_tp是该传输的设备名称(例如/ dev / udp)。例程clnt_dg_create()和clnt_vc_create()设置clnt_ops和cl_private字段。
底层接口的客户端
以下代码示例显示对clnt_vc_create()和clnt_dg_create()的调用。
示例4-14底层客户
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* * variables are: * cl: CLIENT * * tinfo: struct t_info returned from either t_open or t_getinfo * svcaddr: struct netbuf * */ switch(tinfo.servtype) { case T_COTS: case T_COTS_ORD: cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz); break; case T_CLTS: cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz); break; default: goto err; } |
这些例程要求打开并绑定文件描述符。svcaddr 是服务器的地址。
底层接口的服务器端
以下代码示例是创建底层服务器的示例。
示例4-15底层服务器
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* * variables are: * xprt: SVCXPRT * */ switch(tinfo.servtype) { case T_COTS_ORD: case T_COTS: xprt = svc_vc_create(fd, sendsz, recvsz); break; case T_CLTS: xprt = svc_dg_create(fd, sendsz, recvsz); break; default: goto err; } |
服务器缓存
svc_dg_enablecache()启动数据报传输的服务缓存。仅在服务器过程是“仅一次”操作类型的情况下才应使用缓存。多次执行缓存的服务器过程会产生不同的结果。
0 1 2 |
svc_dg_enablecache(xprt, cache_size) SVCXPRT *xprt; unsigned int cache_size; |
此函数为服务端点xprt分配一个重复的请求缓存,该缓存足够大以容纳cache_size条目。如果服务包含结果不同的过程,则需要重复的请求缓存。启用缓存后,将无法禁用它。
低层数据结构
第一个结构是在<rpc / clnt.h>中定义的客户端RPC句柄。低级实现必须为每个连接提供并初始化一个句柄,如以下代码示例所示。
示例4-16 RPC客户端句柄结构
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
typedef struct { AUTH *cl_auth; /* 身份验证者 */ struct clnt_ops { enum clnt_stat (*cl_call)(); /* 调用远程过程 */ void (*cl_abort)(); /* 中止通话 */ void (*cl_geterr)(); /* 获取特定的错误代码 */ bool_t (*cl_freeres)(); /* 释放结果 */ void (*cl_destroy)(); /* 销毁此结构 */ bool_t (*cl_control)(); /* rpc的 ioctl() */ } *cl_ops; caddrt_t cl_private; /* private stuff 私人物品 */ char *cl_netid; /* network token 网络令牌 */ char *cl_tp; /* device name 设备名称 */ } CLIENT; |
客户端句柄的第一个字段是在<rpc / auth.h>中定义的身份验证结构。默认情况下,此字段设置为AUTH_NONE。客户端程序必须适当地初始化cl_auth,如以下代码示例所示。
示例4-17客户端身份验证句柄
0 1 2 3 4 5 6 7 8 9 10 11 12 |
typedef struct { struct opaque_auth ah_cred; struct opaque_auth ah_verf; union des_block ah_key; struct auth_ops { void (*ah_nextverf)(); int (*ah_marshal)(); /* nextverf & serialize */ int (*ah_validate)(); /* validate varifier */ int (*ah_refresh)(); /* 刷新凭据 */ void (*ah_destroy)(); /* 销毁此结构 */ } *ah_ops; caddr_t ah_private; } AUTH; |
在AUTH结构中,ah_cred包含调用方的凭据,而ah_verf包含用于验证凭据的数据。有关详细信息,请参见身份验证。
示例4-18服务器传输句柄
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
typedef struct { int xp_fd; #define xp_sock xp_fd u_short xp_port; /* 关联的端口号 (已废弃) */ struct xp_ops { bool_t (*xp_recv)(); /* 接收传入请求 */ enum xprt_stat (*xp_stat)(); /* 获取传输状态 */ bool_t (*xp_getargs)(); /* 获取参数 */ bool_t (*xp_reply)(); /* 发送回复 */ bool_t (*xp_freeargs)(); /* 为args免费分配内存 */ void (*xp_destroy)(); /* 销毁此结构 */ } *xp_ops; int xp_addrlen; /* 远程地址的长度 Obsolete */ char *xp_tp; /* 提供传输程序的设备名称 */ char *xp_netid; /* network token 网络令牌 */ struct netbuf xp_ltaddr; /* 本地传输地址 */ struct netbuf xp_rtaddr; /* 远程传输地址 */ char xp_raddr[16]; /* 远程地址 Now obsoleted */ struct opaque_auth xp_verf; /* 原始响应验证程序 */ caddr_t xp_p1; /* 专用:供svc ops使用 */ caddr_t xp_p2; /* 专用:供svc ops使用 */ caddr_t xp_p3; /* 专用:供svc lib使用 */ } SVCXPRT; |
下表显示了服务器传输句柄的字段。
|
其余字段由底层服务器例程svc_dg_create()和svc_vc_create()初始化 。
对于面向连接的端点,在请求并接受服务器连接之前,以下字段无效:
|