The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

Okay... how to write a program using POE. First we need a program to write. How about a simple chat server? Ok!

First do some preliminary setup things. Turn on strict, and import stuff we need. That will be Socket, for the socket constants and address manipulation; and some POE classes. All the POE classes get POE:: prepended to them when used along with POE.pm itself. Here are the ones we need:

POE::Wheel::SocketFactory, to create the sockets.

POE::Wheel::ReadWrite, to send and receive on the client sockets.

POE::Driver::SysRW, to read and write with sysread() and syswrite().

POE::Filter::Line, to process input and output as lines.

POE::Component::UserBase, to allow user authentication.

Here we go:

Now we need to create the listening server and wait for connections. First we define the subroutines that will handle events, and then we create the POE::Session that maps the event names to the handlers.

But first a quick note about event handler parameters. Every handler gets its parameters in some strange order. Actually, they all get them in the same order, but the order changes from time to time (usually between versions). So Rocco and Artur benchmarked a bunch of different ways to pass parameters where the order makes no difference. The least slowest way to do this-- which still is slower than plain list assignment-- was to use an array slice.

So we came up with some constants for parameter indices into @_, and exported them from POE::Session (which is automatically included when you use POE). Now you can say my ($heap, $kernel, $parameter) = @_[HEAP, KERNEL, ARG0], and it will continue to work even if new parameters are added. And if parameters are ever removed, well, it will break at compile time instead of causing sneaky runtime problems.

So anyway, some of the important parameter offsets and what they do:

  KERNEL is a reference to the POE kernel (event loop and services
  object).

  SESSION is a reference to the current POE::Session object.

  HEAP is an anonymous hashref that a session can use to hold its own
  "global" variables.

  FROM is the session that sent the event.

  ARG0..ARG9 are the first ten event parameters.  If you need more
  than that, you can either use ARG9+1..ARG9+$whatever; or you can
  pass parameters as an array reference.  Array references would be
  faster than slinging a bunch of parameters all over the place.

Now about the SocketFactory. A SocketFactory is a factory that creates... sockets. See? Anyway, the socket factory creates sockets, but it does not return them right away. Instead, it waits until the sockets are ready, and then it sends a "this socket is ready" sort of success event. The socket itself is sent as a parameter (ARG0) of the success event. And because this is non-blocking (even during connect), the program can keep working on other things while it waits.

There is more magic. For listening sockets, it sends the "this socket is ready" event whenever a connection is successfully accepted. And the socket that accompanies the event is the accepted one, not the listening one. This makes writing servers real easy, because all the work between "create this server socket" and "here's your client connection" is taken care of inside the SocketFactory object.

So here is the server stuff:

This section of the program is the actual chat management. For the sake of the tutorial, it is just a hash to keep track of connections and a subroutine to distribute messages to everyone.

Now we need to handle the accepted client connections.

A quick recap of where the accepted socket currently is. It was accepted by the SocketFactory, and passed to &server_accept with the "we got a connection" event. Then &server_accept handed it off to a new POE::Session as a parameter to its _start event. The _start event handler (&chat_start) will then get the handle (and the peer address and port) as ARG0, ARG1 and ARG2.

So anyway, read input from the client connection, process it somehow, and generate responses. Here we are at chat_start...

Initialize and start the authentication server. The states below are designed to add authentication to the chat server.

And finally, start the server and run the event queue.