扶凯

取势 明道 优术

作者为 扶 凯 发表

因为公司为了节约流量,所以准备了一下测试 Flash 的 p2p 功能,合作公司让提供一个 crossdoman.xml 文件,但很搞笑的是,最开始我没了解清楚,也没去查相关的资料一直以为是 HTTP 服务需要吐这个文件出来。后来才知道,原来是需要 Socket 的方式吐出来。并且需要在 CDN 的节点上开个 843 的端口。并启用 Socket 的服务吐出一个 crossdoman.xml 文件。

所以和合作的人员了解了一下接口,自己花了几分钟,简单使用 AnyEvent 来简单实现了一个这样的 socket 的服务器并且也能正常的工作了(有小 bug 但不影响使用)。

#!/usr/bin/perl
use warnings;
use strict;  

use EV;
use AnyEvent::Socket;  
use AnyEvent::Handle;  

my $success = <<_EOF_; 
<?xml version="1.0"?>  
<cross-domain-policy>
<allow-access-from domain="*" to-ports="*" />
</cross-domain-policy>
_EOF_


my $port = 843;

tcp_server undef, $port, sub {  
    my ($fh) = @_  or die "tcp_server: $!";  

    my $hdl = new AnyEvent::Handle fh => $fh,
       on_error => sub {
          my ($hdl, $fatal, $msg) = @_;
          $hdl->destroy;
       };
    ;  

    $hdl->push_read (line => "<policy-file-request/>\0", sub {  
          $hdl->push_write($success.chr(0));  
          $hdl->destroy;
    });  
};  

EV::run;



后来花了几分钟到网上了解了一下来源,发现在 Flash player 9.0.124.0 以后,当 flash 文件要进行 socket 通信的时候,需要向服务器端获取 crossdomain.xml 文件。因为安全沙箱的原因如果找不到这个文件就出现客户端无法连接服务器的现象。所以当封装在页面的 flash 发起 socket 通信请求的时候会先寻找服务器端的 843 端口,获取 Crossdomain.xml 文件。

Flash Player 在你的 socket.connect("domain",port) 运行之前,会向你的 socket 服务器的 843 端口发送一个字符串 "<policy-file-request/>",这个时候如果你有一个服务在监听 843 端口那么收到这个字符串之后,直接按照 XML 格式发回策略文件就解决了。(注意发回的时候记得加一个截止字符"\0")。

现在我们不讲 Flash 的问题,我是想来介绍 AnyEvent,让大家了解和知道它是非常的方便,从上面的代码例子可以见到,要使用他开发一个高性能的 TCP 的服务器相当的方便,因为有了 AnyEvent::Handle 这个模块,他直接可以帮你按各种规则来对读写队列进行操作。接口相当简单和方便。

当用户的请求过来时,会直接给用户的请求当成句柄,在上面是直接存到 $fh 中,然后我们给这个 $fh 注册到 AnyEvent::Handle 上,其它的操作都使用 AnyEvent::Handle 的相关接口进行操作。push_read 会不断的从用户端读入数据,真到达到 line 上面的截止字符时,条件成立会调用 push_read 后面指定的匿名子函数。调用时,我们直接使用 push_write 来返回给客户端响应就好了。因为这个协议通信要求截止字符为 "\0",所以我使用了 chr(0) 来返回。

可以见到,做操作相当方便,就象大师 Achilles Xu 讲的,因为 AnyEvent ,Plack 和 Mojo(Dancer) 之类,让很多人又重新回归到 Perl 上了。
上面应用的测试脚本

#!/usr/bin/perl
use strict;
use IO::Socket;
my $host = $ARGV[0] || '127.0.0.1';
my $port = $ARGV[1] || 843;

my $sock = IO::Socket::INET->new(
    Proto    => 'tcp',
    PeerAddr => $host,
    PeerPort => $port,
) or die "Cannot open client socket: $!";
$sock->autoflush;

$sock->print("<policy-file-request/>\0");

my $res = join('', <$sock>);
print "$res\n";

PS: 发现有人使用 Anyevent 写了一个现成的服务出来了。我又重复造了一个轮子。
https://github.com/jegade/p5-AnyEvent-FlashSocketPolicy

来了就留个评论吧! 没有评论