Components

During the evolution of this simple echo server, we've managed to reduce the server from about 130 lines to about 75. In the process, we've whittled away the main loop and a lot of the code for dealing with sockets. In its place, we've added code that manages POE::Wheel objects instead.

It turns out that managing POE::Wheel objects is only a little less tedious than writing servers longhand. Our servers still must set up SocketFactory instances to listen on sockets (see POE::Wheel::SocketFactory), they still must create ReadWrite wheels to interact with clients, and they still must handle errors. Even with wheels, these things happen pretty much the same way for every server, and they're just the coding overhead necessary before sitting down to write the fun stuff.

As with wheels, we've abstracted the repetitive work into something larger. In this case, Ann Barcomb designed a server component to manage the wheels and other things for us. Nearly all of the tedious overhead is gone.

As usual, we set up the Perl program by loading the modules we'll need.

#!/usr/bin/perl

use warnings;
use strict;

use POE qw(Component::Server::TCP);

Next we create and run the TCP server component. It will listen on port 12345, and it will handle all the boring tasks of accepting connections, managing wheels, and so forth.

POE::Component::Server::TCP is customizable through callbacks. In its simplest usage, we only need to supply the function to handle input.

POE::Component::Server::TCP->new
  ( Port => 12345,
    ClientInput => \&client_input,
  );

POE::Kernel->run();
exit;

POE::Component::Server::TCP has a default mode where it accepts new connections and creates the sessions to handle them. At creation time, POE::Component::Server::TCP starts one POE::Session to listen for new connections.

new() starts a server based on POE::Component::Server::TCP and returns a session ID for the master listening session. All error handling is done within the server, via the Error and ClientError callbacks.

The server may be shut down by posting a shutdown event to the master session, either by its ID or the name given to it by the Alias parameter.

POE::Component::Server::TCP does a lot of work in its constructor. The design goal is to push as much overhead into one-time construction so that ongoing run-time has less overhead. Because of this, the server's constructor can take quite a daunting number of parameters.

POE::Component::Server::TCP always returns a POE::Session ID for the session that will be listening for new connections.

Finally we define the input handler.

Every client connection has been given its own POE::Session instance, so each has its own heap to store things in. This simplifies our code because their heaps track things for each connection, not us. Each connection's ReadWrite wheel is already placed into $heap->{client} for us.

sub client_input {
    my ( $heap, $input ) = @_[ HEAP, ARG0 ];
    $heap->{client}->put($input);
}

And, uh, that's all. Our simple echo server is now under 20 lines, most of which deal with the aspects that make it unique.

Here is the full listing:

$ cat -n componentserver.pl 
 1  #!/usr/bin/perl
 2  use warnings;
 3  use strict;
 4  use POE qw(Component::Server::TCP);
 5  POE::Component::Server::TCP->new(
 6    Port        => 12345,
 7    ClientInput => \&client_input,
 8  );
 9  POE::Kernel->run();
10  exit;
11  
12  sub client_input {
13    my ($heap, $input) = @_[HEAP, ARG0];
14    $heap->{client}->put("ECHO: $input");
15  }

And here is an example of execution:
$ ./componentserver.pl
$ gnetcat localhost 12345
hola!
ECHO: hola!
que tal?
ECHO: que tal?
^C

Casiano Rodríguez León
Licencia de Creative Commons
Programación Distribuida y Mejora del Rendimiento
por Casiano Rodríguez León is licensed under a Creative Commons Reconocimiento 3.0 Unported License.

Permissions beyond the scope of this license may be available at http://campusvirtual.ull.es/ocw/course/view.php?id=44.
2012-06-19