1 | =head1 Message Passing for the Non-Blocked Mind |
1 | =head1 Message Passing for the Non-Blocked Mind |
2 | |
2 | |
3 | =head2 Introduction and Terminology |
3 | =head1 Introduction and Terminology |
4 | |
4 | |
5 | This is a tutorial about how to get the swing of the new L<AnyEvent::MP> |
5 | This is a tutorial about how to get the swing of the new L<AnyEvent::MP> |
6 | module. Which allows us to transparently pass messages to our own process and |
6 | module. Which allows us to transparently pass messages to our own process |
7 | to other process on other or the same host. |
7 | and to other processes on another or the same host. |
8 | |
8 | |
9 | What kind of messages? Well, basically a message here means a list of |
9 | What kind of messages? Well, basically a message here means a list of |
10 | Perl strings, numbers, hashes and arrays, mostly everything that can be |
10 | Perl strings, numbers, hashes and arrays, mostly everything that can be |
11 | expressed in a L<JSON> text (as JSON is used by default in the protocol). |
11 | expressed as a L<JSON> text (as JSON is used by default in the protocol). |
12 | |
12 | |
13 | And next you might ask: between which entities are those messages being "passed"? |
13 | And next you might ask: between which entities are those messages |
14 | Basically between C<nodes>, which are basically your applications (as in |
14 | being "passed"? Basically between C<nodes>: a nodes is basically a |
15 | processes) that use L<AnyEvent::MP> that run either on the same or different |
15 | process/program that use L<AnyEvent::MP> and can run either on the same or |
16 | hosts. |
16 | different hosts. |
17 | |
17 | |
18 | In this tutorial I'll show you how to write a simple chat server based on |
18 | In this tutorial I'll show you how to write a simple chat server based on |
19 | L<AnyEvent::MP>. |
19 | L<AnyEvent::MP>. |
20 | |
20 | |
21 | =head2 System Requirements |
21 | =head1 System Requirements |
22 | |
22 | |
23 | Before we can start we have to make sure some things work on your |
23 | Before we can start we have to make sure some things work on your |
24 | system. First of all the host C<localhost> should resolve to a local |
24 | system. |
25 | IP address. Next you should be able to do TCP over that address. |
|
|
26 | |
25 | |
27 | You should of course also make sure that L<AnyEvent> and L<AnyEvent::MP> |
26 | You should of course also make sure that L<AnyEvent> and L<AnyEvent::MP> |
28 | are installed. But how to do that is out of scope of this tutorial. |
27 | are installed. But how to do that is out of scope of this tutorial. |
29 | |
28 | |
30 | Then we have to setup a I<shared secret>. For two L<AnyEvent::MP> nodes |
29 | Then we have to setup a I<shared secret>. For two L<AnyEvent::MP> nodes to |
31 | to be able to communicate with each other and authenticate each other |
30 | be able to communicate with each other and authenticate each other it is |
32 | it is necessary to setup a I<shared secret>. For testing you can write a |
31 | necessary to setup the same I<shared secret> for both of them. For testing |
33 | random string followed by a newline into the file C<.aemp-secret> in your |
32 | you can write a random string into the file C<.aemp-secret> in your home |
34 | home directory: |
33 | directory: |
35 | |
34 | |
|
|
35 | mcookie > ~/.aemp-secret |
|
|
36 | |
|
|
37 | # or something more predictable |
36 | echo "secret123#4blabla_please_pick_your_own" > ~/.aemp-secret |
38 | echo "secret123#4blabla_please_pick_your_own" > ~/.aemp-secret |
37 | |
39 | |
38 | Only if the nodes that want to connect to each other have the same I<shared |
40 | Connections will only be successful when the nodes that want to connect |
39 | secret> connections will be successful. |
41 | to each other have the same I<shared secret>. For more security, you can |
|
|
42 | put a self-signed SSL/TLS key/certificate pair into the file (or a normal |
|
|
43 | key/certificate and it's CA certificate). |
40 | |
44 | |
41 | B<If something does not work as expected, and for example tcpdump shows |
45 | B<If something does not work as expected, and for example tcpdump shows |
42 | that the connections are broken up early, you should make sure that ~/.aemp-secret |
46 | that the connections are closed almost immediatly, you should make sure |
43 | is the same on both hosts/user accounts you are connecting!> |
47 | that F<~/.aemp-secret> is the same on all hosts/user accounts that you try |
|
|
48 | to connect with each other!> |
44 | |
49 | |
45 | =head2 The Chat Client |
50 | =head1 The Chat Client |
46 | |
51 | |
47 | OK, lets start by implementing the "frontend" of the client. We will delay the |
52 | OK, lets start by implementing the "frontend" of the client. We will |
48 | explanation and the code of the server until we finished the client, as the |
53 | develop the client first and postpone the server for later, as the most |
49 | most complex things actually happen in the client. |
54 | complex things actually happen in the client. |
50 | |
55 | |
51 | We will use L<AnyEvent::Handle> to do non-blocking IO read on standard input: |
56 | We will use L<AnyEvent::Handle> to do non-blocking IO read on standard |
|
|
57 | input (all of this code deals with actually handling user input, no |
|
|
58 | message passing yet): |
52 | |
59 | |
53 | #!perl |
60 | #!perl |
|
|
61 | |
54 | use AnyEvent; |
62 | use AnyEvent; |
55 | use AnyEvent::Handle; |
63 | use AnyEvent::Handle; |
56 | |
64 | |
57 | sub send_message { |
65 | sub send_message { |
58 | die "This is where we will send the messages to the server" |
66 | die "This is where we will send the messages to the server" |
… | |
… | |
62 | # make an AnyEvent condition variable for the 'quit' condition |
70 | # make an AnyEvent condition variable for the 'quit' condition |
63 | # (when we want to exit the client). |
71 | # (when we want to exit the client). |
64 | my $quit_cv = AnyEvent->condvar; |
72 | my $quit_cv = AnyEvent->condvar; |
65 | |
73 | |
66 | my $stdin_hdl = AnyEvent::Handle->new ( |
74 | my $stdin_hdl = AnyEvent::Handle->new ( |
67 | fh => \*STDIN, |
75 | fh => *STDIN, |
|
|
76 | on_error => sub { $quit_cv->send }, |
68 | on_read => sub { |
77 | on_read => sub { |
69 | my ($hdl) = @_; |
78 | my ($hdl) = @_; |
70 | |
79 | |
71 | $hdl->push_read (line => sub { |
80 | $hdl->push_read (line => sub { |
72 | my ($hdl, $line) = @_; |
81 | my ($hdl, $line) = @_; |
73 | |
82 | |
74 | if ($line =~ /^\/quit/) { # /quit will end the client |
83 | if ($line =~ /^\/quit/) { # /quit will end the client |
75 | $quit_cv->send; |
84 | $quit_cv->send; |
76 | |
|
|
77 | } else { |
85 | } else { |
78 | send_message ($line); |
86 | send_message ($line); |
79 | } |
87 | } |
80 | }); |
88 | }); |
81 | } |
89 | } |
… | |
… | |
86 | This is now a very basic client. Explaining explicitly what |
94 | This is now a very basic client. Explaining explicitly what |
87 | L<AnyEvent::Handle> does or what a I<condvar> is all about is out of scope |
95 | L<AnyEvent::Handle> does or what a I<condvar> is all about is out of scope |
88 | of this document, please consult L<AnyEvent::Intro> or the manual pages |
96 | of this document, please consult L<AnyEvent::Intro> or the manual pages |
89 | for L<AnyEvent> and L<AnyEvent::Handle>. |
97 | for L<AnyEvent> and L<AnyEvent::Handle>. |
90 | |
98 | |
91 | =head2 First Step Into Messaging |
99 | =head1 First Step Into Messaging |
92 | |
100 | |
93 | Now we take a look at L<AnyEvent::MP>. We need to know what to do in |
101 | To supply the C<send_message> function we now take a look at |
94 | C<send_message>. This is an example of how it might look like: |
102 | L<AnyEvent::MP>. This is an example of how it might look like: |
95 | |
103 | |
96 | ... # the use lines from the above snippet |
104 | ... # the use lines from the above snippet |
97 | |
105 | |
98 | use AnyEvent::MP; |
106 | use AnyEvent::MP; |
99 | |
107 | |
… | |
… | |
103 | snd $server_port, message => $msg; |
111 | snd $server_port, message => $msg; |
104 | } |
112 | } |
105 | |
113 | |
106 | ... # the rest of the above script |
114 | ... # the rest of the above script |
107 | |
115 | |
108 | The C<snd> function is exported by L<AnyEvent::MP>, it stands for 'send a |
116 | The C<snd> function is exported by L<AnyEvent::MP>, it stands for 'send |
109 | message'. The first argument is the I<port> (a I<port> is something that can |
117 | a message'. The first argument is the I<port> (a I<port> is something |
110 | receive messages) of the server which will receive the message . How we get it |
118 | that can receive messages, represented by a printable string) of the |
111 | will be explained in the next step. The next arguments of C<snd> are |
119 | server which will receive the message. How we get this port will be |
112 | C<message> and C<$msg> are the first two elements of the I<message> (a |
120 | explained in the next step. |
113 | I<message> in L<AnyEvent::MP> is a be a simple list of values, which can be |
|
|
114 | sent to I<ports>). |
|
|
115 | |
121 | |
116 | Thats all fine so far, but how do we get the C<$server_port>? Well, we will |
122 | The remaining arguments of C<snd> are C<message> and C<$msg>, the first |
117 | need to get the unique I<port id> of the server's port where he wants to |
123 | two elements of the I<message> (a I<message> in L<AnyEvent::MP> is a |
118 | receive all the incoming chat messages. A I<port id> is unfortunately a very |
124 | simple list of values, which can be sent to a I<port>). |
119 | unique string, which we are unable to know in advance. But L<AnyEvent::MP> |
125 | |
120 | supports the concept of 'well known ports', which is basically a port on the |
126 | So all the function does is send the two values C<message> (a constant |
121 | server side registered under a well known name. For example, the server has a |
127 | string to tell the server what to expect) and the actual message string. |
|
|
128 | |
|
|
129 | Thats all fine and simple so far, but where do we get the |
|
|
130 | C<$server_port>? Well, we need to get the unique I<port id> of the |
|
|
131 | server's port where it wants to receive all the incoming chat messages. A |
|
|
132 | I<port id> is unfortunately a very unique string, which we are unable |
|
|
133 | to know in advance. But L<AnyEvent::MP> supports the concept of 'well |
|
|
134 | known ports', which is basically a port on the server side registered |
|
|
135 | under a well known name. For example, the server has a port for receiving |
122 | port for receiving chat messages with a unique I<port id> and registered it |
136 | chat messages with a unique I<port id> and registered it under the name |
123 | under the name C<chatter>. |
137 | C<chatter>. |
124 | |
138 | |
125 | BTW, these "well known port names" should follow similar rules as Perl |
139 | BTW, these "well known port names" should follow similar rules as Perl |
126 | identifiers, so you should prefix them with your package/module name to |
140 | identifiers, so you should prefix them with your package/module name to |
127 | make them unique, unless you use them in the main program. |
141 | make them unique, unless you use them in the main program. |
128 | |
142 | |
… | |
… | |
133 | Another new term, what is a I<node>: The messaging network that can be created with |
147 | Another new term, what is a I<node>: The messaging network that can be created with |
134 | L<AnyEvent::MP> consists of I<nodes>. A I<node> handles all the connection and |
148 | L<AnyEvent::MP> consists of I<nodes>. A I<node> handles all the connection and |
135 | low level message sending logic for its application. The application in this |
149 | low level message sending logic for its application. The application in this |
136 | case is the server. Also every client has/is a I<node>. |
150 | case is the server. Also every client has/is a I<node>. |
137 | |
151 | |
138 | =head2 Getting The Chatter Port |
152 | =head1 Getting The Chatter Port |
139 | |
153 | |
140 | Ok, lots of talk, now some code. Now we will actually get the C<$server_port> |
154 | Ok, lots of talk, now some code. Now we will actually get the C<$server_port> |
141 | from the backend: |
155 | from the backend: |
142 | |
156 | |
143 | ... |
157 | ... |
… | |
… | |
267 | # send the server a 'join' message: |
281 | # send the server a 'join' message: |
268 | snd $server_port, join => "$client_port"; |
282 | snd $server_port, join => "$client_port"; |
269 | |
283 | |
270 | Then the server knows where to send all the new messages to. |
284 | Then the server knows where to send all the new messages to. |
271 | |
285 | |
272 | =head2 The Completed Client |
286 | =head1 The Completed Client |
273 | |
287 | |
274 | This is the complete client script: |
288 | This is the complete client script: |
275 | |
289 | |
276 | #!perl |
290 | #!perl |
277 | |
291 | |
… | |
… | |
342 | } |
356 | } |
343 | ); |
357 | ); |
344 | |
358 | |
345 | $quit_cv->recv; |
359 | $quit_cv->recv; |
346 | |
360 | |
347 | =head2 The Server |
361 | =head1 The Server |
348 | |
362 | |
349 | Ok, now finally to the server. What do we need? Well, we need to setup |
363 | Ok, now finally to the server. What do we need? Well, we need to setup |
350 | the well known port C<chatter> where all clients send their messages to. |
364 | the well known port C<chatter> where all clients send their messages to. |
351 | |
365 | |
352 | Up and into code right now: |
366 | Up and into code right now: |
… | |
… | |
402 | C<%client_ports> hash. In the receiver for the message type C<message> we will |
416 | C<%client_ports> hash. In the receiver for the message type C<message> we will |
403 | just iterate through all known C<%client_ports> and relay the message to them. |
417 | just iterate through all known C<%client_ports> and relay the message to them. |
404 | |
418 | |
405 | And thats it. |
419 | And thats it. |
406 | |
420 | |
407 | =head2 The Remaining Problems |
421 | =head1 The Remaining Problems |
408 | |
422 | |
409 | The shown implementation still has some bugs. For instance: How does the |
423 | The shown implementation still has some bugs. For instance: How does the |
410 | server know that the client isn't there anymore, and can cleanup the |
424 | server know that the client isn't there anymore, and can cleanup the |
411 | C<%client_ports> hash? And also the chat messages have no originator, |
425 | C<%client_ports> hash? And also the chat messages have no originator, |
412 | so we don't know who actually sent the message (which would be quite useful |
426 | so we don't know who actually sent the message (which would be quite useful |
… | |
… | |
416 | L<AnyEvent::MP> and explained some common idioms. |
430 | L<AnyEvent::MP> and explained some common idioms. |
417 | |
431 | |
418 | How to solve the reliability and C<%client_ports> cleanup problem will |
432 | How to solve the reliability and C<%client_ports> cleanup problem will |
419 | be explained later in this tutorial (TODO). |
433 | be explained later in this tutorial (TODO). |
420 | |
434 | |
421 | =head2 Inside The Protocol |
435 | =head1 Inside The Protocol |
422 | |
436 | |
423 | Now, for the interested parties, let me explain some details about the protocol |
437 | Now, for the interested parties, let me explain some details about the protocol |
424 | that L<AnyEvent::MP> nodes use to communicate to each other. If you are not |
438 | that L<AnyEvent::MP> nodes use to communicate to each other. If you are not |
425 | interested you can skip this section. |
439 | interested you can skip this section. |
426 | |
440 | |