This example contains a trivial application server and its client.

Both the client and server use POE::Filter::Reference to send and receive Perl data structures. Filter::Reference transparently serializes and reconstitutes Perl data, so neither the client nor the server needs to be concerned with it. Senders call put() with references, and receivers receive the data intact.

In this example, the server takes lists of things and separates them into categories: odd integers, even integers, and non-integers. It also sums the integers and returns a status indicator.

The client sends such a list and dumps the response it gets.

  Client received:
    bad  = 2.718 3.1416 four score and seven years ago
    even = 0 6 8
    odd  = 3 5 7 9
    stat = OK
    sum  = 38

Both the client and server are created in the same process. It's simple to split them, however. First make a second copy of the file, then delete the server from one and the client from the other.

By default POE::Filter::Reference uses Storable or FreezeThaw to send Perl data over a socket. It can also use other means. Any module or object that provides freeze() and thaw() functions will work.

The sample application client and server can easily be made to use [YAML]. First load the YAML module.

use YAML;

Then customize the filters used by ClientFilter Filter in the server and client constructors.

ClientFilter => ["POE::Filter::Reference", "YAML"],

and

Filter => ["POE::Filter::Reference", "YAML"],

That's it. The server and client will now use YAML. While not as fast as Storable, YAML allows POE programs to share data with those written in other languages. See [YAML's home page] for more information.

#!/usr/bin/perl
use warnings;
use strict;
use POE;
use POE::Filter::Reference;
use POE::Component::Server::TCP;
use POE::Component::Client::TCP;
### Create a server that receives and sends Perl data structures.  It
### will be referred to by the name "sum-server" when necessary.  It
### listens on localhost port 12345.  It uses POE::Filter::Reference
### to parse input and format output.
POE::Component::Server::TCP->new(
  Alias        => "sum_server",
  Address      => "localhost",
  Port         => 12345,
  ClientFilter => "POE::Filter::Reference",

  # Handle client requests here.
  ClientInput => sub {
    my ($heap, $list) = @_[HEAP, ARG0];
    my $sum = 0;
    my (@odd, @even, @bad, $status);

    # Process the request into buckets for odd, even, and
    # non-integers.  Sum the integers in the request.
    if (ref($list) eq 'ARRAY') {
      foreach (@$list) {
        if (/^\d+$/) {
          if   ($_ % 2) { push @odd,  $_; }
          else          { push @even, $_; }
          $sum += $_;
        }
        else {
          push @bad, $_;
        }
      }
      $status = "OK";
    }
    else {
      $status = "Error: Bad request type: " . ref($list);
    }

    # Build the response hash, then send it to the client.
    my %response = (
      sum  => $sum,
      odd  => \@odd,
      even => \@even,
      bad  => \@bad,
      stat => $status,
    );
    $heap->{client}->put(\%response);
  },

  # Since this is a one-shot test program, shut down the server
  # after the first client has disconnected.  This posts "shutdown"
  # to the server itself.  Using yield() here would just shut down
  # the client connection.
  ClientDisconnected => sub {
    my $kernel = $_[KERNEL];
    $kernel->post(sum_server => "shutdown");
  },
);
### Create a client that sends and receives Perl data structures.
### Upon connecting with the server, it sends a list of things to
### process.  The server responds with a hash of a few things.  Upon
### receipt of that hash, the client displays its contents and exits.
POE::Component::Client::TCP->new(
  Alias         => "sum_client",
  RemoteAddress => "localhost",
  RemotePort    => 12345,
  Filter        => "POE::Filter::Reference",

  # Build a request and send it.
  Connected => sub {
    my $heap = $_[HEAP];
    my @request =
      qw( 0 2.718 3 3.1416 5 6 7 8 9 four score and seven years ago );
    $heap->{server}->put(\@request);
  },

  # Receive a response, display it, and shut down the client.
  ServerInput => sub {
    my ($kernel, $hash) = @_[KERNEL, ARG0];
    if (ref($hash) eq 'HASH') {
      print "Client received:\n";
      foreach (sort keys %$hash) {
        my $value = $hash->{$_};
        $value = "@$value" if ref($value) eq 'ARRAY';
        printf "\t%-4s = $value\n", $_;
      }
    }
    else {
      print "Client received an unknown response type: ", ref($hash), "\n";
    }
    $kernel->yield("shutdown");
  },
);
### Run both the client and server.  Yes, in the same program.
$poe_kernel->run();


zby: Shouldn't it use the new POE::Component::Server::PreforkTCP module?

rocco: I agree. Someone should write a version using the new PreforkTCP component. It should be posted as a separate recipe, though, not as a replacement for this one.

zby: I just would like to note that when you simply replace POE::Component::Client::TCP to POE::Component::Server::PreforkTCP in the program it works. Perhaps what is needed is a test to show that there are those multiple servers.