前面的server是用getline实现的,想改用sysread实现
但是coro默认的sysread的行为是模拟系统的sysread,和我的需求不太一样
我希望是指定读取nbyte的字符,如果不满nbyte,就全部返回,为空就等待
而sysread会等待达到指定长度直到超时
需要对sysread对应的READ方法作如下修改
sub READ {
my $len = $_[2];
my $ofs = $_[3]; #偏移量,默认为0
my $res;
# first deplete the read buffer
if (length $_[0][3]) {
my $l = length $_[0][3];
if ($l <= $len) { #收到数据不足指定长度,buf为数据,,返回值为长度
substr ($_[1], $ofs) = $_[0][3]; $_[0][3] = "";
$len -= $l;
$ofs += $l;
$res += $l;
# return $res unless $len; #原来只有读够才返回
return $res if $l;
} else { #超过指定长度,返回部分.返回值为长度,数据在buf
substr ($_[1], $ofs) = substr ($_[0][3], 0, $len);
substr ($_[0][3], 0, $len) = "";
return $len;
}
}
while() {
my $r = sysread $_[0][0], $_[1], $len, $ofs;
if (defined $r) {
$len -= $r;
$ofs += $r;
$res += $r;
######################增加eof的处理
if($r ==0 ){
# EOF
return 0 unless length $_[0][3];
}
######################
#last unless $len && $r; #原来只有读够或者没读到,才返回,
last if $r; #改为if $r,只要有返回就last
} elsif ($! != EAGAIN && $! != EINTR && $! != WSAEWOULDBLOCK) {
last;
}
last if $_[0][8] || !&readable;
}
$res
}
为了不改动原有的sysread功能,我参照readline实现了一个新的READSTR方法,就通过原来的read接口(反正原来read和sysread是一样的)
感觉这样更好一些
#目标:实现一个类似getline的方法,读到指定长度或者buf为空就返回,不判断超时
sub READSTR {
my $len = $_[2];
my $ofs = $_[3];
my $res;
while () {
my $len = sysread $_[0][0],$_[0][3], $len, $ofs;
unless ($len) {
if (defined $len) {
# EOF
return undef unless length $_[0][3];
return delete $_[0][3];
} elsif (($! != EAGAIN && $! != EINTR && $! != WSAEWOULDBLOCK) || !&readable) {
return length $_[0][3] ? delete $_[0][3] : undef;
}
}
if (length $_[0][3]) {
my $l = length $_[0][3];
if ($l <= $len) { #不足指定长度,全部返回
return delete $_[0][3];
}
else{#超过指定长度,返回部分
$res = substr ($_[0][3], 0, $len);
#清空部分buf
substr ($_[0][3], 0, $len) = "";
return $res;
}
}
}
}
使用该方法改造的coro echo server
#!/bin/env perl
#sever coro::socket+coro::handle来实现
#不使用getline,改为sysread(不完美)
use strict;
use Coro;
use AnyEvent;
#这个是ae的perl实现,要比EV慢一点点
use AnyEvent::Impl::Perl;
use Coro::Socket;
use Coro::Handle;
my $port = 11212;
#1.创建监听端口
$|++; # 因为print到终端,所以这里要打开autoflush
my $s = Coro::Socket->new(LocalAddr => 'localhost', # 创建一个侦听socket
LocalPort => $port,
Listen => 5,
Proto => 'tcp')
or die $@;
#2.处理handle
my @coro;
while(1){
my ($fh, $peername) = $s->accept;
next unless $peername;
print "accepet new sock at" . $fh->fileno . "\n";
&doit($fh);
}
sub doit($){
my ($fh)=@_;
push @coro, async {
$fh->autoflush(1);
my $line="";
#while(my $line=$fh->readline()){
#while(my $ret=$fh->sysread($line,64)){
#while($fh->readable){
while(my $line=$fh->read($buf,32)){
#my $line=$fh->readline();
#my $ret=$fh->recv($line,32);
#my $ret=$fh->sysread($line,32);
print $fh->fileno, " ", $line, " ";
$fh->print($line);
}
print "no more data, close socket " . $fh->fileno . "\n";
$fh->close;
#关闭协程
return;
}
}
说明,在这个地方卡了很久.
对coro的readble,sysread这些方法理解不透.(不明白从原来的阻塞方式改为现在的非阻塞方式究竟有哪些变化)
作者只是一句Work like their function equivalents轻轻带过(囧阿,similar...)
后来还是参照源码,添加打印信息,一点点摸索.
目前还是对readable的实现不理解,感觉不像作者说的
Wait until the filehandle is readable or writable (and return true) or
until an error condition happens (and return false).
而是始终为true.
否则用readable配合recv等直读方法也是可以的