6 C Nodes
This is an example of how to solve the example problem by using a C node. Note that a C node would not typically be used for solving a simple problem like this, a port would suffice.
6.1 Erlang Program
From Erlang's point of view, the C node is treated like a normal Erlang node. Therefore, calling the functions
foo
andbar
only involves sending a message to the C node asking for the function to be called, and receiving the result. Sending a message requires a recipient; a process which can be defined using either a pid or a tuple consisting of a registered name and a node name. In this case a tuple is the only alternative as no pid is known.{RegName, Node} ! MsgThe node name
Node
should be the name of the C node. If short node names are used, the plain name of the node will becN
whereN
is an integer. If long node names are used, there is no such restriction. An example of a C node name using short node names is thusc1@idril
, an example using long node names iscnode@idril.ericsson.se
.The registered name
RegName
could be any atom. The name can be ignored by the C code, or it could be used for example to distinguish between different types of messages. Below is an example of what the Erlang code could look like when using short node names, complex3.erl.-module(complex3). -export([foo/1, bar/1]). foo(X) -> call_cnode({foo, X}). bar(Y) -> call_cnode({bar, Y}). call_cnode(Msg) -> {any, c1@idril} ! {call, self(), Msg}, receive {cnode, Result} -> Result end.In complex4.erl. there is an example of what the Erlang code could look like when using long node names.
6.2 C Program
6.2.1 Setting Up the Communication
Before calling any other
erl_interface
function, the memory handling must be initiated.erl_init(NULL, 0);Now the C node can be initiated. If short node names are used, this is done by calling
erl_connect_init()
.erl_connect_init(1, "secretcookie", 0);The first argument is the integer which is used to construct the node name. In the example the plain node name will be
c1
.
The second argument is a string defining the magic cookie.
The third argument is an integer which is used to identify a particular instance of a C node.If long node node names are used, initiation is done by calling
erl_connect_xinit()
.erl_connect_xinit("idril", "cnode", "cnode@idril.ericsson.se", &addr, "secretcookie", 0);The first three arguments are the host name, the plain node name, and the full node name. The fourth argument is a pointer to an
in_addr
struct with the IP address of the host, and the fifth and sixth arguments are the magic cookie and instance number.The C node can act as a server or a client when setting up the communication Erlang-C. If it acts as a client, it connects to an Erlang node by calling
erl_connect()
, which will return an open file descriptor at success.fd = erl_connect("e1@idril");If the C node acts as a server, it must first create a socket (call
bind()
andlisten()
) listening to a certain port numberport
. It then publishes its name and port number withepmd
(the Erlang port mapper daemon, see the man page forepmd
).erl_publish(port);Now the C node server can accept connections from Erlang nodes.
fd = erl_accept(listen, &conn);The second argument to
erl_accept
is a structErlConnect
that will contain useful information when a connection has been established; for example, the name of the Erlang node.6.2.2 Sending and Receiving Messages
The C node can receive a message from Erlang by calling
erl_receive msg()
. This function reads data from the open file descriptorfd
into a buffer and puts the result in anErlMessage
structemsg
.ErlMessage
has a fieldtype
defining which kind of data was received. In this case the type of interest isERL_REG_SEND
which indicates that Erlang sent a message to a registered process at the C node. The actual message, anETERM
, will be in themsg
field.It is also necessary to take care of the types
ERL_ERROR
(an error occurred) andERL_TICK
(alive check from other node, should be ignored). Other possible types indicate process events such as link/unlink and exit.while (loop) { got = erl_receive_msg(fd, buf, BUFSIZE, &emsg); if (got == ERL_TICK) { /* ignore */ } else if (got == ERL_ERROR) { loop = 0; /* exit while loop */ } else { if (emsg.type == ERL_REG_SEND) {Since the message is an
ETERM
struct,erl_interface
functions can be used to manipulate it. In this case, the message will be a 3-tuple (because that was how the Erlang code was written, see above). The second element will be the pid of the caller and the third element will be the tuple{Function,Arg}
determining which function to call with which argument. The result of calling the function is made into anETERM
struct as well and sent back to Erlang usingerl_send()
, which takes the open file descriptor, a pid and a term as arguments.fromp = erl_element(2, emsg.msg); tuplep = erl_element(3, emsg.msg); fnp = erl_element(1, tuplep); argp = erl_element(2, tuplep); if (strncmp(ERL_ATOM_PTR(fnp), "foo", 3) == 0) { res = foo(ERL_INT_VALUE(argp)); } else if (strncmp(ERL_ATOM_PTR(fnp), "bar", 3) == 0) { res = bar(ERL_INT_VALUE(argp)); } resp = erl_format("{cnode, ~i}", res); erl_send(fd, fromp, resp);Finally, the memory allocated by the
ETERM
creating functions (includingerl_receive_msg()
must be freed.erl_free_term(emsg.from); erl_free_term(emsg.msg); erl_free_term(fromp); erl_free_term(tuplep); erl_free_term(fnp); erl_free_term(argp); erl_free_term(resp);The resulting C programs can be found in cnode_s.c (C node server, short node names), cnode_s2.c (C node server, long node names) and cnode_c.c (C node client).
6.3 Running the Example
1. Compile the C code, providing the paths to the
erl_interface
include files and libraries, and to thesocket
andnsl
libraries.In R5B and later versions of OTP, the
include
andlib
directories are situated underOTPROOT/lib/erl_interface-VSN
, where OTPROOT is the root directory of the OTP installation (/usr/local/otp
in the example above) and VSN is the version of theerl_interface
application (3.2.1 in the example above).
In R4B and earlier versions of OTP,include
andlib
are situated underOTPROOT/usr
.> gcc -o cserver \ -I/usr/local/otp/lib/erl_interface-3.2.1/include \ -L/usr/local/otp/lib/erl_interface-3.2.1/lib \ complex.c cnode_s.c \ -lerl_interface -lei -lsocket -lnsl unix> gcc -o cserver2 \ -I/usr/local/otp/lib/erl_interface-3.2.1/include \ -L/usr/local/otp/lib/erl_interface-3.2.1/lib \ complex.c cnode_s2.c \ -lerl_interface -lei -lsocket -lnsl unix> gcc -o cclient \ -I/usr/local/otp/lib/erl_interface-3.2.1/include \ -L/usr/local/otp/lib/erl_interface-3.2.1/lib \ complex.c cnode_c.c \ -lerl_interface -lei -lsocket -lnsl2. Compile the Erlang code.
unix> erl -compile complex3 complex43. Run the C node server example with short node names.
Start the C program
cserver
and Erlang in different windows.cserver
takes a port number as argument and must be started before trying to call the Erlang functions. The Erlang node should be given the short namee1
and must be set to use the same magic cookie as the C node,secretcookie
.unix> cserver 3456 unix> erl -sname e1 -setcookie secretcookie Erlang (BEAM) emulator version 4.9.1.2 Eshell V4.9.1.2 (abort with ^G) (e1@idril)1> complex3:foo(3). 4 (e1@idril)2> complex3:bar(5). 104. Run the C node client example. Terminate
cserver
but not Erlang and startcclient
. The Erlang node must be started before the C node client is.unix> cclient (e1@idril)3> complex3:foo(3). 4 (e1@idril)4> complex3:bar(5). 105. Run the C node server, long node names, example.
unix> cserver2 3456 unix> erl -name e1 -setcookie secretcookie Erlang (BEAM) emulator version 4.9.1.2 Eshell V4.9.1.2 (abort with ^G) (e1@idril.du.uab.ericsson.se)1> complex4:foo(3). 4 (e1@idril.du.uab.ericsson.se)2> complex4:bar(5). 10