📄 courier.tbl.me
字号:
c cl l.File ContentsExample1_defs.h scope widening macros (includes Example1.h)Example1.h definitions, typedefs, and constant declarationsExample1_support.c routines to map between C and CourierExample1_server.c server main program and support routinesExample1_client.c client routines to support applications calling Example.TE.)b.ppThe header file.i "Example1_defs.h"should be included via.sp #include ``Example1_defs.h''.spin all user-written parts of the Courier program(i.e., the implementations of the client program and server procedures.).ppThe .i "\fB-I\fIinclude-directory"switch may be used on the command line of.i xnscourierto establish a search path for Courier specification files named in aDEPENDS UPON clause. By default, the compiler looks first in the currentdirectory followed by the courier-description-file named in the .i /etc/Courierservicesfile..sh 2 "Calling Remote Procedures \- Client Implementation".ppA remote procedure appears to the C programmer to look almost likea local procedure (although it typically makes use of resources notavailable on the local machine or else runs many times slowerthan would a local procedure). A C client program (i.e. the callerof a remote procedure) actually calls a stub function generated bythe courier compiler, which in turn executes the code necessary toinvoke the corresponding procedure on the remote machine. .ppBefore calling the remote procedure it is necessary to establish aCourier SPP connection (in the process binding a local socket toa remote machine). This may be done by the calling the functionCourierOpen(), passing it as argument a structure identifying the remotehost. This structureis of type ``ns_addr''as defined in the include file.i "#include <netns/ns.h>" .CourierOpen() returns an anonymous pointer(of type CourierConnection*) to a structure containingthe socket to be used for this courier call, which should be passedto each remote procedure to be executed on the particular remote host.CourierOpen() returns NULL if there is a problem opening an SPP connectionto the host specified..ppFor low-level access to ns_addr structures,the procedure getXNSaddr() is available, taking a string in the form``network#a1.a2.a3.a4.a5.a6#socket'' (where all numbers are hex) or ``networkhigh-networklow#d1-d2-d3-d4#socket'' (where all numbers are3-digit decimal numbers)and returning a pointer to thecorresponding ns_addr structure. This result may then be passed as theargument to CourierOpen().More normally, the user will have a string in the form of an NS address,e.g. ``jqj:computer\ science:cornell-univ''. This string can becoerced into a Clearinghouse ObjectName, then used in a Clearinghouse-basedlookup to find the corresponding address:.sp 1.nf Clearinghouse2_ObjectName hobj, hdefault; struct ns_addr * host; char * string; CH_NameDefault(&hdefault); /* get defaults */ hobj = CH_StringToName(string, &hdefault); host = CH_LookupAddr(hobj, 0); .fi.sp 1.ppThe SPP connection may be reused by multiple RPCs destined for the samehost, even if the procedures are part of different remote programs. However,the SPP connection may not be reused while an RPC is outstanding; thus,if you need to issue a Courier call during a BDT transfer, you will needto obtain a second CourierConnection..ppThe client then calls the compiler-generated stub function to invokethe remote procedure. This stubfunction takes at least 2 arguments:.ip (1)The pointer returned from CourierOpen().If the SPP connection does notcurrently exist (i.e. has been closed by the server)it is reopened for this call..ip (2)A pointer to a user-supplied function which will be used if a Bulk DataTransfer is required (see the section on BDT below); if this remoteprocedure does not involve Bulk Data Transfer, then the function pointershould be 0 or NULL..ip (3...)One additional argument corresponding to each parameter in the Courierlanguage description of this procedure..ppThe remote procedure returns in one of 2 ways: either it returnsnormally, passing back a structure corresponding to the RETURNS parametersspecified in the procedure description, or it signals an error whichmay be caught using the DURING ... HANDLER mechanism described in.i except(1) ..ppA Courier procedure is allowed to return multiple results, a language featurenot easily mapped into C. To provide this functionality, all UNIXcourier remote procedures actually return a structurecontaining the procedure results. Thus, for a procedure namedFoo, declared.sp 1 Foo: PROCEDURE [ ] RETURNS [str: STRING];.sp 1the C function Foo would return a structure of type FooResults, declared.sp 1.nf typedef struct { String str; } FooResults;.fi.fi.ppInstead of returning a value of type determined by the RESULTS clauseof a PROCEDURE declaration, a Courier remote procedure call may returna reject message indicating the remote system's inability to evenattempt a remote operation, or an abort message raising a remote error,i.e. reporting the operation's failure. Reject and abort messages arehandled by a signalling mechanism or if uncaught cause termination ofthe client program. For example, a client calling the remote proceduredefined by.sp 1.nf NoSuchFile: ERROR = 0; OtherError: ERROR [ errorstring: STRING ] = 1; Example: PROCEDURE [ ] REPORTS [ NoSuchFile, OtherError ];.fi.sp 1might be coded as.sp 1.nf conn = CourierOpen(destaddr); DURING Example(conn,NULL) HANDLER switch(Exception.Code) { case REJECT_ERROR: fprintf(stderr,"Remote reject.\en"); exit(1); case NoSuchFile: fprintf(stderr,"No such file\en"); break; case OtherError: fprintf(stderr,"Remote error %s\en", CourierErrArgs(OtherErrorArgs,errorstring) ); break; case PROTOCOL_VIOLATION: case INTERNAL_ERROR: fprintf(stderr,"Local internal error %s\en", Exception.Message); break; default: fprintf(stderr,"Unknown error, type %d\en", Exception.Code); } END_HANDLER.fi.sp 1The error value, Exception.code, will be one of (1) ``REJECT_ERROR'',which indicates a REJECT message from the remote server, (2) a program-definedERROR value (offset by ERROR_OFFSET), (3) or ``INTERNAL_ERROR'' or ``PROTOCOL_VIOLATION''indicatinga serious internal error in the Unix courier implementation; in the thirdcase, a string is available as Exception.Message.Both errors and rejections may have arguments, which can be accessedwithin a handler as a struct pointed to by Exception.Message. Forprogrammer convenience a macro, CourierErrArgs, is defined, takingas arguments the typedef defining the current error's arguments and the name of the argument to be accessed; this macro may be usedonly within an error handler.For rejection messages, the type should be ``rejectionDetails,''as defined on page 25 of the Courier spec..ppWhen done with a particular remote connection, the client should callCourierClose() with argument the pointer returned from CourierOpen()to free the socket and cleanly close the connection..ppThe client program should be loaded with .i "Example1_client.o" ,.i "Example1_support.o" ,and -lcourier (the XNS Courier library)to produce an executable program..sh 2 "Writing a Courier Server".ppThe Courier protocol specifies a standard for communicatingparameters and results which has been adhered to in thisimplementation.It also specifies an initial connection protocoland a format for messages..ppThere is a single Courier Daemon per UNIX machine whose functionis to listen on a well-known XNS port for Courier interface activationrequests.A request contains the number of the Courier programwhose server is to be activated, and its version number. These twonumbers are used as a key in the file .i /etc/Courierservicesto identifythe executable file associated with the particular courier program..i /etc/Courierservicesconsists of a sequence of lines of the form.sp 1 program-name program-number version courier-description-file server-binary-file.sp 1The daemoneither spawns the executable fileor replies with a reject message. Note that each incoming remote procedurecall may be serviced by a separate UNIX process.This implies that connection-oriented services (services which consist of asequence of related procedure calls) must be implemented by maintainingthe state of connections in a file..ppThe executable file containing the server for a particular remoteprogram consists of a main program generated by the courier compilerwhich interprets CALL messages from the remote client and callsone or more user-written functions to implement thecourier procedures..ppGiven a courier specification for the remote program Example containingremote procedure Example,the server functions (including the C function Example and any supportroutines needed)should be loaded with .i "Example1_server.o" ,.i "Example1_support.o" ,and -lcourierto produce the server process that will be invoked wheneveran activation request arrives..ppA server function receives an argument list similar to that passed bya client to a remote procedure. The first two arguments in theparameter list should be ignored; each succeeding argument correspondsto one parameter in the courier declaration..ppTo return from a server function you must return a structure containingthe results of the procedure. If this structure contains pointers (e.g.Strings) you should insure that the data pointed to is allocatedstaticly or on the heap rather than on the stack.You must not call exit(3) from within the server function; doing so willcause the client (remote caller) to hang indefinitely waiting for areply..ppTo abort a server function, returning an error code, use the``raise(code, msg)''library function, with arguments the error code and either NULL (if theerror takes no arguments) or a pointer to a structure containing thearguments to the error. Raise causes a longjump out of the functiondirectly to the handler, and thence to the remote procedure caller.For example, to return the OtherError message defined above one might code.sp 1.nf OtherErrorArg randomerr; . . . randomerr.errorstring = sys_errlist[errno]); raise(OtherError,(char*) &randomerr);.fi.sp 1.ppWhen a server function returns to its main program caller the main()sends the appropriate RETURN or ABORT message to the remote client,and does an exit(0). The server then holds the connection for up to 90 seconds waiting for another Courier call to arrive on the same SPPstream..sh 2 "Dynamic Allocation".ppOne additional .i caveat is necessary when using Courier remote procedure calls from C: C does nothave garbage collection, so you should free any dynamically allocated storagewhen you are done with it. In general, top level Courier objects arealways allocated from the stack or statically; however, strings and sequenceswithin an object are generally allocated from the heap using malloc()..ppA client program which calls the remote procedure Foo defined above,returning a STRING,actually has returned to it a C struct containing a pointer to an arrayof malloced characters. When done with this string, the program may freeit by calling clear_FooResults() with argument the address of theresult returned by the call to Foo. Thus:.sp 1.nf FooResults result; . . . result = Foo(conn,NULL); . . . printf("result was %s\n",result.str); clear_FooResults(&result);.fi.sp 1.sh 1 "Using Bulk Data Transfer".pp.sh 2 "Overview".ppWhen a Courier program needs to transfer an arbitrary amount ofinformation as an argument or result of a Courier procedure, theprocedure is usually defined to have an argument of type ``BulkData.Sink''or ``BulkData.Source'' (and a ``DEPENDS UPON BulkData''is included in the program).The argument is a ``source'' if it is information transferred from callerto server (as though a procedure argument), a ``sink'' if it isinformation transferred from server to caller (as though a procedureresult). In thisimplementation, a Courier procedure may have at most one such argument,which must have the value null or immediate.In a Courier call, the bulk data is transmitted in a special way,multiplexed into the same data path as control messagesbetween the arguments and the results..ppThe BDT user should include in the .i ".cr"file a ``DEPENDS UPON BulkData (0) VERSION 1'', then reference thesearguments as BulkData.Source and BulkData.Sink in PROCEDUREdeclarations.The declaration of a Courier PROCEDURE can then indicate a bulkdata transfer by including in its formal argument list a variableof type BulkData.Source or BulkData.Sinkas appropriate.The corresponding parameter to the C procedure will be of typeBulkData1_Descriptor..sh 2 "Coding a Client".ppTo use Bulk Data in a client program, the Courier definition of theprocedure must include one BulkData.Source or BulkData.Sink parameter.As the actual argument to the call, the C client passes a constant,either BulkData1_immediateSource, BulkData1_immediateSink, BulkData1_nullSource, or BulkData1_nullSink as appropriate. The clientalso specifies a pointer to a user-supplied function as the secondargument to the remote procedure.Courier sets up the transaction, then calls the supplied function withone argument, the pointer returned by CourierOpen() describing theSPP socket on which to write (if a source argument) or read(if a sink) the bulk data. .ppTo complete the transaction normally, thefunction should send packets of SPP data terminated byan end-of-message (if writing) or read up to but not beyond anend-of-message (if reading), and should return normally to its caller.To do so, it should call the procedures BDTread(), BDTwrite(), or BDTclosewrite()as appropriate. BDTread() and BDTwrite() take arguments analogous to read()and write() except that instead of a file descriptor they accept the SPPdescriptor returned by CourierOpen(); their arguments are thus(1) the pointer to the SPP data structure,(2) a pointer to an array of characters (the IO buffer),and (3) a count;they return the number of characters actually read or written..ppThe UNIX program can finish a write transfer by calling BDTclosewrite() orBDTabort(), each with the SPP descriptor as argument.BDTclosewrite() produces an SPP end-of-message, while BDTabort() instructsCourier to discard any buffered data andsend a Bulk Data Abort to abort the transaction. A read transfer may also beprematurely ended by calling BDTabort()..sh 2 "Coding a Server".ppWriting a server that uses BDTis similar to writing the BDT function in a caller.The server is passed a BulkData.Source or BulkData.Sink (the two areindistinguishable at run time).The server routine should check to make sure that the source or sinkis of type null or immediate (active and passive descriptors are notsupported in this implementation) by means of code such as:.sp 1.nfFoo(bdtconnection,ignoredarg,..., s, ...) CourierConnection *bdtconnection; BulkData1_Sink s; . . . switch (s.designator) { case active: case passive: /* can't raise BulkData1_InvalidDescriptor, so */ raise(someerror); case null: /* handle null transfer */ break; case immediate: /* handle normal, i.e. immediate, transfer */ break; }.fi.sp 1Note that the BDT error InvalidDescriptor cannot be used unless theprocedure declaration explicitly notes that this is a valid REPORTS errortype. Normally, each courier program defines its own error to be reportedon BDT problems..ppUse the first argument to the server procedure as the handle on theBDT connection.As with a client BDTabort() maybe used to send abort messages..sh 2 "Sending Bulk Data".ppSending bulk data, either as client or server, is quitestraightforward. Use the supplied procedure BDTwrite(), whosesemantics are similar to write(). BDTwrite() will return -1if an error occurs or an abort from the listener is received; in thiscase, the sender should immediately cease transmitting BDT data, andshould instead call BDTabort(). Typical code to send data on ``bdtconnection'',in this case read from a buffered file openas ``source,'' might appear as:.sp 1.nf CourierConnection *bdtconnection; FILE *source; int count; char buffer[SPPMAXDATA]; ... while ( (count = fread(buffer,1,SPPMAXDATA,source)) > 0 && BDTwrite(bdtconnection,buffer,count) >= 0 ) ; if (count <= 0) /* succsfull transfer */ BDTclosewrite(bdtconnection); else BDTabort(bdtconnection);.fi.sp 1.ppEach BDTwrite() transmits one packet after checking for an abort messagefrom the receiver.A sender which gets an abort from a remote receiver must immediatelystop transferring data and instead must echo an abort via BDTabort()to acknowledge the end of the transfer..ppAlso, for efficiency'ssake it is desirable to write packets as near as possible to thenominal maximum length of an SPP packet; this suggests a buffer length of534 bytes. For programmer convenience, the file
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -