… | |
… | |
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 as 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 |
13 | And next you might ask: between which entities are those messages |
14 | being "passed"? Basically between C<nodes>: a nodes is basically a |
14 | being "passed"? Effectively between I<nodes>: a nodes is basically a |
15 | process/program that use L<AnyEvent::MP> and can run either on the same or |
15 | process/program that use L<AnyEvent::MP> and can run either on the same or |
16 | different hosts. |
16 | different hosts. |
|
|
17 | |
|
|
18 | To make this more managable, every node can contain any number of |
|
|
19 | I<ports>: Ports are ultimately the receivers of your messages. |
17 | |
20 | |
18 | In this tutorial I'll show you how to write a simple chat server based on |
21 | In this tutorial I'll show you how to write a simple chat server based on |
19 | L<AnyEvent::MP>. |
22 | L<AnyEvent::MP>. |
20 | |
23 | |
21 | =head1 System Requirements |
24 | =head1 System Requirements |
… | |
… | |
94 | This is now a very basic client. Explaining explicitly what |
97 | This is now a very basic client. Explaining explicitly what |
95 | L<AnyEvent::Handle> does or what a I<condvar> is all about is out of scope |
98 | L<AnyEvent::Handle> does or what a I<condvar> is all about is out of scope |
96 | of this document, please consult L<AnyEvent::Intro> or the manual pages |
99 | of this document, please consult L<AnyEvent::Intro> or the manual pages |
97 | for L<AnyEvent> and L<AnyEvent::Handle>. |
100 | for L<AnyEvent> and L<AnyEvent::Handle>. |
98 | |
101 | |
99 | =head1 First Step Into Messaging |
102 | =head1 First Steps Into Messaging |
100 | |
103 | |
101 | To supply the C<send_message> function we now take a look at |
104 | To supply the C<send_message> function we now take a look at |
102 | L<AnyEvent::MP>. This is an example of how it might look like: |
105 | L<AnyEvent::MP>. This is an example of how it might look like: |
103 | |
106 | |
104 | ... # the use lines from the above snippet |
107 | ... # the use lines from the above snippet |
… | |
… | |
127 | string to tell the server what to expect) and the actual message string. |
130 | string to tell the server what to expect) and the actual message string. |
128 | |
131 | |
129 | Thats all fine and simple so far, but where do we get the |
132 | 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 |
133 | 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 |
134 | 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 |
135 | I<port id> is unfortunately a very unique string, which we are unable to |
133 | to know in advance. But L<AnyEvent::MP> supports the concept of 'well |
136 | know in advance. But L<AnyEvent::MP> supports the concept of 'registered |
134 | known ports', which is basically a port on the server side registered |
137 | ports', which is basically a port on the server side registered under |
135 | under a well known name. For example, the server has a port for receiving |
138 | a well known name. |
136 | chat messages with a unique I<port id> and registered it under the name |
|
|
137 | C<chatter>. |
|
|
138 | |
139 | |
|
|
140 | For example, the server has a port for receiving chat messages with a |
|
|
141 | unique I<port id> and registers it under the name C<chatter>. |
|
|
142 | |
139 | BTW, these "well known port names" should follow similar rules as Perl |
143 | BTW, these "registered port names" should follow similar rules as Perl |
140 | identifiers, so you should prefix them with your package/module name to |
144 | identifiers, so you should prefix them with your package/module name to |
141 | make them unique, unless you use them in the main program. |
145 | make them unique, unless you use them in the main program. |
142 | |
146 | |
143 | As I<messages> can only be sent to a I<port id> and not just to a name we have |
147 | As I<messages> can only be sent to a I<port id> and not just to some name |
144 | to ask the server I<node> what I<port id> has the well known port with the |
148 | we have to ask the server node for the I<port id> of the port registered |
145 | name C<chatter>. |
149 | as C<chatter>. |
146 | |
150 | |
147 | Another new term, what is a I<node>: The messaging network that can be created with |
|
|
148 | L<AnyEvent::MP> consists of I<nodes>. A I<node> handles all the connection and |
|
|
149 | low level message sending logic for its application. The application in this |
|
|
150 | case is the server. Also every client has/is a I<node>. |
|
|
151 | |
|
|
152 | =head1 Getting The Chatter Port |
151 | =head1 Finding The Chatter Port |
153 | |
152 | |
154 | Ok, lots of talk, now some code. Now we will actually get the C<$server_port> |
153 | Ok, lots of talk, now some code. Now we will actually get the |
155 | from the backend: |
154 | C<$server_port> from the backend: |
156 | |
155 | |
157 | ... |
156 | ... |
158 | |
157 | |
159 | use AnyEvent::MP; |
158 | use AnyEvent::MP; |
160 | |
159 | |
|
|
160 | my $server_node = "127.0.0.1:1299"; |
|
|
161 | |
|
|
162 | my $client_port = port; |
|
|
163 | |
|
|
164 | snd $server_node, lookup => "chatter", $client_port, "resolved"; |
|
|
165 | |
161 | my $resolved_cv = AnyEvent->condvar; |
166 | my $resolved_cv = AnyEvent->condvar; |
162 | |
|
|
163 | my $client_port = create_port; |
|
|
164 | |
|
|
165 | my $server_node = "localhost:1299#"; |
|
|
166 | |
|
|
167 | snd $server_node, wkp => "chatter", "$client_port", "resolved"; |
|
|
168 | |
|
|
169 | my $server_port; |
167 | my $server_port; |
170 | |
168 | |
171 | # setup a receiver callback for the 'resolved' message: |
169 | # setup a receiver callback for the 'resolved' message: |
172 | $client_port->rcv (resolved => sub { |
170 | rcv $client_port, resolved => sub { |
173 | my ($client_port, $type, $chatter_port_id) = @_; |
171 | my ($tag, $chatter_port_id) = @_; |
174 | |
172 | |
175 | print "Resolved the server port 'chatter' to $chatter_port_id\n"; |
173 | print "Resolved the server port 'chatter' to $chatter_port_id\n"; |
176 | $server_port = $chatter_port_id; |
174 | $server_port = $chatter_port_id; |
177 | |
175 | |
178 | $resolved_cv->send; |
176 | $resolved_cv->send; |
179 | 1 |
177 | 1 |
180 | }); |
178 | }; |
181 | |
179 | |
182 | # lets block the client until we resolved the server port. |
180 | # lets block the client until we have resolved the server port. |
183 | $resolved_cv->recv; |
181 | $resolved_cv->recv; |
184 | |
182 | |
185 | # now setup another receiver callback for the chat messages: |
183 | # now setup another receiver callback for the chat messages: |
186 | $client_port->rcv (message => sub { |
184 | rcv $client_port, message => sub { |
187 | my ($client_port, $type, $msg) = @_; |
185 | my ($tag, $msg) = @_; |
188 | |
186 | |
189 | print "chat> $msg\n"; |
187 | print "chat> $msg\n"; |
190 | 0 |
188 | 0 |
191 | }); |
189 | }; |
192 | |
190 | |
193 | # send the server a 'join' message: |
191 | # send a 'join' message to the server: |
194 | snd $server_port, join => "$client_port"; |
192 | snd $server_port, join => "$client_port"; |
195 | |
193 | |
196 | sub send_message { ... |
194 | sub send_message { ... |
197 | |
195 | |
198 | Now that was a lot of new stuff. In order to ask the server and receive an |
196 | Now that was a lot of new stuff: |
199 | answer we need to have a I<port> where we can receive the answer. |
|
|
200 | This is what C<create_port> will do for us, it just creates a new local |
|
|
201 | port and returns us an object (that will btw. stringify to the I<port id>), |
|
|
202 | that we can use to receive messages. |
|
|
203 | |
197 | |
204 | Next thing is the C<$server_node>. In order to refer to another node we need |
198 | First we define the C<$server_node>: In order to refer to another node |
205 | some kind of string to reference it. The I<noderef> is basically a comma |
199 | we need some kind of string to reference it - the node reference. The |
206 | separated list of C<host:port> pairs. We assume in this tutorial that the |
200 | I<noderef> is basically a comma separated list of C<address:port> |
207 | server runs on your localhost at port 1299, this gives us the noderef |
201 | pairs. We assume in this tutorial that the server runs on C<127.0.0.1> |
208 | C<localhost:1299>. |
202 | (localhost) on port 1299, which results in the noderef C<127.0.0.1:1299>. |
209 | |
203 | |
210 | Now you might ask what the C<#> at the end in C<$server_node> the above |
204 | Next, in order to receive a reply from the other node or the server we |
211 | example is about. Well, what I didn't tell you yet is that each I<node> has a |
205 | need to have a I<port> that messages can be sent to. This is what the |
212 | default I<port> to receive messages. The default port is the empty string |
206 | C<port> function will do for us, it just creates a new local port and |
213 | C<"">. The I<default port> of a I<node> also provides some special services for |
207 | returns it's I<port ID> that can then be used to receive messages. |
214 | us, for example resolving a well known port to a I<port id>. |
|
|
215 | |
208 | |
216 | Now to this line: |
209 | When you look carefully, you will see that the first C<snd> uses the |
|
|
210 | C<$server_node> (a noderef) as destination port. Well, what I didn't |
|
|
211 | tell you yet is that each I<node> has a default I<port> to receive |
|
|
212 | messages. The ID of this port is the same as the noderef. |
217 | |
213 | |
|
|
214 | This I<default port> provides some special services for us, for example |
|
|
215 | resolving a registered name to a I<port id> (a-ha! finally!). |
|
|
216 | |
|
|
217 | This is exactly what this line does: |
|
|
218 | |
218 | snd $server_node, wkp => "chatter", "$client_port", "resolved"; |
219 | snd $server_node, lookup => "chatter", $client_port, "resolved"; |
219 | |
220 | |
220 | We send a message with first element being C<wkp> (standing for 'well known |
221 | This sends a message with first element being C<lookup>, followed by the |
221 | port'). Then the well known port name that we want to resolve to a I<port id>: |
222 | (hopefully) registered port name that we want to resolve to a I<port |
222 | C<chatter>. And in order for the server node to be able to send us back the |
223 | id>: C<chatter>. And in order for the server node to be able to send us |
223 | resolved I<port id> we have to tell it where to send the result message: The |
224 | back the resolved I<port ID> we have to tell it where to send it: The |
224 | result message will have as it's first argument the string C<resolved> and |
|
|
225 | will be sent to C<$client_port> (the I<port id> of our own just created |
225 | result message will be sent to C<$client_port> (the I<port id> of the |
226 | port). |
226 | port we just created), and will have the string C<resolved> as the first |
|
|
227 | element. |
227 | |
228 | |
|
|
229 | When the node receives this message, it will look up the name, gobble up |
|
|
230 | all the extra arguments we passed, append the resolved name, and send the |
|
|
231 | resulting list as a message. |
|
|
232 | |
228 | Next comes the receiver for this C<wkp> request. |
233 | Next we register a receiver for this C<lookup>-request. |
229 | |
234 | |
230 | $client_port->rcv (resolved => sub { |
235 | rcv $client_port, resolved => sub { |
231 | my ($client_port, $type, $chatter_port_id) = @_; |
236 | my ($tag, $chatter_port_id) = @_; |
232 | ... |
237 | ... |
233 | 1 |
238 | 1 |
234 | }); |
239 | }; |
235 | |
240 | |
236 | This sets up a receiver on our own port for the result message with the first |
241 | This sets up a receiver on our own port for messages with the first |
237 | argument being the string C<resolved>. Receivers can match the contents of |
242 | element being the string C<resolved>. Receivers can match the contents of |
238 | the messages before actually 'sending' it to the given callback. |
243 | the messages before actually executing the specified callback. |
239 | |
244 | |
240 | B<Please note> that the given callback has to return either a true or a false |
245 | B<Please note> that the every C<rcv> callback has to return either a true |
241 | value for indicating whether it is B<done> (true value) or still wants to |
246 | or a false value, indicating whether it is B<successful>/B<done> (true) or |
242 | B<continue> (false value) receiving messages. |
247 | still wants to B<continue> (false) receiving messages. |
243 | |
248 | |
244 | In this case we tell the C<$client_port> to look into the received messages and |
249 | In this case we tell the C<$client_port> to look into all the messages |
245 | look for the string C<resolved> in the first element of the message. If it is |
250 | it receives and look for the string C<resolved> in the first element of |
246 | found, the given callback will be called with the C<$client_port> as first |
251 | the message. If it is found, the given callback will be called with the |
247 | argument, and the message as the remaining arguments. |
252 | message elements as arguments. |
248 | |
253 | |
249 | We name the first element of the message C<$type> in this case. It's a common |
254 | Using a string as the first element of the message is called I<tagging> |
250 | idiom to code the 'type' of a message into it's first element, this allows for |
255 | the message. It's common practise to code the 'type' of a message into |
251 | simple matching. |
256 | it's first element, as this allows for simple matching. |
252 | |
257 | |
253 | The result message will contain the I<port id> of the well known port C<chatter> |
258 | The result message will contain the I<port ID> of the well known port |
254 | as next element, and will be put in C<$chatter_port_id>. |
259 | C<chatter> as second element, which will be stored in C<$chatter_port_id>. |
255 | |
260 | |
256 | Next we just assign C<$server_port> and return a 1 (a true value) |
261 | This port ID will then be stored in C<$server_port>, followed by calling |
257 | from the callback. It indicates that we are done and don't want to receive |
262 | C<send> on $resolved_cv> so the program will continue. |
258 | further C<resolved> messages with this callback. |
|
|
259 | |
263 | |
260 | Now we continue to the rest of the client by calling C<send> on |
264 | The callback then returns a C<1> (a true value), to indicate that it has |
261 | C<$resolved_cv>. |
265 | done it's job and doesn't want to receive further C<resolved> messages. |
262 | |
266 | |
263 | First new step after this is setting up the chat message receiver callback. |
267 | After this the chat message receiver callback is registered with the port: |
264 | |
268 | |
265 | $client_port->rcv (message => sub { |
269 | rcv $client_port, message => sub { |
266 | my ($client_port, $type, $msg) = @_; |
270 | my ($tag, $msg) = @_; |
267 | |
271 | |
268 | print "chat> $msg\n"; |
272 | print "chat> $msg\n"; |
|
|
273 | |
269 | 0 |
274 | 0 |
270 | }); |
275 | }; |
271 | |
276 | |
272 | We assume that all messages that are broadcast to all clients by the server |
277 | We assume that all messages that are broadcast to the clients by the |
273 | will contain the string C<message> as first element, and the actual message as |
278 | server contain the string tag C<message> as first element, and the actual |
274 | second element. The callback returns a false value this time, to indicate that |
279 | message as second element. The callback returns a false value this time, |
275 | it wants to continue receiving messages. |
280 | to indicate that it is not yet done and wants to receive further messages. |
276 | |
281 | |
277 | Last but not least we actually tell the server to send us |
282 | The last thing to do is to tell the server to send us new chat messages |
278 | the new chat messages from other clients. We do so by sending the |
283 | from other clients. We do so by sending the message C<join> followed by |
279 | message type C<join> followed by our own I<port id>. |
284 | our own I<port ID>. |
280 | |
285 | |
281 | # send the server a 'join' message: |
286 | # send the server a 'join' message: |
282 | snd $server_port, join => "$client_port"; |
287 | snd $server_port, join => $client_port; |
283 | |
288 | |
284 | Then the server knows where to send all the new messages to. |
289 | This way the server knows where to send all the new messages to. |
285 | |
290 | |
286 | =head1 The Completed Client |
291 | =head1 The Completed Client |
287 | |
292 | |
288 | This is the complete client script: |
293 | This is the complete client script: |
289 | |
294 | |