<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html xmlns:fn="http://www.w3.org/2005/02/xpath-functions"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="stylesheet" href="../otp_doc.css" type="text/css"> <title>Erlang -- C Nodes</title> </head> <body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000"><div id="container"> <script id="js" type="text/javascript" language="JavaScript" src="../js/flipmenu/flipmenu.js"></script><script id="js2" type="text/javascript" src="../js/erlresolvelinks.js"></script><script language="JavaScript" type="text/javascript"> <!-- function getWinHeight() { var myHeight = 0; if( typeof( window.innerHeight ) == 'number' ) { //Non-IE myHeight = window.innerHeight; } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) { //IE 6+ in 'standards compliant mode' myHeight = document.documentElement.clientHeight; } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { //IE 4 compatible myHeight = document.body.clientHeight; } return myHeight; } function setscrollpos() { var objf=document.getElementById('loadscrollpos'); document.getElementById("leftnav").scrollTop = objf.offsetTop - getWinHeight()/2; } function addEvent(obj, evType, fn){ if (obj.addEventListener){ obj.addEventListener(evType, fn, true); return true; } else if (obj.attachEvent){ var r = obj.attachEvent("on"+evType, fn); return r; } else { return false; } } addEvent(window, 'load', setscrollpos); //--></script><div id="leftnav"><div class="innertube"> <img alt="Erlang logo" src="../erlang-logo.png"><br><small><a href="users_guide.html">User's Guide</a><br><a href="../pdf/otp-system-documentation-5.8.5.pdf">PDF</a><br><a href="../index.html">Top</a></small><p><strong>Interoperability Tutorial</strong><br><strong>User's Guide</strong><br><small>Version 5.8.5</small></p> <br><a href="javascript:openAllFlips()">Expand All</a><br><a href="javascript:closeAllFlips()">Contract All</a><p><small><strong>Chapters</strong></small></p> <ul class="flipMenu" imagepath="../js/flipmenu"> <li id="no" title="Introduction" expanded="false">Introduction<ul> <li><a href="introduction.html"> Top of chapter </a></li> <li title="Purpose"><a href="introduction.html#id310395">Purpose</a></li> <li title="Prerequisites"><a href="introduction.html#id308146">Prerequisites</a></li> </ul> </li> <li id="no" title="Overview" expanded="false">Overview<ul> <li><a href="overview.html"> Top of chapter </a></li> <li title="Built-In Mechanisms"><a href="overview.html#id309894">Built-In Mechanisms</a></li> <li title="C and Java Libraries"><a href="overview.html#id310459">C and Java Libraries</a></li> <li title="Standard Protocols"><a href="overview.html#id306836">Standard Protocols</a></li> <li title="IC"><a href="overview.html#id309919">IC</a></li> <li title="Old Applications"><a href="overview.html#id310397">Old Applications</a></li> </ul> </li> <li id="no" title="Problem Example" expanded="false">Problem Example<ul> <li><a href="example.html"> Top of chapter </a></li> <li title="Description"><a href="example.html#id308393">Description</a></li> </ul> </li> <li id="no" title="Ports" expanded="false">Ports<ul> <li><a href="c_port.html"> Top of chapter </a></li> <li title="Erlang Program"><a href="c_port.html#id303981">Erlang Program</a></li> <li title="C Program"><a href="c_port.html#id306775">C Program</a></li> <li title="Running the Example"><a href="c_port.html#id310592">Running the Example</a></li> </ul> </li> <li id="no" title="Erl_Interface" expanded="false">Erl_Interface<ul> <li><a href="erl_interface.html"> Top of chapter </a></li> <li title="Erlang Program"><a href="erl_interface.html#id309007">Erlang Program</a></li> <li title="C Program"><a href="erl_interface.html#id309116">C Program</a></li> <li title="Running the Example"><a href="erl_interface.html#id309995">Running the Example</a></li> </ul> </li> <li id="no" title="Port drivers" expanded="false">Port drivers<ul> <li><a href="c_portdriver.html"> Top of chapter </a></li> <li title="Port Drivers"><a href="c_portdriver.html#id310194">Port Drivers</a></li> <li title="Erlang Program"><a href="c_portdriver.html#id310214">Erlang Program</a></li> <li title="C Driver"><a href="c_portdriver.html#id310332">C Driver</a></li> <li title="Running the Example"><a href="c_portdriver.html#id310807">Running the Example</a></li> </ul> </li> <li id="loadscrollpos" title="C Nodes" expanded="true">C Nodes<ul> <li><a href="cnode.html"> Top of chapter </a></li> <li title="Erlang Program"><a href="cnode.html#id310930">Erlang Program</a></li> <li title="C Program"><a href="cnode.html#id311010">C Program</a></li> <li title="Running the Example"><a href="cnode.html#id311341">Running the Example</a></li> </ul> </li> <li id="no" title="NIFs" expanded="false">NIFs<ul> <li><a href="nif.html"> Top of chapter </a></li> <li title="NIFs"><a href="nif.html#id311644">NIFs</a></li> <li title="Erlang Program"><a href="nif.html#id311665">Erlang Program</a></li> <li title="NIF library code"><a href="nif.html#id311729">NIF library code</a></li> <li title="Running the Example"><a href="nif.html#id311830">Running the Example</a></li> </ul> </li> </ul> </div></div> <div id="content"> <div class="innertube"> <h1>7 C Nodes</h1> <p>This is an example of how to solve the <span class="bold_code"><a href="example.html">example problem</a></span> 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.</p> <h3><a name="id310930">7.1 Erlang Program</a></h3> <p>From Erlang's point of view, the C node is treated like a normal Erlang node. Therefore, calling the functions <span class="code">foo</span> and <span class="code">bar</span> 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.</p> <div class="example"><pre> {RegName, Node} ! Msg</pre></div> <p>The node name <span class="code">Node</span> should be the name of the C node. If short node names are used, the plain name of the node will be <span class="code">cN</span> where <span class="code">N</span> 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 <span class="code">c1@idril</span>, an example using long node names is <span class="code">cnode@idril.ericsson.se</span>.</p> <p>The registered name <span class="code">RegName</span> 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. </p> <div class="example"><pre> -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. </pre></div> <p> When using long node names the code is slightly different as shown in the following example: </p> <div class="example"><pre> -module(complex4). -export([foo/1, bar/1]). foo(X) -> call_cnode({foo, X}). bar(Y) -> call_cnode({bar, Y}). call_cnode(Msg) -> {any, 'cnode@idril.du.uab.ericsson.se'} ! {call, self(), Msg}, receive {cnode, Result} -> Result end. </pre></div> <h3><a name="id311010">7.2 C Program</a></h3> <h4>Setting Up the Communication</h4> <p>Before calling any other Erl_Interface function, the memory handling must be initiated.</p> <div class="example"><pre> erl_init(NULL, 0);</pre></div> <p>Now the C node can be initiated. If short node names are used, this is done by calling <span class="code">erl_connect_init()</span>.</p> <div class="example"><pre> erl_connect_init(1, "secretcookie", 0);</pre></div> <p>The first argument is the integer which is used to construct the node name. In the example the plain node name will be <span class="code">c1</span>. <br> The second argument is a string defining the magic cookie. <br> The third argument is an integer which is used to identify a particular instance of a C node.</p> <p>If long node node names are used, initiation is done by calling <span class="code">erl_connect_xinit()</span>.</p> <div class="example"><pre> erl_connect_xinit("idril", "cnode", "cnode@idril.ericsson.se", &addr, "secretcookie", 0);</pre></div> <p>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 <span class="code">in_addr</span> struct with the IP address of the host, and the fifth and sixth arguments are the magic cookie and instance number.</p> <p>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 <span class="code">erl_connect()</span>, which will return an open file descriptor at success.</p> <div class="example"><pre> fd = erl_connect("e1@idril");</pre></div> <p>If the C node acts as a server, it must first create a socket (call <span class="code">bind()</span> and <span class="code">listen()</span>) listening to a certain port number <span class="code">port</span>. It then publishes its name and port number with <span class="code">epmd</span> (the Erlang port mapper daemon, see the man page for <span class="code">epmd</span>).</p> <div class="example"><pre> erl_publish(port);</pre></div> <p>Now the C node server can accept connections from Erlang nodes.</p> <div class="example"><pre> fd = erl_accept(listen, &conn);</pre></div> <p>The second argument to <span class="code">erl_accept</span> is a struct <span class="code">ErlConnect</span> that will contain useful information when a connection has been established; for example, the name of the Erlang node.</p> <h4>Sending and Receiving Messages</h4> <p>The C node can receive a message from Erlang by calling <span class="code">erl_receive msg()</span>. This function reads data from the open file descriptor <span class="code">fd</span> into a buffer and puts the result in an <span class="code">ErlMessage</span> struct <span class="code">emsg</span>. <span class="code">ErlMessage</span> has a field <span class="code">type</span> defining which kind of data was received. In this case the type of interest is <span class="code">ERL_REG_SEND</span> which indicates that Erlang sent a message to a registered process at the C node. The actual message, an <span class="code">ETERM</span>, will be in the <span class="code">msg</span> field.</p> <p>It is also necessary to take care of the types <span class="code">ERL_ERROR</span> (an error occurred) and <span class="code">ERL_TICK</span> (alive check from other node, should be ignored). Other possible types indicate process events such as link/unlink and exit.</p> <div class="example"><pre> 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) {</pre></div> <p>Since the message is an <span class="code">ETERM</span> 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 <span class="code">{Function,Arg}</span> determining which function to call with which argument. The result of calling the function is made into an <span class="code">ETERM</span> struct as well and sent back to Erlang using <span class="code">erl_send()</span>, which takes the open file descriptor, a pid and a term as arguments.</p> <div class="example"><pre> 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);</pre></div> <p>Finally, the memory allocated by the <span class="code">ETERM</span> creating functions (including <span class="code">erl_receive_msg()</span> must be freed.</p> <div class="example"><pre> 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);</pre></div> <p>The resulting C programs can be found in looks like the following examples. First a C node server using short node names.</p> <div class="example"><pre> /* cnode_s.c */ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include "erl_interface.h" #include "ei.h" #define BUFSIZE 1000 int main(int argc, char **argv) { int port; /* Listen port number */ int listen; /* Listen socket */ int fd; /* fd to Erlang node */ ErlConnect conn; /* Connection data */ int loop = 1; /* Loop flag */ int got; /* Result of receive */ unsigned char buf[BUFSIZE]; /* Buffer for incoming message */ ErlMessage emsg; /* Incoming message */ ETERM *fromp, *tuplep, *fnp, *argp, *resp; int res; port = atoi(argv[1]); erl_init(NULL, 0); if (erl_connect_init(1, "secretcookie", 0) == -1) erl_err_quit("erl_connect_init"); /* Make a listen socket */ if ((listen = my_listen(port)) <= 0) erl_err_quit("my_listen"); if (erl_publish(port) == -1) erl_err_quit("erl_publish"); if ((fd = erl_accept(listen, &conn)) == ERL_ERROR) erl_err_quit("erl_accept"); fprintf(stderr, "Connected to %s\n\r", conn.nodename); while (loop) { got = erl_receive_msg(fd, buf, BUFSIZE, &emsg); if (got == ERL_TICK) { /* ignore */ } else if (got == ERL_ERROR) { loop = 0; } else { if (emsg.type == ERL_REG_SEND) { 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); 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); } } } /* while */ } int my_listen(int port) { int listen_fd; struct sockaddr_in addr; int on = 1; if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return (-1); setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); memset((void*) &addr, 0, (size_t) sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(listen_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) return (-1); listen(listen_fd, 5); return listen_fd; } </pre></div> <p>Below follows a C node server using long node names.</p> <div class="example"><pre> /* cnode_s2.c */ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include "erl_interface.h" #include "ei.h" #define BUFSIZE 1000 int main(int argc, char **argv) { struct in_addr addr; /* 32-bit IP number of host */ int port; /* Listen port number */ int listen; /* Listen socket */ int fd; /* fd to Erlang node */ ErlConnect conn; /* Connection data */ int loop = 1; /* Loop flag */ int got; /* Result of receive */ unsigned char buf[BUFSIZE]; /* Buffer for incoming message */ ErlMessage emsg; /* Incoming message */ ETERM *fromp, *tuplep, *fnp, *argp, *resp; int res; port = atoi(argv[1]); erl_init(NULL, 0); addr.s_addr = inet_addr("134.138.177.89"); if (erl_connect_xinit("idril", "cnode", "cnode@idril.du.uab.ericsson.se", &addr, "secretcookie", 0) == -1) erl_err_quit("erl_connect_xinit"); /* Make a listen socket */ if ((listen = my_listen(port)) <= 0) erl_err_quit("my_listen"); if (erl_publish(port) == -1) erl_err_quit("erl_publish"); if ((fd = erl_accept(listen, &conn)) == ERL_ERROR) erl_err_quit("erl_accept"); fprintf(stderr, "Connected to %s\n\r", conn.nodename); while (loop) { got = erl_receive_msg(fd, buf, BUFSIZE, &emsg); if (got == ERL_TICK) { /* ignore */ } else if (got == ERL_ERROR) { loop = 0; } else { if (emsg.type == ERL_REG_SEND) { 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); 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); } } } } int my_listen(int port) { int listen_fd; struct sockaddr_in addr; int on = 1; if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return (-1); setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); memset((void*) &addr, 0, (size_t) sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(listen_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) return (-1); listen(listen_fd, 5); return listen_fd; } </pre></div> <p>And finally we have the code for the C node client.</p> <div class="example"><pre> /* cnode_c.c */ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include "erl_interface.h" #include "ei.h" #define BUFSIZE 1000 int main(int argc, char **argv) { int fd; /* fd to Erlang node */ int loop = 1; /* Loop flag */ int got; /* Result of receive */ unsigned char buf[BUFSIZE]; /* Buffer for incoming message */ ErlMessage emsg; /* Incoming message */ ETERM *fromp, *tuplep, *fnp, *argp, *resp; int res; erl_init(NULL, 0); if (erl_connect_init(1, "secretcookie", 0) == -1) erl_err_quit("erl_connect_init"); if ((fd = erl_connect("e1@idril")) < 0) erl_err_quit("erl_connect"); fprintf(stderr, "Connected to ei@idril\n\r"); while (loop) { got = erl_receive_msg(fd, buf, BUFSIZE, &emsg); if (got == ERL_TICK) { /* ignore */ } else if (got == ERL_ERROR) { loop = 0; } else { if (emsg.type == ERL_REG_SEND) { 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); 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); } } } } </pre></div> <h3><a name="id311341">7.3 Running the Example</a></h3> <p>1. Compile the C code, providing the paths to the Erl_Interface include files and libraries, and to the <span class="code">socket</span> and <span class="code">nsl</span> libraries.</p> <p>In R5B and later versions of OTP, the <span class="code">include</span> and <span class="code">lib</span> directories are situated under <span class="code">OTPROOT/lib/erl_interface-VSN</span>, where <span class="code">OTPROOT</span> is the root directory of the OTP installation (<span class="code">/usr/local/otp</span> in the example above) and <span class="code">VSN</span> is the version of the <span class="code">erl_interface</span> application (3.2.1 in the example above). <br> In R4B and earlier versions of OTP, <span class="code">include</span> and <span class="code">lib</span> are situated under <span class="code">OTPROOT/usr</span>.</p> <div class="example"><pre> > <span class="bold_code">gcc -o cserver \\ </span> <span class="bold_code">-I/usr/local/otp/lib/erl_interface-3.2.1/include \\ </span> <span class="bold_code">-L/usr/local/otp/lib/erl_interface-3.2.1/lib \\ </span> <span class="bold_code">complex.c cnode_s.c \\ </span> <span class="bold_code">-lerl_interface -lei -lsocket -lnsl</span> unix> <span class="bold_code">gcc -o cserver2 \\ </span> <span class="bold_code">-I/usr/local/otp/lib/erl_interface-3.2.1/include \\ </span> <span class="bold_code">-L/usr/local/otp/lib/erl_interface-3.2.1/lib \\ </span> <span class="bold_code">complex.c cnode_s2.c \\ </span> <span class="bold_code">-lerl_interface -lei -lsocket -lnsl</span> unix> <span class="bold_code">gcc -o cclient \\ </span> <span class="bold_code">-I/usr/local/otp/lib/erl_interface-3.2.1/include \\ </span> <span class="bold_code">-L/usr/local/otp/lib/erl_interface-3.2.1/lib \\ </span> <span class="bold_code">complex.c cnode_c.c \\ </span> <span class="bold_code">-lerl_interface -lei -lsocket -lnsl</span></pre></div> <p>2. Compile the Erlang code.</p> <div class="example"><pre> unix> <span class="bold_code">erl -compile complex3 complex4</span></pre></div> <p>3. Run the C node server example with short node names.</p> <p>Start the C program <span class="code">cserver</span> and Erlang in different windows. <span class="code">cserver</span> 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 name <span class="code">e1</span> and must be set to use the same magic cookie as the C node, <span class="code">secretcookie</span>.</p> <div class="example"><pre> unix> <span class="bold_code">cserver 3456</span> unix> <span class="bold_code">erl -sname e1 -setcookie secretcookie</span> Erlang (BEAM) emulator version 4.9.1.2 Eshell V4.9.1.2 (abort with ^G) (e1@idril)1> <span class="bold_code">complex3:foo(3).</span> 4 (e1@idril)2> <span class="bold_code">complex3:bar(5).</span> 10</pre></div> <p>4. Run the C node client example. Terminate <span class="code">cserver</span> but not Erlang and start <span class="code">cclient</span>. The Erlang node must be started before the C node client is.</p> <div class="example"><pre> unix> <span class="bold_code">cclient</span> (e1@idril)3> <span class="bold_code">complex3:foo(3).</span> 4 (e1@idril)4> <span class="bold_code">complex3:bar(5).</span> 10</pre></div> <p>5. Run the C node server, long node names, example.</p> <div class="example"><pre> unix> <span class="bold_code">cserver2 3456</span> unix> <span class="bold_code">erl -name e1 -setcookie secretcookie</span> Erlang (BEAM) emulator version 4.9.1.2 Eshell V4.9.1.2 (abort with ^G) (e1@idril.du.uab.ericsson.se)1> <span class="bold_code">complex4:foo(3).</span> 4 (e1@idril.du.uab.ericsson.se)2> <span class="bold_code">complex4:bar(5).</span> 10</pre></div> </div> <div class="footer"> <hr> <p>Copyright © 2000-2011 Ericsson AB. All Rights Reserved.</p> </div> </div> </div></body> </html>