%% ``The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved via the world wide web at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% VCC - Virtual Channel Communication ----------------------------------- This application provides a generic peer-to-peer TCP transport between applications. On top of each transport channel, virtual channels can be created. Like normal Unix sockets, virtual channels are numbered. Operations (module: vccTransport): start_link(Chan_name, Conn_opts, VC_opts [, Timeout]) -> {ok, Channel} Creates a transport channel to another application. See below for information about available options. msg(Channel, VC_no, To, Msg) -> ok. Sends a message on the virtual channel VC_no. Always succeeds, although there is no guarantee that the message is actually delivered (delivery may fail if the channel is not available at the time.) How the message is encoded depends on the characteristics of the VC. See a more detailed description below. adv_msg(Channel, To, Msg, Opts) -> VC_used New_VC Opts : [VC_opt] VC_opt : {vc, integer()} | {temp_vc, boolean()} | {on_receive, fun/1} | {close, Close_option} Close_option : manually | on_reply Sends a message with advanced semantics. A common use would be to send a message on a temporary VC, which may be automatically removed as soon as a reply message has been delivered. This means that one can set up a 'template' VC, which is used for cloning temporary VCs, somewhat mirroring the functionality of channels in Concurrent Haskell or sockets in UNIX. This would be especially useful in languages that do not have 'selective receive' as in Erlang. await_channel(Channel [, Timeout]) -> pid() | raises fail monitor_channel(Channel) -> MonitorRef demonitor_channel(MonitorRef) -> true get_status(Channel) -> up | down | undefined get_vcs(Channel) -> [VCId] set_vc_options(Channel, VCId, VCOpts) -> true. where_channel(Channel) -> pid() | undefined Detailed specification of channel set up. ----------------------------------------- vccTransport:start_link(Chan_name, Conn_opts, VC_opts [,Timeout]) -> {ok, Channel}. Conn_opts : [Opt] Opt : {host, string()} % mandatory | {port, integer()} % mandatory | {protocol, term()} % mandatory | {my_version, term()} % mandatory | {my_port, integer()} | {start_connector, boolean()} % Def: true | {start_listener, boolean()} % Def: true | {heartbeat, {Limit, Time}} % Def: {3, 30000} | {ranges, Rh_ranges} | Sock_opt % std inet socket opts Rh_ranges : [Range_opt] Range_opt : {static, {Min, Max}} % Def: {1, 16#ffff} |{dynamic, {Min, Max}} % Def: {16#10000, 16#ffffffff} Sock_opt : {nodelay, boolean()} % default: true | {packet, integer()} % default: 4 | {packet_size, integer()} | {delay_send, boolean()} % default: true | {recbuf, integer()} | {sndbuf, integer()} | {fd, Fd} | {ip, IPAddr} | {keepalive, boolean()} % default: true | {connect_from_port, integer()} | inet | inet6 VC_opts : [Auto_VC] Auto_VC : {VC_no: integer(), StartF: fun/2} VC_option: | {remote_id, integer()} | {on_receive, fun/3} | {on_send, fun/2} | {encode, fun/1} | {decode, fun/1} | {transforms, [Transform]} Transform: {FromVsn, ToVsn, fun/1} The VC options are usually symmetric in terms of encode/decode and on_send/on_receive, but this is up to the designer of each channel. The two ends of the channel may well be implemented in different programming languages. For erlang-to-erlang communication, the following is an intuitive encode/decode specification: {encode, fun(Msg) -> term_to_binary(Msg) end} {decode, fun(Bin) -> binary_to_term(Bin) end} The 'on_send' function is called with two arguments, To and Msg. Vcc poses no restriction on these two arguments; they are simply passed along from the msg(Chan, VC, To, Msg) function. A simple 'on_send' function might be: {on_send, fun(To, Msg) -> {To, Msg} end} A corresponding 'on_receive' function might be: {on_receive, fun(_VC, _SendF, {To, Msg}) -> To ! Msg end} On the receiving end, a similar set of functions are applied: OnRecv(VC_id, SendFun, Xform(Decode(BinaryMsg))) (assuming that the remote end is a vcc application.) The SendFun can be passed on to the final recipient, and can be used later to send a message back on the same VC. This can be used e.g. in an RPC-type scenario: start() -> spawn(fun() -> register(self(), my_rex), rex_loop() end). rex_loop() -> receive {ReplyF, {apply, M, F, A}} -> Res = (catch apply(M, F, A)), ReplyF(Res), rex_loop() end. The OnRecv function in this scenario might look as follows: fun(_VC, SendFun, Msg) -> my_rex ! {SendFun, Msg} end. Version-specific transformations -------------------------------- The purpose of transformations is to manage small changes in the message format "under the covers". This can free the final recipient of the message from having to support old message formats along with the new ones. It also reduces the number of places where transformation takes place, possibly simplifying the verification of an upgrade. The easiest way to use vcc is to specify the same version on both sides of the communication channel. If the versions differ, vcc expects to find a transformation path from one version to the other. This path may consist of chaining several transformation steps into one. Example: Side A uses version "1.0" Side B uses version "3.2" [{transforms, [{"1.0", "1.5", fun(Msg) -> f1(Msg) end}, {"1.5", "3.0", fun(Msg) -> f2(Msg) end}, {"3.0", "3.2", fun(Msg) -> Msg) end}, {"3.2", "1.0" fun(Msg) -> f21(Msg) end}] The is no need to specify the entity transform, as vcc assumes that no transformation is needed when using the same version on both sides. Note that if the above list is used on both sides, the transformation steps in one direction will be "1.0" -> "1.5" -> "3.0" -> "3.2" while in the other direction: "3.2" -> "1.0" msg(Channel, VC_no, To, Msg) -> ok. Sends a message on the virtual channel VC_no. Always succeeds, although there is no guarantee that the message is actually delivered (delivery may fail if the channel is not available at the time.) How the message is encoded depends on the characteristics of the VC. Each channel has a custom set of funs that are applied to the message: - Send (To, Msg) - Xform (Msg1) - Encode (Msg2) These are applied as follows: BinaryMsg = Encode(Xform(OnSend(To, Msg))) (Note that the funs are user-provided. Vcc poses very few restrictions on them, except to assume that the end result will be an io list. For example, the OnSend function may well ignore the To argument, if it is known that all messages on the given VC go to the same recipient. BinaryMsg is then wrapped with a small header in order to let the remote end know how to treat the package.