… | |
… | |
8 | |
8 | |
9 | What kind of messages? Well, basically a message here means a list of Perl |
9 | What kind of messages? Well, basically a message here means a list of Perl |
10 | strings, numbers, hashes and arrays, anything that can be expressed as a |
10 | strings, numbers, hashes and arrays, anything that can be expressed as a |
11 | L<JSON> text (as JSON is used by default in the protocol). |
11 | L<JSON> text (as JSON is used by default in the protocol). |
12 | |
12 | |
|
|
13 | It's custom in L<AnyEvent::MP> to have a string which describes the type of the |
|
|
14 | message as first element (this is called a I<tag> in L<AnyEvent::MP>), as some |
|
|
15 | API functions (C<rcv>) support matching it directly. So supposedly you want to |
|
|
16 | send a ping message with your current time to something, this is how such a |
|
|
17 | message might look like (in Perl syntax): |
|
|
18 | |
|
|
19 | ['ping', 1251381636] |
|
|
20 | |
13 | And next you might ask: between which entities are those messages being |
21 | And next you might ask: between which entities are those messages being |
14 | "passed"? Physically within or between I<nodes>: a nodes is basically a |
22 | I<passed>? They are I<passed> between I<ports>. I<ports> are just sources and |
15 | process/program that use L<AnyEvent::MP> and can run either on the same or |
23 | destinations for messages. How do these ports relate to things you know? Well, |
16 | different hosts. |
24 | each I<port> belongs to a I<node>, and a I<node> is just the UNIX process that |
|
|
25 | runs your L<AnyEvent::MP> application. |
17 | |
26 | |
18 | To make this more manageable, every node can contain any number of |
27 | Each I<node> is distinguished from other I<nodes> running on the same host or |
19 | I<ports>: Ports are ultimately the receivers of your messages. |
28 | multiple hosts in a network by it's I<node ID>. A I<node ID> can be manually |
|
|
29 | assigned or L<AnyEvent::MP> will assign one it self for you. |
20 | |
30 | |
|
|
31 | So, you might want to visualize it like this (setup is two nodes: Node C<A> (in |
|
|
32 | UNIX process 7066) with ports C<ABC> and C<DEF> and C<B> (in UNIX process 8321) |
|
|
33 | with ports C<FOO> and C<BAR>). |
|
|
34 | |
|
|
35 | |
|
|
36 | |- PID: 7066 -| |- PID: 8321 -| |
|
|
37 | | | | | |
|
|
38 | | Node ID: A | | Node ID: B | |
|
|
39 | | | | | |
|
|
40 | | Port ABC =|= <----\ /-----> =|= Port FOO | |
|
|
41 | | | X | | |
|
|
42 | | Port DEF =|= <----/ \-----> =|= Port BAR | |
|
|
43 | | | | | |
|
|
44 | |-------------| |-------------| |
|
|
45 | |
|
|
46 | The strings for the ports here are just for illustrative purposes. Even if in |
|
|
47 | reality I<ports> in L<AnyEvent::MP> are also identified by strings they can't |
|
|
48 | be choosen manually and are assigned randomly. These I<port ids> should also |
|
|
49 | not be used directly for other purposes than refering to an endpoint for |
|
|
50 | messages. |
|
|
51 | |
|
|
52 | The next sections will explain the API of L<AnyEvent::MP>. First the API is |
|
|
53 | layed out by simple examples. Later some more complex idioms are introduced, |
|
|
54 | which are maybe useful to solve some realworld purposes. |
|
|
55 | |
21 | In this tutorial I'll show you how to write a simple chat server based on |
56 | # In this tutorial I'll show you how to write a simple chat server based on |
22 | L<AnyEvent::MP>. This example is used because it nicely shows how to organise a |
57 | # L<AnyEvent::MP>. This example is used because it nicely shows how to organise a |
23 | simple application, but keep in mind that every node trusts any other, so this |
58 | # simple application, but keep in mind that every node trusts any other, so this |
24 | chat cannot be used to implement a real public chat server and client system, |
59 | # chat cannot be used to implement a real public chat server and client system, |
25 | but it can be used to implement a distributed chat server for example. |
60 | # but it can be used to implement a distributed chat server for example. |
|
|
61 | |
|
|
62 | =head1 Passing Your First Message |
|
|
63 | |
|
|
64 | As start lets have a look at the messaging API. The next example is just a |
|
|
65 | demo to show the basic elements of message passing with L<AnyEvent::MP>. |
|
|
66 | It shout just print: "Ending with: 123". So here the code: |
|
|
67 | |
|
|
68 | use AnyEvent; |
|
|
69 | use AnyEvent::MP; |
|
|
70 | |
|
|
71 | my $end_cv = AnyEvent->condvar; |
|
|
72 | |
|
|
73 | my $port = port; |
|
|
74 | |
|
|
75 | rcv $port, test => sub { |
|
|
76 | my ($data) = @_; |
|
|
77 | $end_cv->send ($data); |
|
|
78 | }; |
|
|
79 | |
|
|
80 | snd $port, test => 123; |
|
|
81 | |
|
|
82 | print "Ending with: " . $end_cv->recv . "\n"; |
|
|
83 | |
|
|
84 | It already contains most functions of the essential L<AnyEvent::MP> API. |
|
|
85 | |
|
|
86 | First there is the C<port> function which will create a I<port> and will return |
|
|
87 | it's I<port id>. |
|
|
88 | |
|
|
89 | That I<port id> can be used to send and receive messages. That I<port id> is a |
|
|
90 | simple string and can be safely passed to other I<nodes> in the network to |
|
|
91 | refer to that specific port (usually used for RPC, where you need to |
|
|
92 | tell the other end which I<port> to send the reply to). |
|
|
93 | |
|
|
94 | Next function is C<rcv>: |
|
|
95 | |
|
|
96 | rcv $port, test => sub { ... }; |
|
|
97 | |
|
|
98 | It sets up a receiver callback on a specific I<port> which needs to be |
|
|
99 | specified as the first argument. The next argument, in this example C<test>, is |
|
|
100 | a I<tag> match. This means that whenever a message, with the first element |
|
|
101 | being the string C<tag>, is received the callback is called with the remaining |
|
|
102 | parts of that message. |
|
|
103 | |
|
|
104 | Messages can be send with the C<snd> function, which looks like this in the |
|
|
105 | example above: |
|
|
106 | |
|
|
107 | snd $port, test => 123; |
|
|
108 | |
|
|
109 | This will send the message C<['test', 123]> to the I<port> with the I<port id> |
|
|
110 | in C<$port>. The receiver got a I<tag> match on C<test> and will call the |
|
|
111 | callback with the first argument being the number C<123>. |
|
|
112 | |
|
|
113 | That callback then just passes that number on to the I<condition variable> |
|
|
114 | C<$end_cv> which will then pass the value to the print. But I<condition |
|
|
115 | variables> are out of the scope of this tutorial. So please consult the |
|
|
116 | L<AnyEvent::Intro> about them. |
|
|
117 | |
|
|
118 | But passing messages inside one process is boring, but before we can continue |
|
|
119 | and take the next step to interprocess message passing we first have to make |
|
|
120 | sure some things have been setup. |
26 | |
121 | |
27 | =head1 System Requirements and System Setup |
122 | =head1 System Requirements and System Setup |
28 | |
123 | |
29 | Before we can start we have to make sure some things work on your |
124 | Before we can start with real IPC we have to make sure some things work on your |
30 | system. |
125 | system. |
31 | |
126 | |
32 | You should of course first make sure that L<AnyEvent> and L<AnyEvent::MP> |
|
|
33 | are installed. But how to do that is out of scope of this tutorial. |
|
|
34 | |
|
|
35 | Then we have to setup a I<shared secret>: for two L<AnyEvent::MP> nodes to |
127 | First we have to setup a I<shared secret>: for two L<AnyEvent::MP> I<nodes> to |
36 | be able to communicate with each other and authenticate each other it is |
128 | be able to communicate with each other and authenticate each other it is |
37 | necessary to setup the same I<shared secret> for both of them (or use TLS |
129 | necessary to setup the same I<shared secret> for both of them (or use TLS |
38 | certificates). |
130 | certificates). |
39 | |
131 | |
40 | The easiest way is to set this up is to use the F<aemp> utility: |
132 | The easiest way is to set this up is to use the F<aemp> utility: |
41 | |
133 | |
42 | aemp gensecret |
134 | aemp gensecret |
43 | |
135 | |
44 | This creates a F<$HOME/.perl-anyevent-mp> config file and generates a |
136 | This creates a F<$HOME/.perl-anyevent-mp> config file and generates a random |
45 | random shared secret. You can copy this file to any other system and then |
137 | shared secret. You can copy this file to any other system and then communicate |
46 | communicate with it. You can also select your own shared secret (F<aemp |
138 | over the network (via TCP) with it. You can also select your own shared secret |
47 | setsecret>) and for increased security requirements you can even create |
139 | (F<aemp setsecret>) and for increased security requirements you can even create |
48 | a TLS certificate (F<aemp gencert>), causing connections to not just be |
140 | a TLS certificate (F<aemp gencert>), causing connections to not just be |
49 | authenticated, but also to be encrypted. |
141 | authenticated, but also to be encrypted. |
50 | |
142 | |
51 | Connections will only be successful when the nodes that want to connect to |
143 | Connections will only be successful when the I<nodes> that want to connect to |
52 | each other have the same I<shared secret> (or successfully verify the TLS |
144 | each other have the same I<shared secret> (or successfully verify the TLS |
53 | certificate of the other side). |
145 | certificate of the other side). |
54 | |
146 | |
55 | B<If something does not work as expected, and for example tcpdump shows |
147 | B<If something does not work as expected, and for example tcpdump shows |
56 | that the connections are closed almost immediately, you should make sure |
148 | that the connections are closed almost immediately, you should make sure |
57 | that F<~/.perl-anyevent-mp> is the same on all hosts/user accounts that |
149 | that F<~/.perl-anyevent-mp> is the same on all hosts/user accounts that |
58 | you try to connect with each other!> |
150 | you try to connect with each other!> |
59 | |
|
|
60 | =head1 Passing Your First Message |
|
|
61 | |
|
|
62 | As start lets have a look at the messaging API. The next example is just a |
|
|
63 | demo to show the basic elements of message passing with L<AnyEvent::MP>. |
|
|
64 | It shout just print: "Ending with: 123". So here the code: |
|
|
65 | |
|
|
66 | use AnyEvent; |
|
|
67 | use AnyEvent::MP; |
|
|
68 | |
|
|
69 | initialise_node; |
|
|
70 | |
|
|
71 | my $end_cv = AnyEvent->condvar; |
|
|
72 | |
|
|
73 | my $port = port; |
|
|
74 | |
|
|
75 | rcv $port, test => sub { |
|
|
76 | my ($data) = @_; |
|
|
77 | $end_cv->send ($data); |
|
|
78 | }; |
|
|
79 | |
|
|
80 | snd $port, test => 123; |
|
|
81 | |
|
|
82 | print "Ending with: " . $end_cv->recv . "\n"; |
|
|
83 | |
|
|
84 | It already contains lots of the API that we are going to use. First there |
|
|
85 | is C<initialise_node>, which will initialise the L<AnyEvent::MP> node for that |
|
|
86 | process. |
|
|
87 | |
|
|
88 | Next there is the C<port> function which will create a I<port id> for us, where |
|
|
89 | we can wait for messages and send messages to. The port id is a simple string, |
|
|
90 | which acts as identifier for a port, with the form C<noderef#portname>. The |
|
|
91 | I<noderef> is basically a string that refers to the node and also contains |
|
|
92 | enough information to contact the node from the outside. The I<portname> is |
|
|
93 | usually just a random string. |
|
|
94 | |
|
|
95 | Next the call to C<rcv> sets up a receiver callback. The first element in |
|
|
96 | every message is usually denoting it's I<type> or I<tag>. Which should be a |
|
|
97 | simple string, the second argument to C<rcv> lets us match the tag of a |
|
|
98 | message. If it matches, the callback will be called, with the remaining |
|
|
99 | elements of the message as arguments. |
|
|
100 | |
|
|
101 | C<snd> sends a message. The message consists of two elements: The string |
|
|
102 | C<'test'> and the number C<123>. |
|
|
103 | |
|
|
104 | The message arrives in the callback we setup with C<rcv> and will trigger the |
|
|
105 | condition variable C<$end_cv> to deliver the result value and end the program. |
|
|
106 | |
151 | |
107 | =head1 The Chat Client |
152 | =head1 The Chat Client |
108 | |
153 | |
109 | OK, lets start by implementing the "frontend" of the client. We will |
154 | OK, lets start by implementing the "frontend" of the client. We will |
110 | develop the client first and postpone the server for later, as the most |
155 | develop the client first and postpone the server for later, as the most |