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
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- This document was generated using DocBuilder 3.3.3 -->
<HTML>
<HEAD>
<TITLE>C Nodes</TITLE>
<SCRIPT type="text/javascript" src="../../doc/erlresolvelinks.js">
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#FF00FF"
ALINK="#FF0000">
<CENTER>
<A HREF="http://www.erlang.se"><IMG BORDER=0 ALT="[Ericsson AB]" SRC="min_head.gif"></A>
</CENTER>
<A NAME="7"><!-- Empty --></A>
<H2>7 C Nodes</H2>
<P>This is an example of how to solve the <A HREF="example.html">example problem</A> 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.<A NAME="7.1"><!-- Empty --></A>
<H3>7.1 Erlang Program</H3>
<P>From Erlang's point of view, the C node is treated like a normal Erlang node. Therefore, calling the functions <CODE>foo</CODE> and <CODE>bar</CODE> 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.
<PRE>
{RegName, Node} ! Msg
</PRE>
<P>The node name <CODE>Node</CODE> should be the name of the C node. If short node names are used, the plain name of the node will be <CODE>cN</CODE> where <CODE>N</CODE> 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 <CODE>c1@idril</CODE>, an example using long node names is <CODE>cnode@idril.ericsson.se</CODE>.
<P>The registered name <CODE>RegName</CODE> 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,
<A TARGET="_top" HREF="complex3.erl">complex3.erl.</A>
<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>
<P>In <A TARGET="_top" HREF="complex4.erl">complex4.erl.</A>
there is an example of what the Erlang code could look like when using long node names.<A NAME="7.2"><!-- Empty --></A>
<H3>7.2 C Program</H3>
<A NAME="7.2.1"><!-- Empty --></A>
<H4>7.2.1 Setting Up the Communication</H4>
<P>Before calling any other Erl_Interface function, the memory handling must be initiated.
<PRE>
erl_init(NULL, 0);
</PRE>
<P>Now the C node can be initiated. If short node names are used, this is done by calling <CODE>erl_connect_init()</CODE>.
<PRE>
erl_connect_init(1, "secretcookie", 0);
</PRE>
<P>The first argument is the integer which is used to construct the node name. In the example the plain node name will be <CODE>c1</CODE>.<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>If long node node names are used, initiation is done by calling <CODE>erl_connect_xinit()</CODE>.
<PRE>
erl_connect_xinit("idril", "cnode", "cnode@idril.ericsson.se",
&addr, "secretcookie", 0);
</PRE>
<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 <CODE>in_addr</CODE> struct with the IP address of the host, and the fifth and sixth arguments are the magic cookie and instance number.
<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 <CODE>erl_connect()</CODE>, which will return an open file descriptor at success.
<PRE>
fd = erl_connect("e1@idril");
</PRE>
<P>If the C node acts as a server, it must first create a socket (call <CODE>bind()</CODE> and <CODE>listen()</CODE>) listening to a certain port number <CODE>port</CODE>. It then publishes its name and port number with <CODE>epmd</CODE> (the Erlang port mapper daemon, see the man page for <CODE>epmd</CODE>).
<PRE>
erl_publish(port);
</PRE>
<P>Now the C node server can accept connections from Erlang nodes.
<PRE>
fd = erl_accept(listen, &conn);
</PRE>
<P>The second argument to <CODE>erl_accept</CODE> is a struct <CODE>ErlConnect</CODE> that will contain useful information when a connection has been established; for example, the name of the Erlang node.<A NAME="7.2.2"><!-- Empty --></A>
<H4>7.2.2 Sending and Receiving Messages</H4>
<P>The C node can receive a message from Erlang by calling <CODE>erl_receive msg()</CODE>. This function reads data from the open file descriptor <CODE>fd</CODE> into a buffer and puts the result in an <CODE>ErlMessage</CODE> struct <CODE>emsg</CODE>. <CODE>ErlMessage</CODE> has a field <CODE>type</CODE> defining which kind of data was received. In this case the type of interest is <CODE>ERL_REG_SEND</CODE> which indicates that Erlang sent a message to a registered process at the C node. The actual message, an <CODE>ETERM</CODE>, will be in the <CODE>msg</CODE> field.
<P>It is also necessary to take care of the types <CODE>ERL_ERROR</CODE> (an error occurred) and <CODE>ERL_TICK</CODE> (alive check from other node, should be ignored). Other possible types indicate process events such as link/unlink and exit.
<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>
<P>Since the message is an <CODE>ETERM</CODE> 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 <CODE>{Function,Arg}</CODE> determining which function to call with which argument. The result of calling the function is made into an <CODE>ETERM</CODE> struct as well and sent back to Erlang using <CODE>erl_send()</CODE>, which takes the open file descriptor, a pid and a term as arguments.
<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>
<P>Finally, the memory allocated by the <CODE>ETERM</CODE> creating functions (including <CODE>erl_receive_msg()</CODE> must be freed.
<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>
<P>The resulting C programs can be found in
<A TARGET="_top" HREF="cnode_s.c">cnode_s.c</A>
(C node server, short node names),
<A TARGET="_top" HREF="cnode_s2.c">cnode_s2.c</A>
(C node server, long node names) and
<A TARGET="_top" HREF="cnode_c.c">cnode_c.c</A>
(C node client).<A NAME="7.3"><!-- Empty --></A>
<H3>7.3 Running the Example</H3>
<P>1. Compile the C code, providing the paths to the Erl_Interface include files and libraries, and to the <CODE>socket</CODE> and <CODE>nsl</CODE> libraries.
<P>In R5B and later versions of OTP, the <CODE>include</CODE> and <CODE>lib</CODE> directories are situated under <CODE>OTPROOT/lib/erl_interface-VSN</CODE>, where <CODE>OTPROOT</CODE> is the root directory of the OTP installation (<CODE>/usr/local/otp</CODE> in the example above) and <CODE>VSN</CODE> is the version of the <CODE>erl_interface</CODE> application (3.2.1 in the example above).<BR>
In R4B and earlier versions of OTP, <CODE>include</CODE> and <CODE>lib</CODE> are situated under <CODE>OTPROOT/usr</CODE>.
<PRE>
> 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
</PRE>
<P>2. Compile the Erlang code.
<PRE>
unix> erl -compile complex3 complex4
</PRE>
<P>3. Run the C node server example with short node names.
<P>Start the C program <CODE>cserver</CODE> and Erlang in different windows. <CODE>cserver</CODE> 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 <CODE>e1</CODE> and must be set to use the same magic cookie as the C node, <CODE>secretcookie</CODE>.
<PRE>
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
</PRE>
<P>4. Run the C node client example. Terminate <CODE>cserver</CODE> but not Erlang and start <CODE>cclient</CODE>. The Erlang node must be started before the C node client is.
<PRE>
unix> cclient
(e1@idril)3> complex3:foo(3).
4
(e1@idril)4> complex3:bar(5).
10
</PRE>
<P>5. Run the C node server, long node names, example.
<PRE>
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
</PRE>
<CENTER>
<HR>
<SMALL>
Copyright © 1991-2006
<A HREF="http://www.erlang.se">Ericsson AB</A><BR>
</SMALL>
</CENTER>
</BODY>
</HTML>
|