aboutsummaryrefslogtreecommitdiffstats
path: root/system/doc/tutorial/cnode.xmlsrc
blob: 293406160f228cb067b199e84ec39181104fb149 (plain) (blame)
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
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">

<chapter>
  <header>
    <copyright>
      <year>2000</year><year>2013</year>
      <holder>Ericsson AB. All Rights Reserved.</holder>
    </copyright>
    <legalnotice>
      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 online 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.
    
    </legalnotice>

    <title>C Nodes</title>
    <prepared></prepared>
    <docno></docno>
    <date></date>
    <rev></rev>
    <file>cnode.xml</file>
  </header>
  <p>This is an example of how to solve the <seealso marker="example">example problem</seealso> 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>

  <section>
    <title>Erlang Program</title>
    <p>From Erlang's point of view, the C node is treated like a normal Erlang node. Therefore, calling the functions <c>foo</c> and <c>bar</c> 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>
    <pre>
{RegName, Node} ! Msg</pre>
    <p>The node name <c>Node</c> should be the name of the C node. If short node names are used, the plain name of the node will be <c>cN</c> where <c>N</c> 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 <c>c1@idril</c>, an example using long node names is <c>[email protected]</c>.</p>
    <p>The registered name <c>RegName</c> 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>
    <codeinclude file="complex3.erl" tag="" type="erl"></codeinclude>
    <p>
      When using long node names the code is slightly different as shown in the following example:
    </p>
    <codeinclude file="complex4.erl" tag="" type="erl"></codeinclude>

  </section>

  <section>
    <title>C Program</title>

    <section>
      <title>Setting Up the Communication</title>
      <p>Before calling any other Erl_Interface function, the memory handling must be initiated.</p>
      <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 <c>erl_connect_init()</c>.</p>
      <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 <c>c1</c>.        <br></br>

        The second argument is a string defining the magic cookie.        <br></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 <c>erl_connect_xinit()</c>.</p>
      <pre>
erl_connect_xinit("idril", "cnode", "[email protected]",
                  &amp;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 <c>in_addr</c> 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 <c>erl_connect()</c>, which will return an open file descriptor at success.</p>
      <pre>
fd = erl_connect("e1@idril");</pre>
      <p>If the C node acts as a server, it must first create a socket (call <c>bind()</c> and <c>listen()</c>) listening to a certain port number <c>port</c>. It then publishes its name and port number with <c>epmd</c> (the Erlang port mapper daemon, see the man page for <c>epmd</c>).</p>
      <pre>
erl_publish(port);</pre>
      <p>Now the C node server can accept connections from Erlang nodes.</p>
      <pre>
fd = erl_accept(listen, &amp;conn);</pre>
      <p>The second argument to <c>erl_accept</c> is a struct <c>ErlConnect</c> that will contain useful information when a connection has been established; for example, the name of the Erlang node.</p>
    </section>

    <section>
      <title>Sending and Receiving Messages</title>
      <p>The C node can receive a message from Erlang by calling <c>erl_receive msg()</c>. This function reads data from the open file descriptor <c>fd</c> into a buffer and puts the result in an <c>ErlMessage</c> struct <c>emsg</c>. <c>ErlMessage</c> has a field <c>type</c> defining which kind of data was received. In this case the type of interest is <c>ERL_REG_SEND</c> which indicates that Erlang sent a message to a registered process at the C node. The actual message, an <c>ETERM</c>, will be in the <c>msg</c> field.</p>
      <p>It is also necessary to take care of the types <c>ERL_ERROR</c> (an error occurred) and <c>ERL_TICK</c> (alive check from other node, should be ignored). Other possible types indicate process events such as link/unlink and exit.</p>
      <pre>
  while (loop) {

    got = erl_receive_msg(fd, buf, BUFSIZE, &amp;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 <c>ETERM</c> 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 <c>{Function,Arg}</c> determining which function to call with which argument. The result of calling the function is made into an <c>ETERM</c> struct as well and sent back to Erlang using <c>erl_send()</c>, which takes the open file descriptor, a pid and a term as arguments.</p>
      <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 <c>ETERM</c> creating functions (including <c>erl_receive_msg()</c> must be freed.</p>
      <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 looks like the following examples. First a C node server using short node names.</p>
      <codeinclude file="cnode_s.c" type="c"/>
      <p>Below follows a C node server using long node names.</p>
      <codeinclude file="cnode_s2.c" type="c"/>
      <p>And finally we have the code for the C node client.</p>
      <codeinclude file="cnode_c.c" type="c"/>
    </section>
  </section>

  <section>
    <title>Running the Example</title>
    <p>1. Compile the C code, providing the paths to the Erl_Interface include files and libraries, and to the <c>socket</c> and <c>nsl</c> libraries.</p>
    <p>In R5B and later versions of OTP, the <c>include</c> and <c>lib</c> directories are situated under <c>OTPROOT/lib/erl_interface-VSN</c>, where <c>OTPROOT</c> is the root directory of the OTP installation (<c>/usr/local/otp</c> in the example above) and <c>VSN</c> is the version of the <c>erl_interface</c> application (3.2.1 in the example above).      <br></br>

      In R4B and earlier versions of OTP, <c>include</c> and <c>lib</c> are situated under <c>OTPROOT/usr</c>.</p>
    <pre>
      
>  <input>gcc -o cserver \\ </input>
<input>-I/usr/local/otp/lib/erl_interface-3.2.1/include \\ </input>
<input>-L/usr/local/otp/lib/erl_interface-3.2.1/lib \\ </input>
<input>complex.c cnode_s.c \\ </input>
<input>-lerl_interface -lei -lsocket -lnsl</input>

unix> <input>gcc -o cserver2 \\ </input>
<input>-I/usr/local/otp/lib/erl_interface-3.2.1/include \\ </input>
<input>-L/usr/local/otp/lib/erl_interface-3.2.1/lib \\ </input>
<input>complex.c cnode_s2.c \\ </input>
<input>-lerl_interface -lei -lsocket -lnsl</input>

unix> <input>gcc -o cclient \\ </input>
<input>-I/usr/local/otp/lib/erl_interface-3.2.1/include \\ </input>
<input>-L/usr/local/otp/lib/erl_interface-3.2.1/lib \\ </input>
<input>complex.c cnode_c.c \\ </input>
<input>-lerl_interface -lei -lsocket -lnsl</input></pre>
    <p>2. Compile the Erlang code.</p>
    <pre>
unix> <input>erl -compile complex3 complex4</input></pre>
    <p>3. Run the C node server example with short node names.</p>
    <p>Start the C program <c>cserver</c> and Erlang in different windows. <c>cserver</c> 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 <c>e1</c> and must be set to use the same magic cookie as the C node, <c>secretcookie</c>.</p>
    <pre>
unix> <input>cserver 3456</input>

unix> <input>erl -sname e1 -setcookie secretcookie</input>
Erlang (BEAM) emulator version 4.9.1.2
 
Eshell V4.9.1.2  (abort with ^G)
(e1@idril)1> <input>complex3:foo(3).</input>
4
(e1@idril)2> <input>complex3:bar(5).</input>
10</pre>
    <p>4. Run the C node client example. Terminate <c>cserver</c> but not Erlang and start <c>cclient</c>. The Erlang node must be started before the C node client is.</p>
    <pre>
unix> <input>cclient</input>

(e1@idril)3> <input>complex3:foo(3).</input>
4
(e1@idril)4> <input>complex3:bar(5).</input>
10</pre>
    <p>5. Run the C node server, long node names, example.</p>
    <pre>
unix> <input>cserver2 3456</input>

unix> <input>erl -name e1 -setcookie secretcookie</input>
Erlang (BEAM) emulator version 4.9.1.2
 
Eshell V4.9.1.2  (abort with ^G)
([email protected])1> <input>complex4:foo(3).</input>
4
([email protected])2> <input>complex4:bar(5).</input>
10</pre>
  </section>
</chapter>