POE学习笔记
福瑞哈哥
|
1#
福瑞哈哥 发表于 2007-03-13 17:53
POE学习笔记
在程序需要同时处理大量连接的情况下,比如服务器程序、spider程序等,一般可以采用多进程、多线程和非阻塞IO三种方式。我自己编程只喜欢用非阻塞IO。在C下面有libevent库可以用,相比较POE是一款高端产品,刚开始有一点摸不着边际,熟悉之后感觉还是很贴心的。
POE主要分以下几个组件Kernel、Session、Wheel、Filter、Driver,还有更高级的Component组件,不过基本上是前面几种组件的组合。Kernel是POE核心,内部实现了IO读写信号回调等处理,简单应用程序与Kernel交互并不多。Session是一个处理线程,比如一个服务器程序每一个客户端连接就应该对应于一个Session,同理Spider程序中对于每一个web服务器的连接也应该对应于一个Session,一个应用程序可以有很多Session。Wheel、Filter、Driver是对底层IO的封装。 来看一个简单的客户端的例子:
[Copy to clipboard] [ - ]
CODE:
use warnings;
use strict; use POE; use POE::Wheel::SocketFactory; use POE::Wheel::ReadWrite; POE::Session->create ( inline_states => { _start => \&start, connected => \&connected, flushed => \&flushed, } ); POE::Kernel->run; sub start { print "_start\n"; my $wheel = POE::Wheel::SocketFactory->new ( RemoteAddress => 'localhost', RemotePort => 8000, SuccessEvent => "connected", FailureEvent => "_stop" , ); $_[HEAP]->{wheel} = $wheel; } sub connected { print "connected\n"; my ($kernel, $heap, $socket) = @_[KERNEL, HEAP, ARG0]; my $wheel = POE::Wheel::ReadWrite->new ( Handle => $socket, FlushedEvent => 'flushed', ); $heap->{wheel} = $wheel; $wheel->put("hello server"); } sub flushed { print "flushed\n"; delete $_[HEAP]->{wheel}; } 打开两个终端,在其中一个输入nc -l -p 8000,另一个终端中执行上面的程序,可以看到在nc终端中输出了hello server,在客户端终端中输出了_started、connected、flushed三行信息。 这个程序基本上可以分为三部分 1) POE::Session->create,创建一个Session。 2) POE::Kernel->run, 启动框架消息分发。 3) sub start ... 定义各种状态的回调函数。 在创建一个Session时,最重要的参数是inline_states,指定各种状态的回调函数。其中以_字符开始的状态是POE系统定义状态,其他是应用程序自定义状态。POE启动后就会自动将系统中每一个Session的状态设为_start,因此_start对应的处理函数就会被调用,因此下面的代码:
[Copy to clipboard] [ - ]
CODE:
use POE;
POE::Session->create ( inline_states => { _start => sub { print "hello world\n"; } } ); POE::Kernel->run; 会直接打印hello world后退出。 接着看上面的例子中的_start状态的处理函数,其中的代码 my $wheel = POE::Wheel::SocketFactory->new 创建了一个Wheel::SocketFactory,这个Wheel会根据你指定的参数去连接远程的服务,在连接完成后,自动触发SuccessEvent参数指定的状态(上面的程序是connected状态)。 在connected状态的处理函数中, my $wheel = POE::Wheel::ReadWrite->new 创建了一个Wheel::ReadWrite,在构造函数中你可以指定可读、出错和所有数据已经发送三种情况的对应状态,然后你就可以在对应状态的处理函数中处理各种情况了。 上面的程序通过指定FlushedEvent,标明了当所有数据被发送后这个Session会被触发到flushed状态。 (简单总结一下,对应Wheel的参数一般是当某种情况发生时,所属的Session的应该被置为什么状态,而Session的参数主要是各种状态的处理函数。) 在各个回调函数中,你可以通过@_[KERNEL]得到系统Kernel对象,通过@_[HEAP]得到Session相关数据对象(比如在服务器程序每个Session中就会保留这个客户端的相关信息,地址用户名等),其他不同的处理函数得到的额外参数个数不同,分别可以通过@_[ARG0]、@_[ARG1]、@_[ARG2]得到。比如connected的处理函数就通过@_[ARG0]得到了SocketFactory创建的socket对象。 下面这一据代码: $heap->{wheel} = $wheel; 有着特殊的意义,当一个Wheel被创建后,应用程序必须把它保存在某处,否则当离开作用域对象被注销后,所有的功能都无法实现了。保存在Session相关数据HEAP中是一个非常自然的选择。类似于flushed处理函数中的代码: delete $_[HEAP]->{wheel}; 如果不执行这一句,Wheel就永远存在,程序也就无法退出了。 下面给出一个简单POE应用程序的框架代码:
[Copy to clipboard] [ - ]
CODE:
use warnings;
use strict; use POE; use POE::Wheel::Somewheel; POE::Session->create ( inline_states => { _start => sub { # do something initial # new some Wheel # put the wheel in HEAP }, state_1 => sub { # handle state 1 # new some other Wheel }, state_2 => sub { # handle state 2 }, } ); POE::Kernel->run; |