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 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<link rel="stylesheet" href="style.css" type="text/css">
<meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="Start" href="index.html">
<link rel="previous" href="Rpc_proxy.html">
<link rel="next" href="Rpc_mapping_ref.html">
<link rel="Up" href="index.html">
<link title="Index of types" rel=Appendix href="index_types.html">
<link title="Index of extensions" rel=Appendix href="index_extensions.html">
<link title="Index of exceptions" rel=Appendix href="index_exceptions.html">
<link title="Index of values" rel=Appendix href="index_values.html">
<link title="Index of class attributes" rel=Appendix href="index_attributes.html">
<link title="Index of class methods" rel=Appendix href="index_methods.html">
<link title="Index of classes" rel=Appendix href="index_classes.html">
<link title="Index of class types" rel=Appendix href="index_class_types.html">
<link title="Index of modules" rel=Appendix href="index_modules.html">
<link title="Index of module types" rel=Appendix href="index_module_types.html">
<link title="Uq_gtk" rel="Chapter" href="Uq_gtk.html">
<link title="Uq_tcl" rel="Chapter" href="Uq_tcl.html">
<link title="Equeue" rel="Chapter" href="Equeue.html">
<link title="Unixqueue" rel="Chapter" href="Unixqueue.html">
<link title="Unixqueue_pollset" rel="Chapter" href="Unixqueue_pollset.html">
<link title="Unixqueue_select" rel="Chapter" href="Unixqueue_select.html">
<link title="Uq_resolver" rel="Chapter" href="Uq_resolver.html">
<link title="Uq_engines" rel="Chapter" href="Uq_engines.html">
<link title="Uq_multiplex" rel="Chapter" href="Uq_multiplex.html">
<link title="Uq_transfer" rel="Chapter" href="Uq_transfer.html">
<link title="Uq_socks5" rel="Chapter" href="Uq_socks5.html">
<link title="Uq_io" rel="Chapter" href="Uq_io.html">
<link title="Uq_lwt" rel="Chapter" href="Uq_lwt.html">
<link title="Uq_libevent" rel="Chapter" href="Uq_libevent.html">
<link title="Uq_mt" rel="Chapter" href="Uq_mt.html">
<link title="Uq_client" rel="Chapter" href="Uq_client.html">
<link title="Uq_server" rel="Chapter" href="Uq_server.html">
<link title="Uq_datagram" rel="Chapter" href="Uq_datagram.html">
<link title="Uq_engines_compat" rel="Chapter" href="Uq_engines_compat.html">
<link title="Equeue_intro" rel="Chapter" href="Equeue_intro.html">
<link title="Equeue_howto" rel="Chapter" href="Equeue_howto.html">
<link title="Netcamlbox" rel="Chapter" href="Netcamlbox.html">
<link title="Netcgi_apache" rel="Chapter" href="Netcgi_apache.html">
<link title="Netcgi_modtpl" rel="Chapter" href="Netcgi_modtpl.html">
<link title="Netcgi_plex" rel="Chapter" href="Netcgi_plex.html">
<link title="Netcgi_common" rel="Chapter" href="Netcgi_common.html">
<link title="Netcgi" rel="Chapter" href="Netcgi.html">
<link title="Netcgi_ajp" rel="Chapter" href="Netcgi_ajp.html">
<link title="Netcgi_scgi" rel="Chapter" href="Netcgi_scgi.html">
<link title="Netcgi_cgi" rel="Chapter" href="Netcgi_cgi.html">
<link title="Netcgi_fcgi" rel="Chapter" href="Netcgi_fcgi.html">
<link title="Netcgi_dbi" rel="Chapter" href="Netcgi_dbi.html">
<link title="Netcgi1_compat" rel="Chapter" href="Netcgi1_compat.html">
<link title="Netcgi_test" rel="Chapter" href="Netcgi_test.html">
<link title="Netcgi_porting" rel="Chapter" href="Netcgi_porting.html">
<link title="Nethttp_client_conncache" rel="Chapter" href="Nethttp_client_conncache.html">
<link title="Nethttp_client" rel="Chapter" href="Nethttp_client.html">
<link title="Nettelnet_client" rel="Chapter" href="Nettelnet_client.html">
<link title="Netftp_data_endpoint" rel="Chapter" href="Netftp_data_endpoint.html">
<link title="Netftp_client" rel="Chapter" href="Netftp_client.html">
<link title="Nethttp_fs" rel="Chapter" href="Nethttp_fs.html">
<link title="Netftp_fs" rel="Chapter" href="Netftp_fs.html">
<link title="Netsmtp" rel="Chapter" href="Netsmtp.html">
<link title="Netpop" rel="Chapter" href="Netpop.html">
<link title="Netldap" rel="Chapter" href="Netldap.html">
<link title="Netclient_tut" rel="Chapter" href="Netclient_tut.html">
<link title="Netgss_bindings" rel="Chapter" href="Netgss_bindings.html">
<link title="Netgss" rel="Chapter" href="Netgss.html">
<link title="Nethttpd_types" rel="Chapter" href="Nethttpd_types.html">
<link title="Nethttpd_kernel" rel="Chapter" href="Nethttpd_kernel.html">
<link title="Nethttpd_reactor" rel="Chapter" href="Nethttpd_reactor.html">
<link title="Nethttpd_engine" rel="Chapter" href="Nethttpd_engine.html">
<link title="Nethttpd_services" rel="Chapter" href="Nethttpd_services.html">
<link title="Nethttpd_plex" rel="Chapter" href="Nethttpd_plex.html">
<link title="Nethttpd_util" rel="Chapter" href="Nethttpd_util.html">
<link title="Nethttpd_intro" rel="Chapter" href="Nethttpd_intro.html">
<link title="Netmcore" rel="Chapter" href="Netmcore.html">
<link title="Netmcore_camlbox" rel="Chapter" href="Netmcore_camlbox.html">
<link title="Netmcore_mempool" rel="Chapter" href="Netmcore_mempool.html">
<link title="Netmcore_heap" rel="Chapter" href="Netmcore_heap.html">
<link title="Netmcore_ref" rel="Chapter" href="Netmcore_ref.html">
<link title="Netmcore_array" rel="Chapter" href="Netmcore_array.html">
<link title="Netmcore_sem" rel="Chapter" href="Netmcore_sem.html">
<link title="Netmcore_mutex" rel="Chapter" href="Netmcore_mutex.html">
<link title="Netmcore_condition" rel="Chapter" href="Netmcore_condition.html">
<link title="Netmcore_queue" rel="Chapter" href="Netmcore_queue.html">
<link title="Netmcore_buffer" rel="Chapter" href="Netmcore_buffer.html">
<link title="Netmcore_matrix" rel="Chapter" href="Netmcore_matrix.html">
<link title="Netmcore_hashtbl" rel="Chapter" href="Netmcore_hashtbl.html">
<link title="Netmcore_process" rel="Chapter" href="Netmcore_process.html">
<link title="Netmcore_tut" rel="Chapter" href="Netmcore_tut.html">
<link title="Netmcore_basics" rel="Chapter" href="Netmcore_basics.html">
<link title="Netplex_types" rel="Chapter" href="Netplex_types.html">
<link title="Netplex_mp" rel="Chapter" href="Netplex_mp.html">
<link title="Netplex_mt" rel="Chapter" href="Netplex_mt.html">
<link title="Netplex_log" rel="Chapter" href="Netplex_log.html">
<link title="Netplex_controller" rel="Chapter" href="Netplex_controller.html">
<link title="Netplex_container" rel="Chapter" href="Netplex_container.html">
<link title="Netplex_sockserv" rel="Chapter" href="Netplex_sockserv.html">
<link title="Netplex_workload" rel="Chapter" href="Netplex_workload.html">
<link title="Netplex_main" rel="Chapter" href="Netplex_main.html">
<link title="Netplex_config" rel="Chapter" href="Netplex_config.html">
<link title="Netplex_kit" rel="Chapter" href="Netplex_kit.html">
<link title="Rpc_netplex" rel="Chapter" href="Rpc_netplex.html">
<link title="Netplex_cenv" rel="Chapter" href="Netplex_cenv.html">
<link title="Netplex_semaphore" rel="Chapter" href="Netplex_semaphore.html">
<link title="Netplex_sharedvar" rel="Chapter" href="Netplex_sharedvar.html">
<link title="Netplex_mutex" rel="Chapter" href="Netplex_mutex.html">
<link title="Netplex_encap" rel="Chapter" href="Netplex_encap.html">
<link title="Netplex_mbox" rel="Chapter" href="Netplex_mbox.html">
<link title="Netplex_internal" rel="Chapter" href="Netplex_internal.html">
<link title="Netplex_intro" rel="Chapter" href="Netplex_intro.html">
<link title="Netplex_advanced" rel="Chapter" href="Netplex_advanced.html">
<link title="Netplex_admin" rel="Chapter" href="Netplex_admin.html">
<link title="Netshm" rel="Chapter" href="Netshm.html">
<link title="Netshm_data" rel="Chapter" href="Netshm_data.html">
<link title="Netshm_hashtbl" rel="Chapter" href="Netshm_hashtbl.html">
<link title="Netshm_array" rel="Chapter" href="Netshm_array.html">
<link title="Netshm_intro" rel="Chapter" href="Netshm_intro.html">
<link title="Netstring_pcre" rel="Chapter" href="Netstring_pcre.html">
<link title="Netconversion" rel="Chapter" href="Netconversion.html">
<link title="Netchannels" rel="Chapter" href="Netchannels.html">
<link title="Netstream" rel="Chapter" href="Netstream.html">
<link title="Netmime_string" rel="Chapter" href="Netmime_string.html">
<link title="Netmime" rel="Chapter" href="Netmime.html">
<link title="Netsendmail" rel="Chapter" href="Netsendmail.html">
<link title="Neturl" rel="Chapter" href="Neturl.html">
<link title="Netaddress" rel="Chapter" href="Netaddress.html">
<link title="Netbuffer" rel="Chapter" href="Netbuffer.html">
<link title="Netmime_header" rel="Chapter" href="Netmime_header.html">
<link title="Netmime_channels" rel="Chapter" href="Netmime_channels.html">
<link title="Neturl_ldap" rel="Chapter" href="Neturl_ldap.html">
<link title="Netdate" rel="Chapter" href="Netdate.html">
<link title="Netencoding" rel="Chapter" href="Netencoding.html">
<link title="Netulex" rel="Chapter" href="Netulex.html">
<link title="Netaccel" rel="Chapter" href="Netaccel.html">
<link title="Netaccel_link" rel="Chapter" href="Netaccel_link.html">
<link title="Nethtml" rel="Chapter" href="Nethtml.html">
<link title="Netstring_str" rel="Chapter" href="Netstring_str.html">
<link title="Netmappings" rel="Chapter" href="Netmappings.html">
<link title="Netaux" rel="Chapter" href="Netaux.html">
<link title="Nethttp" rel="Chapter" href="Nethttp.html">
<link title="Netpagebuffer" rel="Chapter" href="Netpagebuffer.html">
<link title="Netfs" rel="Chapter" href="Netfs.html">
<link title="Netglob" rel="Chapter" href="Netglob.html">
<link title="Netauth" rel="Chapter" href="Netauth.html">
<link title="Netsockaddr" rel="Chapter" href="Netsockaddr.html">
<link title="Netnumber" rel="Chapter" href="Netnumber.html">
<link title="Netxdr_mstring" rel="Chapter" href="Netxdr_mstring.html">
<link title="Netxdr" rel="Chapter" href="Netxdr.html">
<link title="Netcompression" rel="Chapter" href="Netcompression.html">
<link title="Netunichar" rel="Chapter" href="Netunichar.html">
<link title="Netasn1" rel="Chapter" href="Netasn1.html">
<link title="Netasn1_encode" rel="Chapter" href="Netasn1_encode.html">
<link title="Netoid" rel="Chapter" href="Netoid.html">
<link title="Netstring_tstring" rel="Chapter" href="Netstring_tstring.html">
<link title="Netdn" rel="Chapter" href="Netdn.html">
<link title="Netx509" rel="Chapter" href="Netx509.html">
<link title="Netascii_armor" rel="Chapter" href="Netascii_armor.html">
<link title="Nettls_support" rel="Chapter" href="Nettls_support.html">
<link title="Netmech_scram" rel="Chapter" href="Netmech_scram.html">
<link title="Netmech_scram_gssapi" rel="Chapter" href="Netmech_scram_gssapi.html">
<link title="Netmech_scram_sasl" rel="Chapter" href="Netmech_scram_sasl.html">
<link title="Netmech_scram_http" rel="Chapter" href="Netmech_scram_http.html">
<link title="Netgssapi_support" rel="Chapter" href="Netgssapi_support.html">
<link title="Netgssapi_auth" rel="Chapter" href="Netgssapi_auth.html">
<link title="Netchannels_crypto" rel="Chapter" href="Netchannels_crypto.html">
<link title="Netx509_pubkey" rel="Chapter" href="Netx509_pubkey.html">
<link title="Netx509_pubkey_crypto" rel="Chapter" href="Netx509_pubkey_crypto.html">
<link title="Netsaslprep" rel="Chapter" href="Netsaslprep.html">
<link title="Netmech_plain_sasl" rel="Chapter" href="Netmech_plain_sasl.html">
<link title="Netmech_crammd5_sasl" rel="Chapter" href="Netmech_crammd5_sasl.html">
<link title="Netmech_digest_sasl" rel="Chapter" href="Netmech_digest_sasl.html">
<link title="Netmech_digest_http" rel="Chapter" href="Netmech_digest_http.html">
<link title="Netmech_krb5_sasl" rel="Chapter" href="Netmech_krb5_sasl.html">
<link title="Netmech_gs2_sasl" rel="Chapter" href="Netmech_gs2_sasl.html">
<link title="Netmech_spnego_http" rel="Chapter" href="Netmech_spnego_http.html">
<link title="Netchannels_tut" rel="Chapter" href="Netchannels_tut.html">
<link title="Netmime_tut" rel="Chapter" href="Netmime_tut.html">
<link title="Netsendmail_tut" rel="Chapter" href="Netsendmail_tut.html">
<link title="Netulex_tut" rel="Chapter" href="Netulex_tut.html">
<link title="Neturl_tut" rel="Chapter" href="Neturl_tut.html">
<link title="Netsys" rel="Chapter" href="Netsys.html">
<link title="Netsys_posix" rel="Chapter" href="Netsys_posix.html">
<link title="Netsys_pollset" rel="Chapter" href="Netsys_pollset.html">
<link title="Netlog" rel="Chapter" href="Netlog.html">
<link title="Netexn" rel="Chapter" href="Netexn.html">
<link title="Netsys_win32" rel="Chapter" href="Netsys_win32.html">
<link title="Netsys_pollset_posix" rel="Chapter" href="Netsys_pollset_posix.html">
<link title="Netsys_pollset_win32" rel="Chapter" href="Netsys_pollset_win32.html">
<link title="Netsys_pollset_generic" rel="Chapter" href="Netsys_pollset_generic.html">
<link title="Netsys_signal" rel="Chapter" href="Netsys_signal.html">
<link title="Netsys_oothr" rel="Chapter" href="Netsys_oothr.html">
<link title="Netsys_xdr" rel="Chapter" href="Netsys_xdr.html">
<link title="Netsys_rng" rel="Chapter" href="Netsys_rng.html">
<link title="Netsys_crypto_types" rel="Chapter" href="Netsys_crypto_types.html">
<link title="Netsys_types" rel="Chapter" href="Netsys_types.html">
<link title="Netsys_mem" rel="Chapter" href="Netsys_mem.html">
<link title="Netsys_tmp" rel="Chapter" href="Netsys_tmp.html">
<link title="Netsys_sem" rel="Chapter" href="Netsys_sem.html">
<link title="Netsys_pmanage" rel="Chapter" href="Netsys_pmanage.html">
<link title="Netsys_crypto" rel="Chapter" href="Netsys_crypto.html">
<link title="Netsys_tls" rel="Chapter" href="Netsys_tls.html">
<link title="Netsys_ciphers" rel="Chapter" href="Netsys_ciphers.html">
<link title="Netsys_digests" rel="Chapter" href="Netsys_digests.html">
<link title="Netsys_crypto_modes" rel="Chapter" href="Netsys_crypto_modes.html">
<link title="Netsys_gssapi" rel="Chapter" href="Netsys_gssapi.html">
<link title="Netsys_sasl_types" rel="Chapter" href="Netsys_sasl_types.html">
<link title="Netsys_sasl" rel="Chapter" href="Netsys_sasl.html">
<link title="Netsys_polypipe" rel="Chapter" href="Netsys_polypipe.html">
<link title="Netsys_polysocket" rel="Chapter" href="Netsys_polysocket.html">
<link title="Netsys_global" rel="Chapter" href="Netsys_global.html">
<link title="Nettls_gnutls_bindings" rel="Chapter" href="Nettls_gnutls_bindings.html">
<link title="Nettls_nettle_bindings" rel="Chapter" href="Nettls_nettle_bindings.html">
<link title="Nettls_gnutls" rel="Chapter" href="Nettls_gnutls.html">
<link title="Netunidata" rel="Chapter" href="Netunidata.html">
<link title="Netgzip" rel="Chapter" href="Netgzip.html">
<link title="Rpc_auth_local" rel="Chapter" href="Rpc_auth_local.html">
<link title="Rpc_xti_client" rel="Chapter" href="Rpc_xti_client.html">
<link title="Rpc" rel="Chapter" href="Rpc.html">
<link title="Rpc_program" rel="Chapter" href="Rpc_program.html">
<link title="Rpc_util" rel="Chapter" href="Rpc_util.html">
<link title="Rpc_portmapper_aux" rel="Chapter" href="Rpc_portmapper_aux.html">
<link title="Rpc_packer" rel="Chapter" href="Rpc_packer.html">
<link title="Rpc_transport" rel="Chapter" href="Rpc_transport.html">
<link title="Rpc_client" rel="Chapter" href="Rpc_client.html">
<link title="Rpc_simple_client" rel="Chapter" href="Rpc_simple_client.html">
<link title="Rpc_portmapper_clnt" rel="Chapter" href="Rpc_portmapper_clnt.html">
<link title="Rpc_portmapper" rel="Chapter" href="Rpc_portmapper.html">
<link title="Rpc_server" rel="Chapter" href="Rpc_server.html">
<link title="Rpc_auth_sys" rel="Chapter" href="Rpc_auth_sys.html">
<link title="Rpc_auth_gssapi" rel="Chapter" href="Rpc_auth_gssapi.html">
<link title="Rpc_proxy" rel="Chapter" href="Rpc_proxy.html">
<link title="Rpc_intro" rel="Chapter" href="Rpc_intro.html">
<link title="Rpc_mapping_ref" rel="Chapter" href="Rpc_mapping_ref.html">
<link title="Rpc_intro_gss" rel="Chapter" href="Rpc_intro_gss.html">
<link title="Shell_sys" rel="Chapter" href="Shell_sys.html">
<link title="Shell" rel="Chapter" href="Shell.html">
<link title="Shell_uq" rel="Chapter" href="Shell_uq.html">
<link title="Shell_fs" rel="Chapter" href="Shell_fs.html">
<link title="Shell_intro" rel="Chapter" href="Shell_intro.html">
<link title="Intro" rel="Chapter" href="Intro.html">
<link title="Platform" rel="Chapter" href="Platform.html">
<link title="Foreword" rel="Chapter" href="Foreword.html">
<link title="Ipv6" rel="Chapter" href="Ipv6.html">
<link title="Regexp" rel="Chapter" href="Regexp.html">
<link title="Tls" rel="Chapter" href="Tls.html">
<link title="Crypto" rel="Chapter" href="Crypto.html">
<link title="Authentication" rel="Chapter" href="Authentication.html">
<link title="Credentials" rel="Chapter" href="Credentials.html">
<link title="Gssapi" rel="Chapter" href="Gssapi.html">
<link title="Ocamlnet4" rel="Chapter" href="Ocamlnet4.html">
<link title="Get" rel="Chapter" href="Get.html"><title>Ocamlnet 4 Reference Manual : Rpc_intro</title>
</head>
<body>
<div class="navbar"><a class="pre" href="Rpc_proxy.html" title="Rpc_proxy">Previous</a>
<a class="up" href="index.html" title="Index">Up</a>
<a class="post" href="Rpc_mapping_ref.html" title="Rpc_mapping_ref">Next</a>
</div>
<h1>Rpc_intro</h1>
<div class="info-desc">
<p><b>Contents</b></p>
<ul>
<li><code class="code">Rpc_intro.intro</code>
<ul>
<li><code class="code">Rpc_intro.intro_clnt</code></li>
<li><code class="code">Rpc_intro.intro_srv</code></li>
</ul>
</li>
<li><code class="code">Rpc_intro.rpcgen</code></li>
<li><code class="code">Rpc_intro.mapping</code>
<ul>
<li><code class="code">Rpc_intro.syn_xdr</code></li>
<li><code class="code">Rpc_intro.syn_prog</code></li>
<li><code class="code">Rpc_intro.map_names</code></li>
<li><code class="code">Rpc_intro.map_ints</code></li>
<li><code class="code">Rpc_intro.map_fps</code></li>
<li><code class="code">Rpc_intro.map_strings</code></li>
<li><code class="code">Rpc_intro.map_arrays</code></li>
<li><code class="code">Rpc_intro.map_records</code></li>
<li><code class="code">Rpc_intro.map_enums</code></li>
<li><code class="code">Rpc_intro.map_eunions</code></li>
<li><code class="code">Rpc_intro.map_iunions</code></li>
<li><code class="code">Rpc_intro.map_opts</code></li>
<li><code class="code">Rpc_intro.map_recs</code></li>
</ul>
</li>
<li><code class="code">Rpc_intro.lib</code></li>
<li><code class="code">Rpc_intro.rpc_netplex</code></li>
<li><code class="code">Rpc_intro.restrictions</code></li>
</ul>
<h2 id="intro">Introduction to <code class="code">ocamlrpcgen</code></h2>
<p>The tool <code class="code">ocamlrpcgen</code> generates O'Caml modules which greatly simplify
the creation and invocation of remote procedures. For example, if we have an
XDR definition file <code class="code">calculate.x</code></p>
<pre class="codepre"><code class="code">program P {
version V {
int add(int,int) = 1;
} = 2;
} = 3;
</code></pre>
<p>the generation of a corresponding RPC client is done by issuing the command</p>
<pre class="codepre"><code class="code">ocamlrpcgen -aux -clnt calculate.x
</code></pre>
<p>and the tool will generate an RPC server by calling</p>
<pre class="codepre"><code class="code">ocamlrpcgen -aux -srv calculate.x
</code></pre>
<p>The flag -aux causes <code class="code">ocamlrpcgen</code> to create a module <code class="code">Calculate_aux</code>
containing types, and constants from the XDR definition, and
containing conversion functions doing the language mapping from XDR to
O'Caml and vice versa.</p>
<p><code class="code">Calculate_aux</code> defines the types for the arguments of the procedure and
the result as follows:</p>
<pre class="codepre"><code class="code">type t_P'V'add'arg = (* Arguments *)
( Netnumber.int4 * Netnumber.int4 )
and t_P'V'add'res = (* Result *)
Netnumber.int4
</code></pre>
<p>Note that the XDR integer type is mapped to <a href="Netnumber.html#TYPEint4"><code class="code">Netnumber.int4</code></a> which is an
opaque type representing 4-byte signed integers. <a href="Netnumber.html"><code class="code">Netnumber</code></a> defines
conversion functions for int4 to/from other O'Caml types. If
<a href="Netnumber.html#TYPEint4"><code class="code">Netnumber.int4</code></a> is not what you want, you can select a different
integer mapping on the command line of <code class="code">ocamlrpcgen</code>. For example, <code class="code">-int
int32</code> selects that you want the built-in <code class="code">int32</code> integer type, and <code class="code">-int
unboxed</code> selects that you want the built-in <code class="code">int</code> 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 <code class="code">hyper</code> type
(8-byte integers).</p>
<p><code class="code">Calculate_aux</code> also defines constants (none in our example), conversion
functions, XDR type terms, and RPC programs. These other kinds of definitions
can be ignored for the moment.</p>
<h3 id="intro_clnt">Generating clients with <code class="code">ocamlrpcgen</code></h3>
<p>The flag <code class="code">-clnt</code> causes <code class="code">ocamlrpcgen</code> to generate the module
<code class="code">Calculate_clnt</code> containing functions necessary to contact a remote
program as client. Here, <code class="code">Calculate_clnt</code> has the signature:</p>
<pre class="codepre"><code class="code">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
</code></pre>
<p>(Note: Depending on the version of <code class="code">ocamlrpcgen</code> your are using,
another function <code class="code">create_client2</code> may also be generated.)</p>
<p>Normally, the function <code class="code">P.V.create_portmapped_client</code> is the preferred
function to contact the RPC program. For example, to call the <code class="code">add</code>
procedure running on host <code class="code">moon</code>, the following statements suffice:</p>
<pre class="codepre"><code class="code">let m1 = 42 in
let m2 = 36 in
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;
</code></pre>
<p>That's all for a simple client!</p>
<p>The invocation of <code class="code">P.V.create_portmapped_client</code> first asks the
portmapper on "moon" for the TCP instance of the program <code class="code">P.V</code>, and
stores the resulting internet port. Because we wanted TCP, the TCP
connection is opened, too. When <code class="code">P.V.add</code> is called, the values <code class="code">m1</code> and
<code class="code">m2</code> are XDR-encoded and sent over the TCP connection to the remote
procedure; the answer is XDR-decoded and returned, here <code class="code">n</code>. Finally,
the function <code class="code">Rpc_client.shut_down</code> closes the TCP connection.</p>
<p>Of course, this works for UDP transports, too; simply pass <code class="code">Rpc.Udp</code>
instead of <code class="code">Rpc.Tcp</code>.</p>
<p>The function <code class="code">P.V.create_client</code> does not contact the portmapper to
find out the internet port; you must already know the port and pass it
as connector argument (see <a href="Rpc_client.html"><code class="code">Rpc_client</code></a> for details).</p>
<p>You could have also invoked <code class="code">add</code> in an asynchronous way by using
<code class="code">P.V.add'async</code>. 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:</p>
<pre class="codepre"><code class="code">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
</code></pre>
<p>Here, the two clients can coexist because they share the same event
system (see the <a href="Unixqueue.html"><code class="code">Unixqueue</code></a> module); this system manages it that
every network event on the connection to "moon" will be forwarded to
<code class="code">client1</code> and that the network events on the connection to "mars" will
be forwarded to <code class="code">client2</code>. The <code class="code">add'async</code> calls do not block; they
only register themselves with the event system and return
immediately. <a href="Unixqueue.html#VALrun"><code class="code">Unixqueue.run</code></a> starts the event system: The XDR-encoded
values <code class="code">(m1,m2)</code> are sent to "moon", and <code class="code">(m3,m4)</code> to "mars"; replies
are recorded. Once the reply of "moon" is complete, <code class="code">got_answer1</code> is
called; once the reply of "mars" has been fully received,
<code class="code">got_answer2</code> is called. These functions can now query the received
values by invoking <code class="code">get_value</code>; note that <code class="code">get_value</code> will either
return the value or raise an exception if something went wrong. When
both answers have been received and processed, <a href="Unixqueue.html#VALrun"><code class="code">Unixqueue.run</code></a> will
return.</p>
<p>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 <a href="Equeue_intro.html"><code class="code">Equeue_intro</code></a>.</p>
<p>Note that clients have only a limited lifetime: After a shutdown or an
error they become unusable. Since Ocamlnet version 3 there is another
flavor of client, the so-called proxies. See <a href="Rpc_proxy.html#tut"><i>The Rpc_proxy tutorial</i></a> for an
introduction. In particular, proxies can reconnect the connection to
the server after a shutdown, and they can even manage several
connections to the same server, or to different servers that are seen
as equivalent.</p>
<h3 id="intro_srv">Generating servers with <code class="code">ocamlrpcgen</code></h3>
<p>The flag <code class="code">-srv</code> causes <code class="code">ocamlrpcgen</code> to generate the module
<code class="code">Calculate_srv</code> containing functions which can act as RPC
servers. (Note: Recent versions of <code class="code">ocamlrpcgen</code> also support a switch
<code class="code">-srv2</code> that generates slightly better server stubs where one can bind
several programs/versions to the same server port.) Here,
<code class="code">Calculate_srv</code> has the signature:</p>
<pre class="codepre"><code class="code">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
</code></pre>
<p>There are two functions: <code class="code">P.V.create_server</code> acts as a synchronous
server, and <code class="code">P.V.create_async_server</code> works as asynchronous
server. Let's first explain the simpler synchronous case.</p>
<p><code class="code">P.V.create_server</code> accepts a number of labeled arguments and a number
of anonymous arguments. There is always an optional <code class="code">limit</code> parameter
limiting the number of pending connections accepted by the server
(default: 20); this is the second parameter of the <code class="code">Unix.listen</code>
system call. For every procedure p realized by the server there is a
labeled argument <code class="code">proc_</code>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 <code class="code">add</code>
procedure, and so there is only a <code class="code">proc_add</code> argument. The anonymous
<a href="Rpc_server.html#TYPEconnector"><code class="code">Rpc_server.connector</code></a> argument specifies the internet port (or the
file descriptor) on which the server will listen for incoming
connections. The <a href="Rpc.html#TYPEprotocol"><code class="code">Rpc.protocol</code></a> argument defines whether this is a
TCP-like (stream-oriented) or a UDP-like (datagram-oriented)
service. The <a href="Rpc.html#TYPEmode"><code class="code">Rpc.mode</code></a> 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 passed as last argument.</p>
<p>For example, to define a server accepting connections on the local
loopback interface on TCP port 6789, the following statement creates
such a server:</p>
<pre class="codepre"><code class="code">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
</code></pre>
<p>Note that this statement creates the server, but actually does not
serve the incoming connections. You need an additionally</p>
<pre class="codepre"><code class="code">Unixqueue.run esys
</code></pre>
<p>to start the service. (Note: If the server raises an exception, it will
fall through to the caller of <code class="code">Unixqueue.run</code>. The recommended way of
handling this is to log the exception, and call <code class="code">Unixqueue.run</code> again
in a loop. If too many exceptions occur in very short time the program
should terminate.)</p>
<p>Not all combinations of connectors, protocols, and modes are
sensible. Especially the following values work:</p>
<ul>
<li>TCP internet servers: One of the connectors <code class="code">Localhost</code>
or <code class="code">Portmapped</code>; the protocol <code class="code">Rpc.Tcp</code>; the mode <code class="code">Rpc.Socket</code></li>
<li>UDP internet servers: One of the connectors <code class="code">Localhost</code>
or <code class="code">Portmapped</code>; the protocol <code class="code">Rpc.Udp</code>; the mode <code class="code">Rpc.Socket</code></li>
<li>Stream-based Unix domain socket servers: The
connector <code class="code">Unix</code>, the protocol <code class="code">Rpc.Tcp</code>; the mode <code class="code">Rpc.Socket</code></li>
<li>Datagram-based Unix domain socket servers: These are
not supported</li>
<li>Serving an already accepted (inetd) stream connection:
The connector <code class="code">Descriptor</code>; the protocol <code class="code">Rpc.Tcp</code>; the mode <code class="code">Rpc.BiPipe</code></li>
</ul>
<p>The connector <code class="code">Portmapped</code> registers the service at the local
portmapper, and is the connector of choice.</p>
<p>Note that servers with mode=<code class="code">Socket</code> never terminate; they wait
forever for service requests. On the contrary, servers with
mode=<code class="code">BiPipe</code> process only the current (next) request, and terminate
then.</p>
<p>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).</p>
<p>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 <code class="code">sync</code>, the
replies of the procedures are sent back:</p>
<pre class="codepre"><code class="code">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
...
</code></pre>
<p>Here, the variables <code class="code">client_a_sync</code> and <code class="code">client_b_sync</code> store whether
one of the clients have already called the <code class="code">sync</code> service, and if so,
the variables store also the function that needs to be called to pass
the result back. For example, if <code class="code">A</code> calls <code class="code">sync</code> first, it is only
recorded that there was such a call; because send_result is not
invoked, <code class="code">A</code> will not get a reply. However, the function <code class="code">send_result</code>
is stored in <code class="code">client_a_sync</code> such that it can be invoked later. If <code class="code">B</code>
calls the <code class="code">sync</code> procedure next, <code class="code">client_b_sync</code> 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 <code class="code">client_a_sync</code> and <code class="code">client_b_sync</code>; the arguments of
these functions are the return values of the <code class="code">sync</code> procedure.</p>
<p>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).</p>
<p>To create multi-port servers, several servers can share the same event
system; e.g.</p>
<pre class="codepre"><code class="code">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
</code></pre>
<p>(Note: To create servers that implement several program or version
definitions, look for what the -srv2 switch of <code class="code">ocamlrpcgen</code> generated.)</p>
<h3 id="debuggig">Debugging aids</h3>
<p>There are some built-in debugging aids for developing RPC clients and
servers. Debug messages can be enabled by setting certain variables
to <code class="code">true</code>:</p>
<ul>
<li><a href="Rpc_client.Debug.html#VALenable"><code class="code">Rpc_client.Debug.enable</code></a>: Enables a general debug log for clients</li>
<li><a href="Rpc_client.Debug.html#VALenable_ptrace"><code class="code">Rpc_client.Debug.enable_ptrace</code></a>: Enables the client-side procedure
trace. For every procedure call two messages are emitted, one for
the request message and one for the response message. The level of
verbosity can be set with <a href="Rpc_client.Debug.html#VALptrace_verbosity"><code class="code">Rpc_client.Debug.ptrace_verbosity</code></a>.</li>
<li><a href="Rpc_server.Debug.html#VALenable"><code class="code">Rpc_server.Debug.enable</code></a>: Enables a general debug log for servers</li>
<li><a href="Rpc_server.Debug.html#VALenable_ptrace"><code class="code">Rpc_server.Debug.enable_ptrace</code></a>: Enables the server-side procedure
trace. For every procedure call three messages are emitted, one for
the request message, one at the time the request is decoded, and one
for the response message. The level of
verbosity can be set with <a href="Rpc_server.Debug.html#VALptrace_verbosity"><code class="code">Rpc_server.Debug.ptrace_verbosity</code></a>.</li>
<li><a href="Rpc_server.Debug.html#VALenable_ctrace"><code class="code">Rpc_server.Debug.enable_ctrace</code></a>: Enables the server-side connection
trace</li>
</ul>
<p>The messages are output via <a href="Netlog.Debug.html"><code class="code">Netlog.Debug</code></a>, and have a <code class="code">`Debug</code> log
level.</p>
<p>In Netplex context, the messages are redirected to the current Netplex
logger, so that they appear in the normal log file. Also, messages are
suppressed when they refer to the internally used RPC clients and servers.</p>
<h2 id="rpcgen">Command line arguments of ocamlrpcgen</h2>
<p>The tool accepts the following options:</p>
<pre class="codepre"><code class="code">usage: ocamlrpcgen [-aux] [-clnt] [-srv] [-srv2]
[-int (abstract | int32 | unboxed) ]
[-hyper (abstract | int64 | unboxed) ]
[-cpp (/path/to/cpp | none) ]
[-D var=value]
[-U var]
[-direct]
file.xdr ...
</code></pre>
<ul>
<li><code class="code">-aux</code>: 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.</li>
<li><code class="code">-clnt</code>: Creates for every XDR file a client module.</li>
<li><code class="code">-srv</code>: Creates for every XDR file a server module.</li>
<li><code class="code">-srv2</code>: Creates for every XDR file a new-style server module.</li>
<li><code class="code">-int abstract</code>: Uses <a href="Netnumber.html#TYPEint4"><code class="code">Netnumber.int4</code></a> for signed ints and
<a href="Netnumber.html#TYPEuint4"><code class="code">Netnumber.uint4</code></a> for unsigned ints as default integer representation.
This is the default. </li>
<li><code class="code">-int int32</code>: Uses <code class="code">int32</code> for both signed and unsigned
ints as default integer representation. Note that overflows are ignored for
unsigned ints; i.e. large unsigned XDR integers are mapped to negative <code class="code">int32</code>
values.</li>
<li><code class="code">-int unboxed</code>: Uses <code class="code">Pervasives.int</code> 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).</li>
<li><code class="code">-hyper abstract</code>: Uses <a href="Netnumber.html#TYPEint8"><code class="code">Netnumber.int8</code></a> for signed ints and
<a href="Netnumber.html#TYPEuint8"><code class="code">Netnumber.uint8</code></a> for unsigned ints as default hyper (64 bit integer)
representation. This is the default.</li>
<li><code class="code">-hyper int64</code>: Uses <code class="code">int64</code> for both signed and unsigned
ints as default hyper representation. Note that overflows are ignored for
unsigned ints; i.e. large unsigned XDR hypers are mapped to negative <code class="code">int64</code>
values.</li>
<li><code class="code">-hyper unboxed</code>: Uses <code class="code">Pervasives.int</code> 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).</li>
<li><code class="code">-cpp /path/to/cpp</code>: Applies the C preprocessor found
under /path/to/cpp on the XDR files before these are processed. The default
is <code class="code">-cpp cpp</code> (i.e. look up the <code class="code">cpp</code> command in the command search path).</li>
<li><code class="code">-cpp none</code>: Does not call the C preprocessor.</li>
<li><code class="code">-D var=value</code>: Defines the C preprocessor variable <code class="code">var</code>
with the given <code class="code">value</code>.</li>
<li><code class="code">-U var</code>: Undefines the C preprocessor variable <code class="code">var</code>.</li>
<li><code class="code">-direct</code>: The effect of this switch is that ocamlrpcgen generates
different code that directly maps the XDR byte representation to
the final OCaml values, and bypasses <a href="Netxdr.html#TYPExdr_value"><code class="code">Netxdr.xdr_value</code></a> as much as
possible. This is the same style the traditional rpcgen preferred.
The code is faster (up to 50% for certain large values), but also much
longer. As a rule of thumb, this style is only a win if you have
arrays of structs, and these structs have many elements, and there
are not many strings (because there is no acceleration for strings).
For example, a numerical RPC interface would definetely profit from
this alternate code generation scheme. Except generating more code,
there is no downside of this scheme. (Available since Ocamlnet-3.5.)</li>
</ul>
<h2 id="mapping">The language mapping underlying ocamlrpcgen</h2>
<p>The language mapping determines how the XDR types are mapped to O'Caml
types. See also <a href="Rpc_mapping_ref.html"><code class="code">Rpc_mapping_ref</code></a>.</p>
<h3 id="syn_xdr">The XDR syntax</h3>
<p>From RFC 1832:</p>
<pre class="codepre"><code class="code"> declaration:
type-specifier identifier
| type-specifier identifier "[" value "]"
| type-specifier identifier "<" [ value ] ">"
| "opaque" identifier "[" value "]"
| "opaque" identifier "<" [ value ] ">"
| "string" identifier "<" [ value ] ">"
| type-specifier "*" identifier
| "void"
value:
constant
| identifier
type-specifier:
[ "unsigned" ] "int"
| [ "unsigned" ] "hyper"
| "float"
| "double"
| "quadruple"
| "bool"
| enum-type-spec
| struct-type-spec
| union-type-spec
| identifier
enum-type-spec:
"enum" enum-body
enum-body:
"{"
( identifier "=" value )
( "," identifier "=" value )*
"}"
struct-type-spec:
"struct" struct-body
struct-body:
"{"
( declaration ";" )
( declaration ";" )*
"}"
union-type-spec:
"union" union-body
union-body:
"switch" "(" declaration ")" "{"
( "case" value ":" declaration ";" )
( "case" value ":" declaration ";" )*
[ "default" ":" declaration ";" ]
"}"
constant-def:
"const" identifier "=" constant ";"
type-def:
"typedef" declaration ";"
| "enum" identifier enum-body ";"
| "struct" identifier struct-body ";"
| "union" identifier union-body ";"
definition:
type-def
| constant-def
specification:
definition *
</code></pre>
<p><code class="code">ocamlrpcgen</code> supports a few extensions to this standard, see below.</p>
<h3 id="syn_prog">Syntax of RPC programs</h3>
<p>From RFC 1831:</p>
<pre class="codepre"><code class="code"> program-def:
"program" identifier "{"
version-def
version-def *
"}" "=" constant ";"
version-def:
"version" identifier "{"
procedure-def
procedure-def *
"}" "=" constant ";"
procedure-def:
type-specifier identifier "(" type-specifier
("," type-specifier )* ")" "=" constant ";"
</code></pre>
<h3 id="map_names">Mapping names</h3>
<p>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</p>
<pre class="codepre"><code class="code">struct a {
t1 c;
...;
}
struct b {
t2 c;
...;
}
</code></pre>
<p>the corresponding O'Caml types will be</p>
<pre class="codepre"><code class="code">type a = { c : t1; ... }
type b = { c' : t2; ... }
</code></pre>
<p>i.e. the second occurrence of <code class="code">c</code> has been renamed to <code class="code">c'</code>. Note that
<code class="code">ocamlrpcgen</code> prints always a warning for such renamings that are hard
to predict.</p>
<p>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 (quietly) renamed, too.</p>
<p>You can specify the O'Caml name of every XDR/RPC identifier manually:
Simply add after the definition of the identifier the phrase <code class="code">=>
ocaml_id</code> where <code class="code">ocaml_id</code> is the preferred name for O'Caml. Example:</p>
<pre class="codepre"><code class="code">struct a {
t1 c => a_c;
...;
}
struct b {
t2 c => b_c;
...;
}
</code></pre>
<p>Now the generated O'Caml types are</p>
<pre class="codepre"><code class="code">type a = { a_c : t1; ... }
type b = { b_c : t2; ... }
</code></pre>
<p>This works wherever a name is defined in the XDR file.</p>
<h3 id="map_dirs">Directives for name mapping</h3>
<p>Since Ocamlnet-3.6.7, there are now also a few directives influencing the
name mapping for structs, unions, and enums:</p>
<ul>
<li><code class="code">_lowercase</code>: the XDR name is lowercased</li>
<li><code class="code">_uppercase</code>: the XDR name is uppercased</li>
<li><code class="code">_capitalize</code>: the XDR name is capitalized</li>
<li><code class="code">_prefix "p"</code>: this prefix is prepended to the XDR name</li>
</ul>
<p>It is possible to specify several directives, which are then applied one
after the other.</p>
<ul>
<li>For structs, these directives control the name mapping of the elements.
Place the directives before the left brace, e.g.
<pre class="codepre"><code class="code"> struct a _lowercase _prefix "a_" {
T1 string;
T2 float;
};
</code></pre>
This would generate the record type
<code class="code"> type a = { a_t1 : string; a_t2 : float } </code>.</li>
</ul>
<ul>
<li>For enums, these directives control the name mapping of the generated
constants. Again, place the directives before the left brace, e.g.
<pre class="codepre"><code class="code"> enum x _lowercase {
ONE = 1,
TWO = 2
};
</code></pre>
The generated OCaml constants have here names <code class="code">one</code> and <code class="code">two</code>.</li>
<li>For unions, the directives control the name mapping of the discriminating
cases (when an enum is used for that). Place the directives before the
<code class="code">switch</code> keyword, e.g.
<pre class="codepre"><code class="code"> union u _lowercase _capitalize switch (x discr) {
case ONE: void;
case TWO: double x;
};
</code></pre>
This would generate the variant type <code class="code"> [`One | `Two of float] </code> in the
Ocaml mapping.</li>
</ul>
<h3 id="map_ints">Mapping integer types</h3>
<p>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 <code class="code">int</code>; the
so-called unboxed integers), 32 bit signed integers (type <code class="code">int32</code>), and 64 bit
signed integers (type <code class="code">int64</code>), it is unclear how to map the XDR integers to
O'Caml integers.</p>
<p>The module <a href="Netnumber.html"><code class="code">Netnumber</code></a> defines the opaque types <code class="code">int4</code>, <code class="code">uint4</code>, <code class="code">int8</code>,
and <code class="code">uint8</code> 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 <a href="Netnumber.html"><code class="code">Netnumber</code></a> integers. Of course, <a href="Netnumber.html"><code class="code">Netnumber</code></a> also provides
conversion functions to the basic O'Caml integer types <code class="code">int</code>, <code class="code">int32</code>,
and <code class="code">int64</code>, but it would be very inconvenient to call these
conversions for every integer individually.</p>
<p>Because of this, <code class="code">ocamlrpcgen</code> 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
<code class="code">_abstract</code>, <code class="code">_int32</code>, <code class="code">_int64</code>, and <code class="code">_unboxed</code> select the variant to
use:</p>
<ul>
<li><code class="code">_abstract int</code>: A signed 32 bit integer mapped to <a href="Netnumber.html#TYPEint4"><code class="code">Netnumber.int4</code></a></li>
<li><code class="code">_int32 int</code>: A signed 32 bit integer mapped to <code class="code">int32</code></li>
<li><code class="code">_int64 int</code>: A signed 32 bit integer mapped to <code class="code">int64</code></li>
<li><code class="code">_unboxed int</code>: A signed 32 bit integer mapped to <code class="code">int</code></li>
<li><code class="code">unsigned _abstract int</code>: An unsigned 32 bit integer mapped to <a href="Netnumber.html#TYPEuint4"><code class="code">Netnumber.uint4</code></a></li>
<li><code class="code">unsigned _int32 int</code>: An unsigned 32 bit integer mapped to <code class="code">int32</code>
(ignoring overflows)</li>
<li><code class="code">unsigned _int64 int</code>: An unsigned 32 bit integer mapped to <code class="code">int64</code></li>
<li><code class="code">unsigned _unboxed int</code>: An unsigned 32 bit integer mapped to <code class="code">int</code></li>
</ul>
<p>Note that the 32 bits of the unsigned integer are simply casted to the
32 bits of <code class="code">int32</code> in the case of <code class="code">unsigned _int32 int</code> (the meaning
of the sign is ignored). In contrast to this, the <code class="code">_unboxed</code> specifier
causes a language mapping rejecting too small or too big values.</p>
<p>A similar mapping can be specified for the 64 bit integers (hypers):</p>
<ul>
<li><code class="code">_abstract hyper</code>: A signed 64 bit integer mapped to <a href="Netnumber.html#TYPEint8"><code class="code">Netnumber.int8</code></a></li>
<li><code class="code">_int64 hyper</code>: A signed 64 bit integer mapped to <code class="code">int64</code></li>
<li><code class="code">_unboxed hyper</code>: A signed 64 bit integer mapped to <code class="code">int</code></li>
<li><code class="code">unsigned _abstract hyper</code>: An unsigned 64 bit integer mapped to <a href="Netnumber.html#TYPEuint8"><code class="code">Netnumber.uint8</code></a></li>
<li><code class="code">unsigned _int64 hyper</code>: An unsigned 64 bit integer mapped to <code class="code">int64</code></li>
<li><code class="code">unsigned _unboxed hyper</code>: An unsigned 64 bit integer mapped to <code class="code">int</code></li>
</ul>
<p>Again, <code class="code">unsigned _int64 hyper</code> causes that the 64 bits of the XDR values are
casted to <code class="code">int64</code>.</p>
<p>If the keyword specifying the kind of language mapping is omitted, the
default mapping applies. Unless changed on the command line (options
<code class="code">-int</code> and <code class="code">-hyper</code>), the default mapping is <code class="code">_abstract</code>.</p>
<h3 id="map_fps">Mapping floating-point types</h3>
<p>The XDR types <code class="code">single</code> and <code class="code">double</code> are supported and both mapped
to the O'Caml type <code class="code">float</code>. The XDR type <code class="code">quadruple</code> is not supported.</p>
<p>The code for <code class="code">double</code> assumes that the CPU represents floating-point
numbers according to the IEEE standards.</p>
<h3 id="map_strings">Mapping string and opaque types</h3>
<p>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.</p>
<p>Since Ocamlnet-3, strings can be declared as "managed" in the XDR
file, e.g.</p>
<pre class="codepre"><code class="code">typedef _managed string s<>;
</code></pre>
<p>A managed string is mapped to the object type <a href="Netxdr_mstring.mstring-c.html"><code class="code">Netxdr_mstring.mstring</code></a>.
The idea of managed strings is to avoid data copies as much as possible,
and to introduce some freedom of representation. In particular, managed
strings can be backed by normal strings or by bigarrays of char. The
RPC library chooses the representation that works best, and avoids copying
so far possible.</p>
<h3 id="map_arrays">Mapping array types</h3>
<p>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.</p>
<h3 id="map_records">Mapping record types (structs)</h3>
<p>Structs are mapped to O'Caml records by default. The elements are mutable.</p>
<p>Since Ocamlnet-3.6.7 it is possible to add an equality constraint, e.g.</p>
<pre class="codepre"><code class="code">struct x _equals "MyModule.x" {
...
};
</code></pre>
<p>would generate</p>
<pre class="codepre"><code class="code">type x = MyModule.x = { ... }
</code></pre>
<p>Another option since 3.6.7 is to request a tuple instead of a record:</p>
<pre class="codepre"><code class="code">struct x _tuple { ... }
</code></pre>
<h3 id="map_enums">Mapping enumerated types (enums)</h3>
<p>Enumerated types are mapped to <a href="Netnumber.html#TYPEint4"><code class="code">Netnumber.int4</code></a> (always, regardless of
what the <code class="code">-int</code> option specifies). The enumerated constants are mapped
to let-bound values of the same name. Example: The XDR definition</p>
<pre class="codepre"><code class="code">enum e {
A = 1;
B = 2;
}
</code></pre>
<p>generates the following lines of code in the auxiliary module:</p>
<pre class="codepre"><code class="code">type e = Netnumber.int4;;
val a : Netnumber.int4;;
val b : Netnumber.int4;;
</code></pre>
<p>However, when the XDR conversion is performed, it is checked whether values of
enumerators are contained in the set of allowed values.</p>
<p>The special enumerator <code class="code">bool</code> is mapped to the O'Caml type <code class="code">bool</code>.</p>
<h3 id="map_eunions">Mapping union types discriminated by enumerations</h3>
<p>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:</p>
<pre class="codepre"><code class="code">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;
}
</code></pre>
<p>This is mapped to the O'Caml type definitions:</p>
<pre class="codepre"><code class="code">type e = Netnumber.int4;;
type u =
[ `a of Netnumber.int4
| `b of Netnumber.int8
| `c of string
| `d of string
]
</code></pre>
<p>Note that the identifiers of the components (<code class="code">discr</code>, <code class="code">x</code>, <code class="code">y</code>, <code class="code">z</code>)
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 <code class="code">default</code> and to define these
cases explicitly.</p>
<h3 id="map_iunions">Mapping union types discriminated by integers</h3>
<p>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:</p>
<pre class="codepre"><code class="code">union u (int discr) {
case -1:
int x;
case 1:
hyper y;
default:
string z;
}
</code></pre>
<p>This is mapped to the O'Caml type definition:</p>
<pre class="codepre"><code class="code">type u =
[ `__1 of Netnumber.int4
| `_1 of Netnumber.int8
| `default of (Netnumber.int4 * string)
]
</code></pre>
<p>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 <code class="code">`default</code> with two arguments: First the value of
the discriminant, second the value of the default component.</p>
<p>This type of mapping is not recommended, and only provided for
completeness.</p>
<h3 id="map_opts">Mapping option types (*)</h3>
<p>The XDR <code class="code">*</code> type is mapped to the O'Caml <code class="code">option</code> type. Example:</p>
<pre class="codepre"><code class="code">typedef string *s;
</code></pre>
<p>is mapped to</p>
<pre class="codepre"><code class="code">type s = string option
</code></pre>
<h3 id="map_recs">Mapping recursive types</h3>
<p>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:</p>
<pre class="codepre"><code class="code">typedef intlistbody *intlist; /* Forward reference */
typedef struct {
int value;
intlist next;
} intlistbody;
</code></pre>
<p>This is mapped to:</p>
<pre class="codepre"><code class="code">type intlist = intlistbody option
and intlistbody =
{ value : Netnumber.int4;
next : intlist;
}
</code></pre>
<p>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 <code class="code">ocamlrpcgen</code>.</p>
<h2 id="lib">Overview over the RPC library</h2>
<p>Normally, only the following modules are of interest:</p>
<ul>
<li><a href="Netnumber.html"><code class="code">Netnumber</code></a>: Supports serialization/deserialization of the
basic integer and fp types</li>
<li><a href="Rpc.html"><code class="code">Rpc</code></a>: Contains some types needed everyhwere</li>
<li><a href="Rpc_client.html"><code class="code">Rpc_client</code></a>: Contains the functions supporting RPC clients</li>
<li><a href="Rpc_server.html"><code class="code">Rpc_server</code></a>: Contains the functions supporting RPC servers</li>
<li><a href="Rpc_portmapper.html"><code class="code">Rpc_portmapper</code></a>: Functions to contact the portmapper service</li>
<li><a href="Rpc_auth_sys.html"><code class="code">Rpc_auth_sys</code></a>: AUTH_SYS style authentication.</li>
</ul>
<h2 id="rpc_netplex">Netplex RPC systems</h2>
<p>If you need multi-processing for your RPC program, the Netplex library
might be a good solution (see <a href="Netplex_intro.html"><code class="code">Netplex_intro</code></a>). It is limited to
stream connections (TCP), however. With Netplex it is possible to
develop systems of RPC services that connect to each other to do a
certain job. Effectively, Netplex supports a component-based approach
comparable to Corba, DCOM or Java Beans, but much more lightweight and
efficient. In the following we call our technology <b>Netplex RPC
systems</b>.</p>
<p>In this section it is assumed that you are familiar with the Netplex
concepts (see <a href="Netplex_intro.html"><code class="code">Netplex_intro</code></a> for an introduction).</p>
<p>The module <a href="Rpc_netplex.html"><code class="code">Rpc_netplex</code></a> (part of the <code class="code">netplex</code> findlib library)
allows us to encapsulate RPC servers as Netplex services. For instance,
to turn the <code class="code">calculate.x</code> example of above into a service we can do</p>
<pre class="codepre"><code class="code">let factory =
Rpc_netplex.rpc_factory
~name:"Calculate"
~configure:(fun _ _ -> ())
~setup:(fun srv () ->
Calculate_srv.bind
~proc_add: add
srv
)
()
</code></pre>
<p>and pass this <code class="code">factory</code> to <a href="Netplex_main.html#VALstartup"><code class="code">Netplex_main.startup</code></a>. Note that we have
to generate <code class="code">calculate_srv.ml</code> with the <code class="code">-srv2</code> switch of
<code class="code">ocamlrpcgen</code>, otherwise <code class="code">Calculate_srv.bind</code> is not available.</p>
<p>In the netplex config file we can refer to (and enable) this service
by a section like</p>
<pre class="codepre"><code class="code"> service {
name = "Calculate_service" (* An arbitrary service name *)
protocol {
name = "Calculate_proto" (* An arbitrary protocol name *)
address {
type = "internet";
bind = "0.0.0.0:2123"
}
};
processor {
type = "Calculate" (* The ~name from above *)
};
workload_manager {
type = "constant";
threads = 1; (* Run in 1 process/thread *)
};
}
</code></pre>
<p>The interesting points of this technology are:</p>
<ul>
<li>You can bundle several services into one program. The services can be
RPC-implemented or by using other protocol modules that are compatible
with Netplex like the <a href="Nethttpd_plex.html"><code class="code">Nethttpd_plex</code></a> web server.</li>
<li>You can profit from multi-processing or multi-threading.</li>
<li>Netplex provides a framework for logging, a message bus, and start/stop.</li>
</ul>
<p>Currently, there is no directory service where one can register
services by name and look them up. Such a service is under development,
however, and will be released once the major functions work.</p>
<h2 id="restrictions">Restrictions of the current implementation</h2>
<p>The authentication styles AUTH_DH and AUTH_LOCAL are not yet supported
on all platforms.</p>
<p>The implementation uses an intermediate, symbolic representation of
the values to transport over the network. This may restrict the
performance.</p>
<p>Quadruple-precision fp numbers are not supported.</p>
<p>RPC broadcasts are not supported.</p>
<p>TI-RPC and rpcbind versions 3 and 4 are not supported. (Note: There
is some restricted support to contact existing TI-RPC servers over
local transport in the <code class="code">Rpc_xti</code> module.)</p>
</div>
</body></html>
|