- 论坛徽章:
- 0
|
Filter:对数据进行包装和拆解
凡是通讯都要涉及到通讯协议的制订,在POE框架中通过Filter层完成对数据的包装和拆解。最简单的Filter是Filter::Stream,它什么也不做只是简单传递原始数据,应用程序需要在事件处理函数中对消息格式进行处理。Wheel ReadWrite缺省使用Filter::Line,这是一个行消息协议处理器,凡是发送的数据都会被自动在末尾加上行分隔符,接收数据时则按进行分割。这就是上面的聊天室程序在没有设置任何消息格式却能正常通讯的原因。
我们可以设计自己的Filter,来对数据进行封装,只需要在创建Wheel ReadWrwite时设置InputFilter和OutputFilter参数即可。设计一个Filter,最重要是实现get_one_start、get_one和put三个函数。对于输出Filer,put函数接收一个数组引用的参数,这个数组里面是应用程序发送的数据包队列,需要返回一个数组引用,数组里面是封装好的数据报队列。对于输入Filter,get_one_start接收一个数组引用,数组里面包含未经处理的远程数据包队列,这个函数需要把这个数据包队列保存起来,然后其他组件可以反复调用get_one得到按照通讯协议分割好的数据包。
下面是经过修改后的聊天室客户端程序,里面包含了一个WhoSaidFilter的类。客户端在启动时接收一个命令行参数作为客户端的名字$name,当用户在终端输入消息后,它会经过WhoSaidFilter把消息$message变成"$name said: $message, IMHO\n"发送给服务端。这个通讯协议兼容Line通讯协议(都用行分割符作为数据包的结束标志),因此服务端不需要更换它的InputFilter。
- use warnings;
- use strict;
- use IO::Socket;
- use POE qw /Wheel::SocketFactory Wheel::ReadWrite Wheel::ReadLine/;
- my $name = shift;
- $name = "Anonymous" unless $name;
- # 设计自己Filter
- @WhoSaidFilter::ISA = qw (POE::Filter);
- sub WhoSaidFilter::new {
- my $type = shift;
- my %params = @_;
- my $name = delete $params{Name};
- return bless { name => $name }, $type;
- }
- # 重载put函数
- sub WhoSaidFilter::put {
- my ($self, $bufs) = @_;
- my @raw;
- foreach (@$bufs) {
- push @raw, "$self->{name} Said: $_, IMHO\n";
- }
- \@raw;
- }
- POE::Session->create
- ( inline_states =>
- { _start => \&start_chat,
- connected => \&connected,
- connect_fail => \&connect_fail,
- server_input => \&server_input,
- user_input => \&user_input,
- }
- );
-
- POE::Kernel->run;
- sub start_chat {
- my $wheel = POE::Wheel::SocketFactory->new
- ( RemoteAddress => 'localhost',
- RemotePort => 8000,
- SuccessEvent => "connected",
- FailureEvent => "connect_fail",
- );
- $_[HEAP]->{server} = $wheel;
- }
- sub connected {
- my ($kernel, $heap, $socket) = @_[KERNEL, HEAP, ARG0];
- my $wheel = POE::Wheel::ReadWrite->new
- ( Handle => $socket,
- InputEvent => "server_input",
- ErrorEvent => "error_happened",
- # 使用输出Filter
- OutputFilter => WhoSaidFilter->new( Name => $name ),
- );
- # 下面这一句同时把以前的那个SocketFactory的轮子删掉
- $heap->{server} = $wheel;
-
- my $console = POE::Wheel::ReadLine->new
- ( InputEvent => 'user_input'
- );
- # 告诉ReadLine监控终端
- $console->get( 'input your message, bye to quit: ');
- $heap->{console} = $console;
- }
- sub connect_fail {
- delete $_[HEAP]->{server};
- }
- sub server_input {
- my ($heap, $input) = @_[HEAP, ARG0];
- # 如果使用print "$input\n"会搞乱终端
- $heap->{console}->put( $input );
- }
- sub user_input {
- my ($heap, $input) = @_[HEAP, ARG0];
- if ($input =~ /(quit)|(exit)|(bye)/i) {
- delete $heap->{server};
- delete $heap->{console};
- return;
- }
- # 发送到服务端
- $heap->{server}->put( $input );
- # 继续监控终端
- $heap->{console}->get( 'input your message, bye to quit: ');
- }
复制代码
Filter的功能很强,从简单的行协议通讯、加数据包长度前缀的通讯,到各种网际协议(HTTP、FTP、Jabber、MSN)都可以设计出相应的Filter来处理。POE/Filter下面的各种各样的Filter,可以拿来学习使用。简单一点的通讯协议可以使用Filter Block,会自动在数据包前面加上长度或者根据长度读取数据。这些Filter又都有丰富的选项,可以改变处理细节。比如Filter Line可以设置行分割符,Filter Block可以设置为固定包长度通讯,或者提供自己的数据包前缀处理函数。 |
|