coro提供了perl的非阻塞io.但是作者提供的文档太简略了,例子也没有.
自己尝试写了几个例子.
分别实现CU上仙子提供的例子以及AE自带的echo server例子
相关的模块有
coro:;socket,coro::handle
ae::socket,ae::handle
先看看传统的socket编程(CU上仙子提供的例子)
客户端使用多进程并发
use strict;
use IO::Socket;
my @childs;
for (1..3) { # fork 3个子进程,同时发送数据
my $child = fork;
if ($child) {
push(@childs, $child);
} else {
# 创建到server的连接socket
my $sock=IO::Socket::INET->new(PeerAddr => 'localhost',
PeerPort => 1234,
Proto => 'tcp') or die $@;
for (1..10) { # 每个子进程里,发送10次数据
print $sock random() . "\n";
select(undef, undef, undef, 0.25); # 每发送一次,就休眠0.25秒
}
$sock->close or die $!; # 发送完后关闭socket,并退出子进程
exit 0;
}
}
for (@childs) { # 回收子进程
my $tmp = waitpid($_, 0);
print "done with pid $tmp\n";
}
sub random { # 该函数产生随机字串
my @x=(0..9,'a'..'z','A'..'Z');
join '',map {$x[int rand @x]} 1..49; # 返回49字节长度的串
}
服务器端使用select复用模式
use strict;
use IO::Socket;
use IO::Select;
$|++; # 因为print到终端,所以这里要打开autoflush
my $s = IO::Socket::INET->new(LocalAddr => 'localhost', # 创建一个侦听socket
LocalPort => 1234,
Listen => 5,
Proto => 'tcp')
or die $@;
my $read_set = new IO::Select(); # 创建一个IO::Select目标
$read_set->add($s); # 把上述侦听socket加入IO::Select的检查队列
while (1) { # 一个死循环
# IO::Select有一个静态select方法,第一个参数如果设置,表示检查可读的socket
# 该方法一直block,直到有可用的句柄返回
# 返回一个三参数列表,第一个参数表示可读的socket句柄集合(一个数组引用)
my ($rh_set) = IO::Select->select($read_set, undef, undef, undef);
foreach my $rh (@$rh_set) { # 遍历可读的socket
# 如果当前可读的socket等于侦听socket,那么说明有请求进来,应该及时accept
# accept后,把已建立连接的socket句柄加入检查队列
if ($rh == $s) {
my $ns = $rh->accept();
$read_set->add($ns);
# 使用sysread读取数据,每次读取32字节,并且只处理这32字节
# 如果客户端发送多于32字节的数据包,会分几次处理,如果几个客户端同时发送,处理过程是无序的
# 避免使用<>方式读取socket,因为<>面向行读取,perlio对它做了缓冲,IO::Select看不到这个缓冲
}else {
my $buf = undef;
if (sysread($rh,$buf,32)) {
print $rh->fileno, " ", $buf, " ";
# sysread的返回是读取的字节数量,如果返回0,则说明抵达文件末尾(EOF)
# 如果sysread返回0,那么说明客户端关闭socket,我们从检查队列里删除该句柄,同时关闭socket句柄
} else {
print "no more data, close socket " . $rh->fileno . "\n";
$read_set->remove($rh);
$rh->close;
}
}
}
}
下面使用coro来进行改造