目录
前文
在之前【读源码】workerman中的2种Timer中,我就已经接触到了stream_select,其中基于该函数实现的微秒级延迟更是让我大开眼界,甚至于有人另外将其简化成一个定时器。而有另外一个有相似用法的函数是socket_select,在[udpate]php ping ipv4 by socket中实现xping的实现中也有用到,而之后,我就一直思考是否也可以用workerman中事件的思路实现更高性能的ping呢?在经过workerman启发还有几天的努力,终于还是有点眉目了。
exec ping 和 xping
exec ping
一次只能ping一个ip,并且每次ping会间隔1秒。
所以所花费的时间是:
(1s + (ttl or timeout)) * ping 次数
xping
一次只能ping一个ip,ping间隔可以忽略
所以所花费的时间是:
((ttl or timeout)) * ping 次数
而 ttl计算 和 timeout 恰恰都是同个socket_select阻塞实现的。也就是说每次ping的花费时间是
ttl < ping一次时间 < timeout
- 最大的问题
可以看到,运行是线性的,时间也是线性增加的。并且大部分时间都是花在socket_select,其阻塞的时间太浪费了
xfping新思路
socket_select 是用于检测在限定时间内有被修改的套接字。那么我的思路是,把其等待的时间缩小,先发下个ping包,再一起等回包。其流程则会变成:
可以发现,按这个思路整体运行时间将大大减少,那么开干。
主要会有几个变化
- 新的timeout机制
socket_select 单纯用于检测套接字状态了,那timeout机制则需另外设计
- 非阻塞的socket_recvfrom
多个回包需要socket_recvfrom多次读取,如果设置为阻塞,那么会阻塞至下个回包,影响退出其他部分运行
- TTL计算
之前的单个ping周期甚至可以不用去检测数据的正确与否,而并发ping则需要明确每个回包的对应关系,才可以准确计算出TTL
- 结束机制
之前socket_select 的阻塞和超时可以视为一个ping的结束,现在需要通过回包和超时的计数去判断ping 任务是否结束
xfping 示例
类exec ping 配置
(xfping::init())
->setDebug(0)
->setCallbackDnsError(function($host){
echo 'DnsError :'.$host.PHP_EOL;
})
->setCallbackTimeOut(function($ip){
echo 'timeout :'.$ip.PHP_EOL;
})
->setCallbackOneIp(function($ip,$ret){
echo formet($ip,$ret).PHP_EOL;
})
->setCallbackSingle(function($ip,$seq,$t){
echo 'from ip='.$ip.' seq='.$seq.' t_ms='.($t*1000).PHP_EOL;
})
->setCallback(function ($ret,$t){
echo 'ret '.PHP_EOL;
foreach ($ret as $ip=>$r){
echo formet($ip,$r).PHP_EOL;
}
echo 'total time_ms '.$t;
})->doit('127.0.0.1',4,1000,1000);
from ip=127.0.0.1 seq=0 t_ms=0.3049373626709
from ip=127.0.0.1 seq=1 t_ms=0.26607513427734
from ip=127.0.0.1 seq=2 t_ms=0.24986267089844
from ip=127.0.0.1 seq=3 t_ms=0.26988983154297
ip=127.0.0.1 send=4 recive=4 loss=0.00% min=0.25ms max=0.30ms avg=0.27ms
ret
ip=127.0.0.1 send=4 recive=4 loss=0.00% min=0.25ms max=0.30ms avg=0.27ms
total time_ms 3022.8109359741
批量ping
(xfping::init())
->setDebug(0)
->setCallbackDnsError(function($host){
echo 'DnsError :'.$host.PHP_EOL;
})
->setCallbackTimeOut(function($ip){
echo 'timeout :'.$ip.PHP_EOL;
})
->setCallback(function ($ret,$t){
echo 'ret '.PHP_EOL;
foreach ($ret as $ip=>$r){
echo formet($ip,$r).PHP_EOL;
}
echo 'total time_ms '.$t;
})->doit('127.0.0.1;127.0.0.2;127.0.0.3',1,1000,10);
ret
ip=127.0.0.1 send=2 recive=2 loss=0.00% min=0.26ms max=0.30ms avg=0.28ms
ip=127.0.0.2 send=2 recive=2 loss=0.00% min=0.26ms max=0.26ms avg=0.26ms
ip=127.0.0.3 send=2 recive=2 loss=0.00% min=0.25ms max=0.26ms avg=0.26ms
total time_ms 72.15690612793
项目源码
Xxx-Bin/php-scoket-ping-Ipv4 xfping