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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
|
******************************************************************************
README - Sun RPC
******************************************************************************
==============================================================================
Abstract
==============================================================================
RPC is a package supporting the Sun RPC protocol. RPC programs, procedures,
clients, and servers can be dynamically represented and modified. Of course,
there is also a classical RPC generator which generates functions doing the
language mapping from XDR values to language values and vice versa.
==============================================================================
Download
==============================================================================
You can download RPC as gzip'ed tarball [1].
==============================================================================
Documentation
==============================================================================
Sorry, there is no manual. The mli files describe each function in detail.
Furthermore, the following additional information may be useful.
------------------------------------------------------------------------------
Introduction to ocamlrpcgen
------------------------------------------------------------------------------
The tool ocamlrpcgen generates O'Caml modules which greatly simplify the
creation and invocation of remote procedures. For example, if we have an XDR
definition calculate.x
program P {
version V {
int add(int,int) = 1;
} = 2;
} = 3;
the generation of a corresponding RPC client is done by
ocamlrpcgen -aux -clnt calculate.x
and the tool will generate an RPC server by
ocamlrpcgen -aux -srv calculate.x
The flag -aux causes ocamlrpcgen to create a module Calculate_aux containing
types, and constants from the XDR definition, and containing conversion
functions doing the language mapping from XDR to O'Caml and vice versa.
Calculate_aux defines the types for the arguments of the procedure and the
result as follows:
type t_P'V'add'arg = (* Arguments *)
( Rtypes.int4 * Rtypes.int4 )
and t_P'V'add'res = (* Result *)
Rtypes.int4
Note that the XDR integer type is mapped to Rtypes.int4 which is an opaque type
representing 4-byte signed integers. Rtypes defines conversion functions for
int4 to/from other O'Caml types. If Rtypes.int4 is not what you want, you can
select a different integer mapping on the command line of ocamlrpcgen. For
example, -int int32 selects that you want the built-in int32 integer type, and
-int unboxed selects that you want the built-in int integer type. Note (1) that
you can also select the integer mapping case-by-case (see below), and (2) that
there is a corresponding switch for the XDR hyper type (8-byte integers).
Calculate_aux defines also constants (none in our example), conversion
functions, XDR type terms, and RPC programs. These other kinds of definitions
can be ignored for the moment.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Generating clients with ocamlrpcgen
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The flag -clnt causes ocamlrpcgen to generate the module Calculate_clnt
containing functions necessary to contact a remote program as client. Here,
Calculate_clnt has the signature:
module P : sig
module V : sig
open Calculate_aux
val create_client :
?esys:Unixqueue.event_system ->
Rpc_client.connector ->
Rpc.protocol ->
Rpc_client.t
val create_portmapped_client :
?esys:Unixqueue.event_system ->
string ->
Rpc.protocol ->
Rpc_client.t
val add : Rpc_client.t -> t_P'V'add'arg -> t_P'V'add'res
val add'async :
Rpc_client.t ->
t_P'V'add'arg ->
((unit -> t_P'V'add'res) -> unit) ->
unit
end
end
Normally, the function P.V.create_portmapped_client is the preferred function
to contact the RPC program. For example, to call the "add" procedure running on
host "moon", the following statements suffice:
let client = Calculator_clnt.P.V.create_portmapped_client "moon" Rpc.Tcp in
let n = Calculator_clnt.P.V.add client (m1,m2) in
Rpc_client.shut_down client;
That's all for a simple client!
The invocation of P.V.create_portmapped_client first asks the portmapper on
"moon" for the TCP instance of the program P.V, and stores the resulting
internet port. Because we wanted TCP, the TCP connection is opened, too. When
P.V.add is called, the values m1 and m2 are XDR-encoded and sent over the TCP
connection to the remote procedure; the answer is XDR-decoded and returned,
here n. Finally, the function Rpc_client.shut_down closes the TCP connection.
Of course, this works for UDP transports, too; simply pass Rpc.Udp instead of
Rpc.Tcp.
The function P.V.create_client does not contact the portmapper to find out the
internet port; you must already know the port and pass it as connector argument
(see Rpc_client for details).
You could have also invoked "add" in an asynchronous way by using
P.V.add'async. This function does not wait until the result of the RPC call
arrives; it returns immediately. When the result value has been received, the
function passed as third argument is called back, and can process the value. An
application of asynchronous calls is to invoke two remote procedures at the
same time:
let esys = Unixqueue.create_event_system() in
let client1 = Calculator_clnt.P.V.create_portmapped_client
~esys:esys "moon" Rpc.Tcp in
let client2 = Calculator_clnt.P.V.create_portmapped_client
~esys:esys "mars" Rpc.Tcp in
let got_answer1 get_value =
let v = get_value() in
print_endline "moon has replied!"; ... in
let got_answer2 get_value =
let v = get_value() in
print_endline "mars has replied!"; ... in
Calculator_clnt.P.V.add'async client1 (m1,m2) got_answer1;
Calculator_clnt.P.V.add'async client2 (m3,m4) got_answer1;
Unixqueue.run esys
Here, the two clients can coexist because they share the same event system (see
the Unixqueue module); this system manages it that every network event on the
connection to "moon" will be forwarded to client1 and that the network events
on the connection to "mars" will be forwarded to client2. The add'async calls
do not block; they only register themselves with the event system and return
immediately. Unixqueue.run starts the event system: The XDR-encoded values
(m1,m2) are sent to "moon", and (m3,m4) to "mars"; replies are recorded. Once
the reply of "moon" is complete, got_answer1 is called; once the reply of
"mars" has been fully received, got_answer2 is called. These functions can now
query the received values by invoking get_value; note that get_value will
either return the value or raise an exception if something went wrong. When
both answers have been received and processed, Unixqueue.run will return.
Obviously, asynchronous clients are a bit more complicated than synchronous
ones; however it is still rather simple to program them. For more information
on how the event handling works, see the Equeue User's Guide [2].
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Generating servers with ocamlrpcgen
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The flag -srv causes ocamlrpcgen to generate the module Calculate_srv
containing functions which can act as RPC servers. Here, Calculate_srv has the
signature:
module P : sig
module V : sig
open Calculate_aux
val create_server :
?limit:int ->
proc_add : (t_P'V'add'arg -> t_P'V'add'res) ->
Rpc_server.connector ->
Rpc.protocol ->
Rpc.mode ->
Unixqueue.event_system ->
Rpc_server.t
val create_async_server :
?limit:int ->
proc_add : (Rpc_server.session ->
t_P'V'add'arg ->
(t_P'V'add'res -> unit) ->
unit) ->
Rpc_server.connector ->
Rpc.protocol ->
Rpc.mode ->
Unixqueue.event_system ->
Rpc_server.t
end
end
There are two functions: P.V.create_server acts as a synchronous server, and
P.V.create_async_server works as asynchronous server. Let's first explain the
simpler synchronous case.
P.V.create_server accepts a number of labeled arguments and a number of
anonymous arguments. There is always an optional ?limit parameter limiting the
number of pending connections accepted by the server (default: 20); this is the
second parameter of the listen system call. For every procedure p realized by
the server there is a labeled argument proc_p passing the function actually
computing the procedure. For synchronous servers, this function simply gets the
argument of the procedure and must return the result of the procedure. In this
example, we only want to realize the "add" procedure, and so there is only a
proc_add argument. The anonymous Rpc_server.connector argument specifies the
internet port (or the file descriptor) on which the server will listen for
incoming connections. The Rpc.protocol argument defines whether this is a
TCP-like (stream-oriented) or a UDP-like (datagram-oriented) service. The
Rpc.mode parameter selects how the connector must be handled: Whether it acts
like a socket or whether is behaves like an already existing bidirectional
pipeline. Finally, the function expects the event system to be used as last
argument.
For example, to define a server accepting connections on the local loopback
interface on TCP port 6789, the following statement creates such a server:
let esys = Unixqueue.create_event_system in
let server =
Calculate_srv.P.V.create_server
~proc_add: add
(Rpc_server.Localhost 6789) (* connector *)
Rpc.Tcp (* protocol *)
Rpc.Socket (* mode *)
esys
Note that this statement creates the server, but actually does not serve the
incoming connections. You need an additionally
Unixqueue.run esys
to start the service.
Not all combinations of connectors, protocols, and modes are sensible.
Especially the following values work:
- TCP internet servers: One of the connectors Localhost or Portmapped; the
protocol Rpc.Tcp; the mode Rpc.Socket
- UDP internet servers: One of the connectors Localhost or Portmapped; the
protocol Rpc.Udp; the mode Rpc.Socket
- Stream-based Unix domain socket servers: The connector Unix, the protocol
Rpc.Tcp; the mode Rpc.Socket
- Datagram-based Unix domain socket servers: These are not supported
- Serving an already accepted (inetd) stream connection: The connector
Descriptor; the protocol Rpc.Tcp; the mode Rpc.BiPipe
- Serving an already received (inetd) datagram: XXX (status unclear)
The connector Portmapped registers the service at the local portmapper, and is
the connector of choice.
Note that servers with mode=Socket never terminate; they wait forever for
service requests. On the contrary, servers with mode=BiPipe process only the
current (next) request, and terminate then.
The resulting server is synchronous because the next request is only accepted
after the previous request has been finished. This means that the calls are
processed in a strictly serialized way (one after another); however, the
network traffic caused by the current and by previous calls can overlap (to
maximize network performance).
In contrast to this, an asynchronous server needs not respond immediately to an
RPC call. Once the call has been registered, the server is free to reply
whenever it likes to, even after other calls have been received. For example,
you can synchronize several clients: Only after both clients A and B have
called the procedure "sync", the replies of the procedures are sent back:
let client_a_sync = ref None
let client_b_sync = ref None
let sync s arg send_result =
if arg.name_of_client = "A" then
client_a_sync := Some send_result;
if arg.name_of_client = "B" then
client_b_sync := Some send_result;
if !client_a_sync <> None && !client_b_sync <> None then (
let Some send_result_to_a = !client_a_sync in
let Some send_result_to_b = !client_b_sync in
send_result_to_a "Synchronized";
send_result_to_b "Synchronized";
)
let server =
Sync.V.create_async_server
~proc_sync: sync
...
Here, the variables client_a_sync and client_b_sync store whether one of the
clients have already called the "sync" service, and if so, the variables store
also the function that needs to be called to pass the result back. For example,
if "A" calls "sync" first, it is only recorded that there was such a call;
because send_result is not invoked, "A" will not get a reply. However, the
function send_result is stored in client_a_sync such that it can be invoked
later. If "B" calls the "sync" procedure next, client_b_sync is updated, too.
Because now both clients have called the service, synchronization has happed,
and the answers to the procedure calls can be sent back to the clients. This is
done by invoking the functions that have been remembered in client_a_sync and
client_b_sync; the arguments of these functions are the return values of the
"sync" procedure.
It is even possible for an asynchronous server not to respond at all; for
example to implement batching (the server receives a large number of calls on a
TCP connection and replies only to the last call; the reply to the last call
implicitly commits that all previous calls have been received, too).
To create multi-port servers, several servers can share the same event system;
e.g.
let esys = Unixqueue.create_event_system in
let tcp_server =
P.V.create_server ... Rpc.Tcp ... esys in
let udp_server =
P.V.create_server ... Rpc.Udp ... esys in
Unixqueue.run esys
------------------------------------------------------------------------------
Command line arguments of ocamlrpcgen
------------------------------------------------------------------------------
The tool accepts the following options:
usage: ocamlrpcgen [-aux] [-clnt] [-srv]
[-int (abstract | int32 | unboxed) ]
[-hyper (abstract | int64 | unboxed) ]
[-cpp (/path/to/cpp | none) ]
[-D var=value]
[-U var]
file.xdr ...
- -aux: Creates for every XDR file the auxiliary module containing the type
and constant definitions as O'Caml expressions, and containing the
conversion functions implementing the language mapping.
- -clnt: Creates for every XDR file a client module.
- -srv: Creates for every XDR file a server module.
- -int abstract: Uses Rtypes.int4 for signed ints and Rtypes.uint4 for
unsigned ints as default integer representation. This is the default.
- -int int32: Uses int32 for both signed and unsigned ints as default integer
representation. Note that overflows are ignored for unsigned ints; i.e.
large unsigned XTR integers are mapped to negative int32 values.
- -int unboxed: Uses Pervasives.int for both signed and unsigned ints as
default integer representation. XDR values outside the range of O'Camls 31
bit signed ints are rejected (raise an exception).
- -hyper abstract: Uses Rtypes.int8 for signed ints and Rtypes.uint8 for
unsigned ints as default hyper (64 bit integer) representation. This is the
default.
- -hyper int64: Uses int64 for both signed and unsigned ints as default hyper
representation. Note that overflows are ignored for unsigned ints; i.e.
large unsigned XTR hypers are mapped to negative int64 values.
- -hyper unboxed: Uses Pervasives.int for both signed and unsigned ints as
default hyper representation. XDR values outside the range of O'Camls 31 bit
signed ints are rejected (raise an exception).
- -cpp /path/to/cpp: Applies the C preprocessor found under /path/to/cpp on
the XDR files before these are processed. The default is -cpp cpp.
- -cpp none: Does not call the C preprocessor.
- -D var=value: Defines the C preprocessor variable var with the given value.
- -U var: Undefines the C preprocessor variable var.
------------------------------------------------------------------------------
The language mapping underlying ocamlrpcgen
------------------------------------------------------------------------------
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Names
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Because XDR has a different naming concept than O'Caml, sometimes identifiers
must be renamed. For example, if you have two structs with equally named
components
struct a {
t1 c;
...;
}
struct b {
t2 c;
...;
}
the corresponding O'Caml types will be
type a = { c : t1; ... }
type b = { c' : t2; ... }
i.e. the second occurrence of c has been renamed to c'. Note that ocamlrpcgen
prints always a warning for such renamings that are hard to predict.
Another reason to rename an identifier is that the first letter has the wrong
case. In O'Caml, the case of the first letter must be compatible with its
namespace. For example, a module name must be uppercase. Because RPC programs
are mapped to O'Caml modules, the names of RPC programs must begin with an
uppercase letter. If this is not the case, the identifier is renamed, too.
You can specify the O'Caml name of every XDR/RPC identifier manually: Simply
add after the definition of the identifier the phrase "=> ocaml_id" where
ocaml_id is the preferred name for O'Caml. Example:
struct a {
t1 c => a_c;
...;
}
struct b {
t2 c => b_c;
...;
}
Now the generated O'Caml types are
type a = { a_c : t1; ... }
type b = { b_c : t2; ... }
This works wherever a name is defined in the XDR file.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Integer types
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
XDR defines 32 bit and 64 bit integers, each in a signed and unsigned variant.
As O'Caml does only know 31 bit signed integers (type "int"; the so-called
unboxed integers), 32 bit signed integers (type "int32"), and 64 bit signed
integers (type "int64"), it is unclear how to map the XDR integers to O'Caml
integers.
The module Rtypes defines the opaque types int4, uint4, int8, and uint8 which
exactly correspond to the XDR types. These are useful to pass integer values
through to other applications, and for simple identification of things.
However, you cannot compute directly with the Rtypes integers. Of course,
Rtypes also provides conversion functions to the basic O'Caml integer types
int, int32, and int64, but it would be very inconvenient to call these
conversions for every integer individually.
Because of this, ocamlrpcgen has the possibility to specify the O'Caml integer
variant for every integer value (and it generates the necessary conversion
invocations automatically). The new keywords "_abstract", "_int32", "_int64",
and "_unboxed" select the variant to use:
- _abstract int: A signed 32 bit integer mapped to Rtypes.int4
- _int32 int: A signed 32 bit integer mapped to int32
- _int64 int: A signed 32 bit integer mapped to int64
- _unboxed int: A signed 32 bit integer mapped to int
- unsigned _abstract int: An unsigned 32 bit integer mapped to Rtypes.uint4
- unsigned _int32 int: An unsigned 32 bit integer mapped to int32 (ignoring
overflows)
- unsigned _int64 int: An unsigned 32 bit integer mapped to int64
- unsigned _unboxed int: An unsigned 32 bit integer mapped to int
Note that the 32 bits of the unsigned integer are simply casted to the 32 bits
of int32 in the case of unsigned _int32 int (the meaning of the sign is
ignored). In contrast to this, the _unboxed specifier causes a language mapping
rejecting too small or too big values.
A similar mapping can be specified for the 64 bit integers (hypers):
- _abstract hyper: A signed 64 bit integer mapped to Rtypes.int8
- _int64 hyper: A signed 64 bit integer mapped to int64
- _unboxed hyper: A signed 64 bit integer mapped to int
- unsigned _abstract hyper: An unsigned 64 bit integer mapped to Rtypes.uint8
- unsigned _int64 hyper: An unsigned 64 bit integer mapped to int64
- unsigned _unboxed hyper: An unsigned 64 bit integer mapped to int
Again, unsigned _int64 hyper causes that the 64 bits of the XDR values are
casted to int64.
If the keyword specifying the kind of language mapping is omitted, the default
mapping applies. Unless changed on the command line (options -int and -hyper),
the default mapping is _abstract:
- int: means _abstract int unless changed on the command line by the -int
option
- unsigned int: means unsigned _abstract int unless changed on the command
line by the -int option
- hyper: means _abstract hyper unless changed on the command line by the
-hyper option
- unsigned hyper: means unsigned _abstract hyper unless changed on the command
line by the -hyper option
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Floating-point types
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The XDR types single and double are supported and both mapped to the O'Caml
type float. The XDR type quadruple is not supported.
Note that the support for single is very bad; the conversion function doing the
necessary bit shifting is written in O'Caml itself (instead of letting the CPU
doing the dirty work).
The code for double assumes that the CPU represents floating-point numbers
according to the IEEE standards.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
String and Opaque types
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Strings and opaque values are mapped to O'Caml strings. If strings have a fixed
length or a maximum length, this constraint is checked when the conversion is
performed.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Array types
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Arrays are mapped to O'Caml arrays. If arrays have a fixed length or a maximum
length, this constraint is checked when the conversion is performed.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Record types (struct)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Structs are mapped to O'Caml records.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Enumerated types (enum)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Enumerated types are mapped to Rtypes.int4 (always, regardless of what the -int
option specifies). The enumerated constants are mapped to let-bound values of
the same name. Example: The XDR definition
enum e {
A = 1;
B = 2;
}
generates the following lines of code in the auxiliary module:
type e = Rtypes.int4;;
val a : Rtypes.int4;;
val b : Rtypes.int4;;
However, when the XDR conversion is performed, it is checked whether values of
enumerators are contained in the set of allowed values.
The special enumerator bool is mapped to the O'Caml type bool.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Union types discriminated by enumerations
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Often, XDR unions are discriminated by enumerations, so this case is handled
specially. For every case of the enumerator, a polymorphic variant is generated
that contains the selected arm of the union. Example:
enum e {
A = 1;
B = 2;
C = 3;
D = 4;
}
union u (e discr) {
case A:
int x;
case B:
hyper y;
default:
string z;
}
This is mapped to the O'Caml type definitions:
type e = Rtypes.int4;;
type u =
[ `a of Rtypes.int4
| `b of Rtypes.int8
| `c of string
| `d of string
]
Note that the identifiers of the components (discr, x, y, z) have vanished;
they are simply not necessary in a sound typing environment. Also note that the
default case has been "expanded"; because the cases of the enumerator are known
it is possible to determine the missing cases meant by "default" and to define
these cases explicitly.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Union types discriminated by integers
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
If the discriminant has integer type, a different mapping scheme is used. For
every case occuring in the union definition a separate polymorphic variant is
defined; if necessary, an extra default variant is added. Example:
union u (int discr) {
case -1:
int x;
case 1:
hyper y;
default:
string z;
}
This is mapped to the O'Caml type definition:
type u =
[ `__1 of Rtypes.int4
| `_1 of Rtypes.int8
| `default of (Rtypes.int4 * string)
]
Note that positive cases get variant tags of the form "_n" and that negative
cases get variant tags of the form "__n". The default case is mapped to the tag
`default with two arguments: First the value of the discriminant, second the
value of the default component.
This type of mapping is not recommended, and only provided for completeness.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The special "option" union type
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
The XDR * type is mapped to the O'Caml option type. Example:
typedef string *s;
is mapped to
type s = string option
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Recursive types
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Recursive types are fully supported. Unlike in the C language, you can
recursively refer to types defined before or after the current type definition.
Example:
typedef intlistbody *intlist; /* Forward reference */
typedef struct {
int value;
intlist next;
} intlistbody;
This is mapped to:
type intlist = intlistbody option
and intlistbody =
{ value : Rtypes.int4;
next : intlist;
}
However, it is not checked whether there is a finite fixpoint of the recursion.
The O'Caml compiler will do this check anyway, so it not really needed within
ocamlrpcgen.
------------------------------------------------------------------------------
Overview over the RPC library
------------------------------------------------------------------------------
Normally, only the following modules are of interest:
- Rtypes: Supports serialization/deserialization of the basic integer and fp
types (rtypes = remote types)
- Rpc: Contains some types needed everyhwere
- Rpc_client: Contains the functions supporting RPC clients
- Rpc_server: Contains the functions supporting RPC servers
- Rpc_portmapper: Functions to contact the portmapper service
- Rpc_auth_sys: AUTH_SYS style authentication.
------------------------------------------------------------------------------
Restrictions of the current implementation
------------------------------------------------------------------------------
The authentication styles AUTH_DH and AUTH_LOCAL are not yet supported on all
platforms.
The implementation uses an intermediate, symbolic representation of the values
to transport over the network. This may restrict the performance.
Quadruple-precision fp numbers are not supported.
RPC broadcasts are not supported.
TI-RPC and rpcbind versions 3 and 4 are not supported.
------------------------------------------------------------------------------
Multi-threading
------------------------------------------------------------------------------
Currently, the library has no special support for multi-threaded applications.
However, the library is reentrant, and this makes it possible to run several
independent instances of the library in separated threads. Of course, this has
only limited applications.
The library should only be used in multi-threaded environments with O'Caml >=
3.01.
I've already written an example with a real multi-threaded server. One thread
handles all network connections, and starts new threads when TCP connections
are accepted. However, this example is very difficult code, and not yet ready
for a public release. Especially, the problem is how to signal thread events
across Unixqueue event systems.
------------------------------------------------------------------------------
RPC tunneled through HTTP
------------------------------------------------------------------------------
There is a new library part rpc-over-http that encapsulates RPC messages into
HTTP messages. Please see the README files in src/rpc-over-http and
examples/http_tunnel for details.
==============================================================================
Author, Copying
==============================================================================
RPC has been written by Gerd Stolpmann [3]. You may copy it as you like, you
may use it even for commercial purposes as long as the license conditions are
respected, see the file LICENSE coming with the distribution. It allows almost
everything.
==============================================================================
History
==============================================================================
- 0.4.1: rpc now works for O'Caml 3.08alpha. Small bugfix regarding cpp on
MacOSX. Some improvements in Rpc_client.
- 0.4: As far as I remember, I did a lot of performance tuning in this
version. There are new "fast" versions XV_enum_fast, XV_struct_fast,
XV_union_over_enum_fast of already existing XDR value representations. These
fast versions use arrays instead of associative lists. ocamlrpcgen was
changed to take advantage from these improvements.
- 0.3.3: Fix: ocamlrpcgen outputs the right names for the server procedures in
mli files.
- 0.3.2: Several smaller fixes: unions can have "fall-through" cases (as in:
union ... { case A: case B: ... }). If the type "bool" is used as
discriminant, the generated names of the O'Caml variants are `False and
`True, and no longer `false and `true (which caused a syntax error in recent
versions of the O'Caml compiler). An error with constants > 255 has been
fixed, too.
- 0.3.1: Updated for O'Caml 3.04.
- 0.3: The package has now a "configure" script. The directory hierarchy has
been improved.
The new library part rpc-over-http allows it to encapsulate RPC messages
into HTTP messages. This makes it possible to call remote services behind
firewalls.
Support for the authentication methods AUTH_SYS, AUTH_DH and local
authentication. The latter two should be regarded as experimental, though.
Especially the two core modules Rpc_client and Rpc_server have been
completely revised.
There are now regression tests for the core functionality.
The distribution contains a comprehensive example of RPC programming: A
server for file queues.
- 0.2.2: Fixed some bugs with big messages.
- 0.2: Added ocamlrpcgen. Improved the possible language mappings for integer
types (int32, int64).
--------------------------
[1] see http://www.ocaml-programming.de/packages/rpc-0.4.1.tar.gz
[2]
see http://www.ocaml-programming.de/packages/documentation/equeue/users-guide/
[3] see mailto:gerd@gerd-stolpmann.de
|