这个其实只有理论上的意义,方便了解coro的原理
实际上既然用了select ,再用coro反而显得很笨拙.
#!/bin/env perl
#select sever coro版(ok)
#换一种写法,每个连接起一个协程
#因为select直接可以得到有数据的socket
#所以可以用cede_to,直接唤醒对应的协程
#主要演示一下cede_to和schedule的用法(类似lua和python)
#没有用到coro的自动调度
use strict;
use IO::Socket;
use IO::Select;
use Coro;
#1.创建监听端口
$|++; # 因为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的检查队列
my @coro;
my %table;
my $conum=0;
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);
print "accepet new sock at" . $ns->fileno . "\n";
#为每个连接创建单独的协程
&doit($ns);
}
#如果是已有的socket,那么切换到协程(不需要指定具体协程,由coro调度实现)
else{
#print "receive data on existing sock.\n";
my $coi= $table{$rh->fileno}-1;
$coro[$coi]->resume;
$coro[$coi]->cede_to;
}
#协程调度
#cede;
}
}
#协程的处理
sub doit($){
my ($rh)=@_;
my $fno=$rh->fileno;
#print "receive data on existing sock " . $rh->fileno . "\n";
#if($table{$fno} > 0){
#return;
#}
print "new coro $fno.\n";
$conum++;
$table{$fno} =$conum;
push @coro, async {
# 使用sysread读取数据,每次读取32字节,并且只处理这32字节
# 如果客户端发送多于32字节的数据包,会分几次处理,如果几个客户端同时发送,处理过程是无序的
# 避免使用<>方式读取socket,因为<>面向行读取,perlio对它做了缓冲,IO::Select看不到这个缓冲
my $buf = undef;
while(1){
if (sysread($rh,$buf,32)) {
print $rh->fileno, " ", $buf, " ";
#每处理32byte切换
#cede;
schedule;
}
else{
# sysread的返回是读取的字节数量,如果返回0,则说明抵达文件末尾(EOF)
# 如果sysread返回0,那么说明客户端关闭socket,我们从检查队列里删除该句柄,同时关闭socket句柄
print "no more data, close socket " . $rh->fileno . "\n";
$read_set->remove($rh);
$rh->close;
#关闭协程
return;
}
}
}
}