20002015 Ericsson AB. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. C Nodes cnode.xml

This section outlines an example of how to solve the example problem in Problem Example by using a C node. Notice that a C node is not typically used for solving simple problems like this, a port is sufficient.

Erlang Program

From Erlang's point of view, the C node is treated like a normal Erlang node. Thus, calling the functions foo and bar 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, that is, a process that 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} ! Msg

The node name Node is to be the name of the C node. If short node names are used, the plain name of the node is cN, where N 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 thus c1@idril, an example using long node names is cnode@idril.ericsson.se.

The registered name, RegName, can be any atom. The name can be ignored by the C code, or, for example, be used to distinguish between different types of messages. An example of Erlang code using short node names follows:

When using long node names, the code is slightly different as shown in the following example:

C Program
Setting Up Communication

Before calling any other function in Erl_Interface, 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);

Here:

The first argument is the integer used to construct the node name.

In the example, the plain node name is c1.

The second argument is a string defining the magic cookie. The third argument is an integer that 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);

Here:

The first argument is the host name. The second argument is the plain node name. The third argument is the full node name. The fourth argument is a pointer to an in_addr struct with the IP address of the host. The fifth argument is the magic cookie. The sixth argument is the instance number.

The C node can act as a server or a client when setting up the Erlang-C communication. If it acts as a client, it connects to an Erlang node by calling erl_connect(), which returns 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() and listen()) listening to a certain port number port. It then publishes its name and port number with epmd, the Erlang port mapper daemon. For details, see the epmd manual page in ERTS:

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 struct ErlConnect which contains useful information when a connection has been established, for example, the name of the Erlang node.

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 descriptor fd into a buffer and puts the result in an ErlMessage struct emsg. ErlMessage has a field type defining what kind of data is received. In this case, the type of interest is ERL_REG_SEND which indicates that Erlang sent a message to a registered process at the C node. The actual message, an ETERM, is in the msg field.

It is also necessary to take care of the types ERL_ERROR (an error occurred) and ERL_TICK (alive check from other node, is to 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) {

As the message is an ETERM struct, Erl_Interface functions can be used to manipulate it. In this case, the message becomes a 3-tuple, because that is how the Erlang code is written. 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, and with which argument. The result of calling the function is made into an ETERM struct as well and sent back to Erlang using erl_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 (including erl_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 following examples show the resulting C programs. First a C node server using short node names:

A C node server using long node names:

Finally, the code for the C node client:

Running the Example

Step 1. Compile the C code. This provides the paths to the Erl_Interface include files and libraries, and to the socket and nsl libraries:

>  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 -lnsl

In Erlang/OTP R5B and later versions of OTP, the include and lib directories are situated under OTPROOT/lib/erl_interface-VSN, where OTPROOT is the root directory of the OTP installation (/usr/local/otp in the recent example) and VSN is the version of the Erl_Interface application (3.2.1 in the recent example).

In R4B and earlier versions of OTP, include and lib are situated under OTPROOT/usr.

Step 2. Compile the Erlang code:

unix> erl -compile complex3 complex4

Step 3. Run the C node server example with short node names.

Do as follows:

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 is to be given the short name e1 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).
10

Step 4. Run the C node client example. Terminate cserver, but not Erlang, and start cclient. The Erlang node must be started before the C node client:

unix> cclient

(e1@idril)3> complex3:foo(3).
4
(e1@idril)4> complex3:bar(5).
10

Step 5. Run the C node server example with long node names:

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