A Beginner's Introduction to POE

17 views
Skip to first unread message

netkiller

unread,
Sep 30, 2008, 8:31:32 AM9/30/08
to netkiller
A Beginner's Introduction to POE

What Is POE, And Why Should I Use It?
回顾一下我们每天写的程序,可以发现这些程序大部分都有着基本的结构:启动,执行一系列的动作,然后退出。对于那些在用户和他们的数据之间不需
要很多交互的应用中,这样的程序都能工作得很好。然后,如果是如果是碰到了复杂的任务,我想你就需要一个更加好的程序框架理解任务的复杂性,完成这个任
务。
所以POE(Perl 对象环境)应运而生了。POE是一个构建Perl程序的框架,以更自然地完成那些需要对外部数据作出反应的任务,比如网
络通讯或者用户接口。用POE写的程序完全是非线性的;你只需要建立一些小的子程序,并且定义好它们之间如何相互调用,那么POE就会根据程序处理的输
入输出,自动在它们之间切换。说到现在,你可能已经头晕了。如果你习惯于看代码去理解,那么看了下面一个小例子后,你就会恍然大悟了。

POE Design
说POE是一个小的操作系统一点都不夸张,它有它自己的内核,进程,进程间通讯(IPC),驱动等等这些。实际上,它是一个简单的系统,构建了
一系列的状态信息。下面对组成POE各个部分的一个简单的描述:
States
POE的基本程序块就是状态,它是一些代码,当事件发生时触发执行。例如,输入的数据到达,一个会话过期,一个会话传递了一条信息给另一个会
话。POE里的每件事情都是基于接收和处理各种各样的事件。
The Kernel
POE的内核跟操作系统的内核很象:它守护着后台的所有你的进程和数据,安排你的代码的触发。你能够用POE的内核来为你的进程设置警报,控制
你的状态队列和其他一些低级服务,当然,大部分时间你不用直接来跟内核交互。
Sessions
POE里的会话相当于一个真的操作系统里的进程。一个会话就是一个POE程序,当它在运行时不停地在状态之间切换。它能够创建子会话,传递信息
给其他的会话等等。每个会话都能通过调用 heap 存储会话特有的数据,这些数据在会话中的各个状态都是可以存取的。
POE有一个简单的协作的多任务处理模块;每个会话在同一个OS进程中执行,不需要任何线程和进程。对于这样的会话,你应该特别注意在POE程
序中使用模块化的系统调用。
这些就是POE的最基本的部分,但是在我们开始实际的代码前还需要稍微ie解释一些高级的POE的部分。
Drivers
驱动位于POE最低级的I/O层。目前,只有一个驱动包括在POE的安装中-- POE::Driver::SysRW,它读写文件句柄中的数
据。不管怎样,我们都不需要直接使用驱动。
Filters
过滤器,在某个方面来说,是非常有用的。过滤器是一个简单的接口,转换数据块从一个格式成另一个格式。例
如,POE::Filter::HTTPD 能够在 HTTP 1.0 requests 之间 HTTP::Request 对象互相转
换。POE::Filter::Line 能够见数据流转换成序列化数据行。
Wheels
Wheels 包含了每天任务的可重复利用的高级逻辑部分。它们是POE封装有用代码的方法。在POE中你需要处理到的Wheels包括事件驱
动的输入输出数据和网络连接的创建。Wheels经常使用过滤器和驱动来处理和传递数据。这个描述太过于含糊了,下面的代码将会给出一个例子来说明这个
概念。
Components
组件是一个会话,被设计来受控于其它会话。你的会话能够从它们那里来处理命令和接受事件,这点很象真实操作系统中的通过IPC通讯。一个组件的
例子就是 POE::Component::IRC,一个创建基于POE的IPC客户端接口,或者是
POE::Component::Client::HTTP,一个事件驱动的Perl实现的HTTP用户代理。组件同样是POE非常有用的一个部分。

A Simple Example
在这个例子中,我们会建立一个守护进程服务端,它接受TCP连接,同时打印由它的客户端提交的算术问题的答案。当有人连接它的端口31008,
它将打印 “Hello,client”。客户端能够提交一个算术表达式,以一个新行结束提交,服务端将送回表达式的答案。够简单吧。
写这样一个 POE程序跟写一个Unix下的守护进程的传统方法没有什么很大的不同。我们将有一个服务器会话在端口31008监听TCP连接。
每当连接到来时,它将创建一个子会话来处理连接。每个子会话都能跟用户交互,然后在连接断开后安静地死掉。所有这些用Perl简单地实现只需要模块化的
72行代码就行了。
这个程序简单地以下面的代码开始:
1 #!/usr/bin/perl -w
2 use strict;
3 use Socket qw(inet_ntoa);
4 use POE qw( Wheel::SocketFactory Wheel::ReadWrite
5 Filter::Line Driver::SysRW );
6 use constant PORT => 31008;
这一段我们导入了脚本所需要的模块以及函数,并定义了监听端口的常量值。”use POE;”后面的 qw()语句一次导入了很多POE::模块。
现在开始本文最酷的部分:
7 new POE::Session (
8 _start => \&server_start,
9 _stop => \&server_stop,
10 );
11 $poe_kernel->run();
12 exit;
这样就已经是一个完整的程序了!我建立了服务端会话,告诉POE内核开始执行事件,并且在事件结束后退出。(当没有任何会话需要管理的时
候,POE内核就认为事情做完了,但是既然我们已经打算把服务端会话放在一个无穷的循环中,它将不会以这种发式退出)POE在你写"use
POE;"h后,自动输出变量$poe_kernel 到你的名字空间中。
new POE::Session 方法调用需要解释一下。当你创建一个会话时,你给内核一个这个会话将接受事件的列表。在上面的代码中,意味
着这个新的会话将通过调用&server_start,&server_stop处理_start和_stop事件。任何一个受到的但没有列举到的事
件,将会被会话忽略。_Start和_Stop事件对于一个POE会话来说是一个特殊事件。_start事件是当一个会话创建时第一件要做的事情,当会
话将要被摧毁时内核就通知这个会话进入_stop状态。
现在,我们已经写了完整的程序,我们还要写当会话运行时要执行的状态代码。接下来,让我们以&server_start开始。当主要的服务端会
话被创建,开始执行程序时,它将会被调用。

13 sub server_start {
14 $_[HEAP]->{listener} = new POE::Wheel::SocketFactory
15 ( BindPort => PORT,
16 Reuse => 'yes',
17 SuccessState => \&accept_new_client,
18 FailureState => \&accept_failed
19 );
20 print "SERVER: Started listening on port ", PORT, ".\n";
21 }

这是一个有关POE状态的很好的例子。首先:看到变量$_[HEAP]了吗?POE有一个特殊的方法来传递参数。@_数组是由许多外部的参数打
包在一起的。外部参数包括目前内核,会话和状态名的一个引用,堆栈的引用,以及其它一些东西。我们可以用POE导出的特殊常量来作为这个数组索引,比
如 HEAP, SESSION, KERNEL, STATE和ARG0~ARG9(用户提供的参数)。跟POE的大部分设计一样,这样子的安排能够
最大化后面的兼容性而又不牺牲速度。上面的例子存储了一个 SocketFactory wheel 在键值 listen 下。
POE::Wheel::SocketFactory wheel是POE里最酷的东西之一。你能够用它创建任何一个流socket(UDP
socket还不行),而不需要担心细节。上面的代码将创建一个SocketFactory来监听特殊的TCP端口的新连接。当连接建立时,它将调用
&accept_new_client来传递一个新的客户端,如果在这其中发生错误了,它将调用&accept_failed,而不是让我们自己来处理
错误。这差不多就是用POE在网络中要做的。
我们存储一个wheel在堆栈中,以防止在状态的最后被Perl的垃圾收集机制意外地消灭掉,——这个方法在会话的每个状态中到处存在。现在我
们来看&server_stop 状态的代码:

22 sub server_stop {
23 print "SERVER: Stopped.\n";
24 }


这段不需要多解释了。接下来的是我们创建一个新的会话来处理每个新到的连接。

25 sub accept_new_client {
26 my ($socket, $peeraddr, $peerport) = @_[ARG0 .. ARG2];
27 $peeraddr = inet_ntoa($peeraddr);
28 new POE::Session (
29 _start => \&child_start,
30 _stop => \&child_stop,
31 main => [ 'child_input', 'child_done',
'child_error' ],
32 [ $socket, $peeraddr, $peerport ]
33 );
34 print "SERVER: Got connection from $peeraddr:$peerport.\n";
35 }
当客户端成功地建立了跟服务端的连接后,我们的 POE::Wheel::SocketFactory将调用这个子例程。
我们将socket地址转换成人可读的IP地址,并且建立一个新的会话来跟客户端通讯。这跟前面的
POE::Session 构造器很象,但是有几点在接下来要说明。
@_[ARG0 .. ARG2]是($_[ARG0], $_[ARG1], $_[ARG2])的快捷方式。你会在POE程序中
看到很多这样的数组切片。
看31行:好像有点奇怪,其实这是一种很聪明的缩写。它可以用下面的比较长的方法来重写:
new POE::Session (
...
child_input => &main::child_input,
child_done => &main::child_done,
child_error => &main::child_error,
...
);
这是一个方便的做法来写出很多的状态名字,这些名字跟事件的名字是一样的。——你可以传递包名或者
对象来作为键,或者是任何一个有子例程,方法名组成的数组引用。可以参考POE::Session得到更多的信息。
最后,在POE::Session 构造器参数列表最后的数组引用是我们要手动传递给_start状态的参数列表。
如果POE::Wheel::SocketFactory在创建监听端口或者接受连接时产生了问题,下面的会发生:
36 sub accept_failed {
37 my ($function, $error) = @_[ARG0, ARG2];
38 delete $_[HEAP]->{listener};
39 print "SERVER: call to $function() failed: $error.\n";
40 }

打印错误信息是正常的处理方式,但是我们为什么还要从堆栈中删除SocketFactory wheel呢?答案在于
POE管理会话资源的方式。只要会话还能产生和接受事件,那么它就被认为是活动的。如果没有wheel,POE内核
会认为会话已经死了,并收集会话的资源。服务端得到事件的唯一方式就是从SocketFactory wheel,如果
它被消灭了,POE内核会一直等待,直到所有它的子会话结束,再收集所有占用的资源。在这一点上,既然
已经没有剩下的会话需要执行,POE内核将会结束并退出。
所以,基本上来说,这是最通常的结束POE会话的方法:结束所有会话资源,让内核干剩下的工作。现在,
我们谈到子会话的细节。


41 sub child_start {
42 my ($heap, $socket) = @_[HEAP, ARG0];
43 $heap->{readwrite} = new POE::Wheel::ReadWrite
44 ( Handle => $socket,
45 Driver => new POE::Driver::SysRW (),
46 Filter => new POE::Filter::Line (),
47 InputState => 'child_input',
48 ErrorState => 'child_error',
49 );
50 $heap->{readwrite}->put( "Hello, client!" );
51 $heap->{peername} = join ':', @_[ARG1, ARG2];
52 print "CHILD: Connected to $heap->{peername}.\n";
53 }
每当新的子会话被创建来处理最新的连接客户端时上面的代码就会被调用。这里我们要介绍一种新的
POE wheel:ReadWrite wheel,这是一个由事件驱动的处理I/O任务的wheel。我们把它传递给文件
句柄,一个驱动,一个过滤器等等。接着,每当新的数据到达文件句柄时,wheel将发送一个child_input
事件给会话,如果有任何错误发生就会发送一个child_error 事件。
我们理解用这个新的wheel来输出字符串”Hello,client”给socket。最后,我们存储在堆栈中
的客户端地址和端口,并打印成功信息。
我们省略了 child_stop 的状态讨论。它只有一行长度。现在我们直接看主要的child_input 程序。

57 sub child_input {
58 my $data = $_[ARG0];
59 $data =~ tr{0-9+*/()-}{}cd;
60 return unless length $data;
61 my $result = eval $data;
62 chomp $@;
63 $_[HEAP]->{readwrite}->put( $@ || $result );
64 print "CHILD: Got input from peer: \"$data\" = $result.\n";
65 }
当客户端发送给我们一行数据,我们取得里面简单的算术表达式并eval它,然后把结果或者错误信息
发送给客户端。通常情况下,将客户端发送来的不可信任的数据直接就eval是非常危险的,所以我们要确保
在eval前,将字符串中所有的非数学字符全部过滤掉。子会话将一直保持接受数据直到客户端断开。
所有事件驱动的应用都是使用POE的好地方。
源代码:http://www.perl.com/2001/01/poe-math3.pl

netkiller

unread,
Oct 5, 2008, 11:53:11 PM10/5/08
to netkiller
by Jeffrey Goff
January 17, 2001

By Dennis Taylor, with Jeff Goff
What Is POE, And Why Should I Use It?

Table of Contents
•POE Design

•A Simple Example

•That's All For Today

•Related Links

Most of the programs we write every day have the same basic blueprint:
they start up, they perform a series of actions, and then they exit.
This works fine for programs that don't need much interaction with
their users or their data, but for more complicated tasks, you need a
more expressive program structure.

That's where POE (Perl Object Environment) comes in. POE is a
framework for building Perl programs that lends itself naturally to
tasks which involve reacting to external data, such as network
communications or user interfaces. Programs written in POE are
completely non-linear; you set up a bunch of small subroutines and
define how they all call each other, and POE will automatically switch
between them while it's handling your program's input and output. It
can be confusing at first, if you're used to procedural programming,
but with a little practice it becomes second nature.
POE Design

It's not much of an exaggeration to say that POE is a small operating
system written in Perl, with its own kernel, processes, interprocess
communication (IPC), drivers, and so on. In practice, however, it just
boils down to a simple system for assembling state machines. Here's a
brief description of each of the pieces that make up the POE
environment:

States
The basic building block of the POE program is the state, which is
a piece of code that gets executed when some event occurs -- when
incoming data arrives, for instance, or when a session runs out of
things to do, or when one session sends a message to another.
Everything in POE is based around receiving and handling these
events.
The Kernel
POE's kernel is much like an operating system's kernel: it keeps
track of all your processes and data behind the scenes, and schedules
when each piece of your code gets to run. You can use the kernel to
set alarms for your POE processes, queue up states that you want to
run, and perform various other low-level services, but most of the
time you don't interact with it directly.
Sessions
Sessions are the POE equivalent to processes in a real operating
system. A session is just a POE program which switches from state to
state as it runs. It can create ``child'' sessions, send POE events to
other sessions, and so on. Each session can store session-specific
data in a hash called the heap, which is accessible from every state
in that session.

POE has a very simple cooperative multitasking model; every
session executes in the same OS process without threads or forking.
For this reason, you should beware of using blocking system calls in
POE programs.

Those are the basic pieces of the Perl Object Environment, although
there are a few slightly more advanced parts that we ought to explain
before we go on to the actual code:

Drivers
Drivers are the lowest level of POE's I/O layer. Currently,
there's only one driver included with the POE distribution --
POE::Driver::SysRW, which reads and writes data from a filehandle --
so there's not much to say about them. You'll never actually use a
driver directly, anyhow.
Filters
Filters, on the other hand, are inordinately useful. A filter is a
simple interface for converting chunks of formatted data into another
format. For example, POE::Filter::HTTPD converts HTTP 1.0 requests
into HTTP::Request objects and back, and POE::Filter::Line converts a
raw stream of data into a series of lines (much like Perl's <>
operator).
Wheels
Wheels contain reusable pieces of high-level logic for
accomplishing everyday tasks. They're the POE way to encapsulate
useful code. Common things you'll do with wheels in POE include
handling event-driven input and output and easily creating network
connections. Wheels often use Filters and Drivers to massage and send
off data. I know this is a vague description, but the code below will
provide some concrete examples.
Components
A Component is a session that's designed to be controlled by other
sessions. Your sessions can issue commands to and receive events from
them, much like processes communicating via IPC in a real operating
system. Some examples of Components include POE::Component::IRC, an
interface for creating POE-based IRC clients, or
POE::Component::Client::HTTP, an event-driven HTTP user agent in Perl.
We won't be using any Components in this article, but they're a very
useful part of POE nevertheless.

A Simple Example

For this simple example, we're going to make a server daemon which
accepts TCP connections and prints the answers to simple arithmetic
problems posed by its clients. When someone connects to it on port
31008, it will print ``Hello, client!''. The client can then send it
an arithmetic expression, terminated by a newline (such as ``6 + 3\n''
or ``50 / (7 - 2)\n'', and the server will send back the answer. Easy
enough, right?

Writing such a program in POE isn't terribly different from the
traditional method of writing daemons in Unix. We'll have a server
session which listens for incoming TCP connections on port 31008. Each
time a connection arrives, it'll create a new child session to handle
the connection. Each child session will interact with the user, and
then quietly die when the connection is closed. And best of all, it'll
only take 74 lines of modular, simple Perl.

The program begins innocently enough:

1 #!/usr/bin/perl -w
2 use strict;
3 use Socket qw(inet_ntoa);
4 use POE qw( Wheel::SocketFactory Wheel::ReadWrite
5 Filter::Line Driver::SysRW );
6 use constant PORT => 31008;

Here, we import the modules and functions which the script will use,
and define a constant value for the listening port. The odd-looking
qw() statement after the ``use POE'' is just POE's shorthand way for
pulling in a lot of POE:: modules at once. It's equivalent to the more
verbose:

use POE;
use POE::Wheel::SocketFactory;
use POE::Wheel:ReadWrite;
use POE::Filter::Line;
use POE::Driver::SysRW;

Now for a truly cool part:

7 new POE::Session (
8 _start => \&server_start,
9 _stop => \&server_stop,
10 );
11 $poe_kernel->run();
12 exit;

That's the entire program! We set up the main server session, tell the
POE kernel to start processing events, and then exit when it's done.
(The kernel is considered ``done'' when it has no more sessions left
to manage, but since we're going to put the server session in an
infinite loop, it'll never actually exit that way in this script.) POE
automatically exports the $poe_kernel variable into your namespace
when you write ``use POE;''.

The new POE::Session call needs a word of explanation. When you create
a session, you give the kernel a list of the events it will accept. In
the code above, we're saying that the new session will handle the
_start and _stop events by calling the &server_start and &server_stop
functions. Any other events which this session receives will be
ignored. _start and _stop are special events to a POE session: the
_start state is the first thing the session executes when it's
created, and the session is put into the _stop state by the kernel
when it's about to be destroyed. Basically, they're a constructor and
a destructor.

Now that we've written the entire program, we have to write the code
for the states which our sessions will execute while it runs. Let's
start with (appropriately enough) &server_start, which is called when
the main server session is created at the beginning of the program:

13 sub server_start {
14 $_[HEAP]->{listener} = new POE::Wheel::SocketFactory
15 ( BindPort => PORT,
16 Reuse => 'yes',
17 SuccessState => \&accept_new_client,
18 FailureState => \&accept_failed
19 );
20 print "SERVER: Started listening on port ", PORT, ".\n";
21 }

This is a good example of a POE state. First things first: Note the
variable called $_[HEAP]? POE has a special way of passing arguments
around. The @_ array is packed with lots of extra arguments -- a
reference to the current kernel and session, the state name, a
reference to the heap, and other goodies. To access them, you index
the @_ array with various special constants which POE exports, such as
HEAP, SESSION, KERNEL, STATE, and ARG0 through ARG9 to access the
state's user-supplied arguments. Like most design decisions in POE,
the point of this scheme is to maximize backwards compatibility
without sacrificing speed. The example above is storing a
SocketFactory wheel in the heap under the key 'listener'.

The POE::Wheel::SocketFactory wheel is one of the coolest things about
POE. You can use it to create any sort of stream socket (sorry, no UDP
sockets yet) without worrying about the details. The statement above
will create a SocketFactory that listens on the specified TCP port
(with the SO_REUSE option set) for new connections. When a connection
is established, it will call the &accept_new_client state to pass on
the new client socket; if something goes wrong, it'll call the
&accept_failed state instead to let us handle the error. That's all
there is to networking in POE!

We store the wheel in the heap to keep Perl from accidentally garbage-
collecting it at the end of the state -- this way, it's persistent
across all states in this session. Now, onto the &server_stop state:

22 sub server_stop {
23 print "SERVER: Stopped.\n";
24 }

Not much to it. I just put this state here to illustrate the flow of
the program when you run it. We could just as easily have had no _stop
state for the session at all, but it's more instructive (and easier to
debug) this way.

Here's where we create new sessions to handle each incoming
connection:

25 sub accept_new_client {
26 my ($socket, $peeraddr, $peerport) = @_[ARG0 .. ARG2];
27 $peeraddr = inet_ntoa($peeraddr);
28 new POE::Session (
29 _start => \&child_start,
30 _stop => \&child_stop,
31 main => [ 'child_input', 'child_done',
'child_error' ],
32 [ $socket, $peeraddr, $peerport ]
33 );
34 print "SERVER: Got connection from $peeraddr:$peerport.\n";
35 }

Our POE::Wheel::SocketFactory will call this subroutine whenever it
successfully establishes a connection to a client. We convert the
socket's address into a human-readable IP address (line 27) and then
set up a new session which will talk to the client. It's somewhat
similar to the previous POE::Session constructor we've seen, but a
couple things bear explaining:

@_[ARG0 .. ARG2] is shorthand for ($_[ARG0], $_[ARG1], $_[ARG2]).
You'll see array slices used like this a lot in POE programs.

What does line 31 mean? It's not like any other event_name = state>
pair that we've seen yet. Actually, it's another clever abbreviation.
If we were to write it out the long way, it would be:

new POE::Session (
...
child_input => &main::child_input,
child_done => &main::child_done,
child_error => &main::child_error,
...
);

It's a handy way to write out a lot of state names when the state name
is the same as the event name -- you just pass a package name or
object as the key, and an array reference full of subroutine or method
names, and POE will just do the right thing. See the POE::Session docs
for more useful tricks like that.

Finally, the array reference at the end of the POE::Session
constructor's argument list (on line 32) is the list of arguments
which we're going to manually supply to the session's _start state.

If the POE::Wheel::SocketFactory had problems creating the listening
socket or accepting a connection, this happens:

36 sub accept_failed {
37 my ($function, $error) = @_[ARG0, ARG2];
38 delete $_[HEAP]->{listener};
39 print "SERVER: call to $function() failed: $error.\n";
40 }

Printing the error message is normal enough, but why do we delete the
SocketFactory wheel from the heap? The answer lies in the way POE
manages session resources. Each session is considered ``alive'' so
long as it has some way of generating or receiving events. If it has
no wheels and no aliases (a nifty POE feature which we won't cover in
this article), the POE kernel realizes that the session is dead and
garbage-collects it. The only way the server session can get events is
from its SocketFactory wheel -- if that's destroyed, the POE kernel
will wait until all its child sessions have finished, and then garbage-
collect the session. At this point, since there are no remaining
sessions to execute, the POE kernel will run out of things to do and
exit.

So, basically, this is just the normal way of getting rid of unwanted
POE sessions: dispose of all the session's resources and let the
kernel clean up. Now, onto the details of the child sessions:

41 sub child_start {
42 my ($heap, $socket) = @_[HEAP, ARG0];
43 $heap->{readwrite} = new POE::Wheel::ReadWrite
44 ( Handle => $socket,
45 Driver => new POE::Driver::SysRW (),
46 Filter => new POE::Filter::Line (),
47 InputState => 'child_input',
48 ErrorState => 'child_error',
49 );
50 $heap->{readwrite}->put( "Hello, client!" );
51 $heap->{peername} = join ':', @_[ARG1, ARG2];
52 print "CHILD: Connected to $heap->{peername}.\n";
53 }

This gets called every time a new child session is created to handle a
newly connected client. We'll introduce a new sort of POE wheel here:
the ReadWrite wheel, which is an event-driven way to handle I/O tasks.
We pass it a filehandle, a driver which it'll use for I/O calls, and a
filter that it'll munge incoming and outgoing data with (in this case,
turning a raw stream of socket data into separate lines and vice
versa). In return, the wheel will send this session a child_input
event whenever new data arrives on the filehandle, and a child_error
event if any errors occur.

We immediately use the new wheel to output the string ``Hello,
client!'' to the socket. (When you try out the code, note that the
POE::Filter::Line filter takes care of adding a line terminator to the
string for us.) Finally, we store the address and port of the client
in the heap, and print a success message.

We will omit discussion of the child_stop state, since it's only one
line long. Now for the real meat of the program: the child_input
state!

57 sub child_input {
58 my $data = $_[ARG0];
59 $data =~ tr{0-9+*/()-}{}cd;
60 return unless length $data;
61 my $result = eval $data;
62 chomp $@;
63 $_[HEAP]->{readwrite}->put( $@ || $result );
64 print "CHILD: Got input from peer: \"$data\" = $result.\n";
65 }

When the client sends us a line of data, we strip it down to a simple
arithmetic expression and eval it, sending either the result or an
error message back to the client. Normally, passing untrusted user
data straight to eval() is a horribly dangerous thing to do, so we
have to make sure we remove every non-arithmetic character from the
string before it's evaled (line 59). The child session will happily
keep accepting new data until the client closes the connection. Run
the code yourself and give it a try!

The child_done and child_error states should be fairly self-
explanatory by now -- they each delete the child session's ReadWrite
wheel, thus causing the session to be garbage-collected, and print an
expository message explaining what happened. Easy enough.
That's All For Today

And that's all there is to it! The longest subroutine in the entire
program is only 12 lines, and all the complicated parts of the server-
witing process have been offloaded to POE. Now, you could make the
argument that it could be done more easily as a procedural-style
program, like the examples in man perlipc. For a simple example
program like this, that would probably be true. But the beauty of POE
is that, as your program scales, it stays easy to modify. It's easier
to organize your program into discrete elements, and POE will provide
all the features you would otherwise have had to hackishly reinvent
yourself when the need arose.

So give POE a try on your next project. Anything that would ordinarily
use an event loop would be a good place to start using POE. Have fun!

Source Listing....
Related Links

http://poe.perl.org/
The POE home page. All good things stem from here.
Reply all
Reply to author
Forward
0 new messages