Event 扩展
Event 可以认为是替代 libevent 最好的扩展, 因为 libevent 已经很久不更新了, 而 Event 一直在更新, 而且 Event 支持更多特性, 使用起来也比 libevent 简单.
Event 地址: http://pecl.php.net/package/event
Event 文档: http://docs.php.net/event
和 libevent 一样, 系统需要先安装 Libevent 库, 因为都是基于 Libevent 库开发的:
yum install libevent-dev
然后安装 PHP 扩展.
php7 安装:
pecl install event
Event 扩展不支持 PHP5.
注: 后面的代码示例均使用的 php7.1 + event 环境.
基本使用
我们把
libevent_tcp_server.php
的例子改为 Event 实现的:
- event_tcp_server.php
- <?php
- /**
- * Created by PhpStorm.
- * User: 公众号: 飞鸿影的博客(fhyblog)
- * Date: 2018/6/23
- */
- $receive = [];
- $master = [];
- $buffers = [];
- $socket = stream_socket_server ("tcp://0.0.0.0:9201", $errno, $errstr);
- if (false === $socket ) {
- echo "$errstr($errno)\n";
- exit();
- }
- if (!$socket) die($errstr."--".$errno);
- //stream_set_blocking($socket,0); // 可选
- $id = (int)$socket;
- $master[$id] = $socket;
- echo "waiting client...\n";
- //accept 事件回调函数, 参数分别是 $fd, $events, $arg
- function ev_accept($socket, $flag, $base){
- global $receive;
- global $master;
- global $buffers;
- $connection = stream_socket_accept($socket);
- stream_set_blocking($connection, 0);// 必须
- $id = (int)$connection;
- echo "new Client $id\n";
- $event = new Event($base, $connection, Event::READ | Event::PERSIST, 'ev_read', $id);
- $event->add();
- $master[$id] = $connection; // 根据业务可选
- $receive[$id] = ''; // 根据业务可选
- $buffers[$id] = $event; // 根据业务可选
- }
- //read 事件回调函数
- function ev_read($buffer, $flag, $id)
- {
- global $receive;
- global $master;
- global $buffers;
- // 该方法里的 $buffer 和 $master[$id]指向相同的内容
- // var_dump(func_get_args(), $master[$id] );
- // 循环读取并解析客户端消息
- while( 1 ) {
- $read = @fread($buffer, 1024);
- // 客户端异常断开
- if($read === '' || $read === false){
- break;
- }
- $pos = strpos($read, "\n");
- if($pos === false)
- {
- $receive[$id] .= $read;
- // echo "received:".$read.";not all package,continue recdiveing\n";
- }else{
- $receive[$id] .= trim(substr ($read,0,$pos+1));
- $read = substr($read,$pos+1);
- switch ( $receive[$id] ){
- case "quit":
- echo "client close conn\n";
- // 关闭客户端连接
- unset($master[$id]);// 断开客户端连接
- unset($buffers[$id]);// 删除事件
- break;
- default:
- // echo "all package:\n";
- echo $receive[$id]."\n";
- break;
- }
- $receive[$id]='';
- }
- }
- }
- // 创建全局 event base
- $base = new EventBase();
- // 创建并设置 event: 其中 $events 设置为 READ | PERSIST ; 回调事件为 ev_accept, 参数 $base
- //PERSIST 可以让注册的事件在执行完后不被删除, 直到调用 Event::del()删除.
- $event = new Event($base, $socket, Event::READ | Event::PERSIST, 'ev_accept', $base);
- $event->add();
- echo "start run...\n";
- // 进入事件循环
- $base->loop();
- // 下面这句不会被执行
- echo "This code will not be executed.\n";
可以发现做的改动非常小, 而且代码更简洁了. 运行脚本后, 我们使用 telnet 测试, 效果一模一样.
使用 Buffer
直接看例子吧, 还是基于上面的例子改的, 注释里写得很清楚了:
- event_buffer_tcp_server.php
- <?php
- /**
- * Created by PhpStorm.
- * User: 公众号: 飞鸿影的博客(fhyblog)
- * Date: 2018/6/23
- */
- $receive = [];
- $master = [];
- $buffers = [];
- $socket = stream_socket_server ("tcp://0.0.0.0:9201", $errno, $errstr);
- if (false === $socket ) {
- echo "$errstr($errno)\n";
- exit();
- }
- if (!$socket) die($errstr."--".$errno);
- //stream_set_blocking($socket,0);// 可选
- $id = (int)$socket;
- $master[$id] = $socket;
- echo "waiting client...\n";
- //accept 事件回调函数, 参数分别是 $fd, $events, $arg
- function ev_accept($socket, $flag, $base){
- global $receive;
- global $master;
- global $buffers;
- $connection = stream_socket_accept($socket);
- //stream_set_blocking($connection, 0);// 可选
- $id = (int)$connection;
- echo "new Client $id\n";
- // 新建 EventBuffer 事件
- $event = new EventBufferEvent($base, $connection, 0, 'ev_read', 'ev_write', 'ev_status', $id);
- $event->setTimeouts(30, 30); //read and write timeout
- $event->setWatermark ( Event::READ, 0, 0xffffff ); //Adjusts read and/or write watermarks
- $event->setPriority(10);
- $event->enable(Event::READ | Event::PERSIST);
- $master[$id] = $connection; // 如果去掉该行, 客户端直接被断开
- $receive[$id] = ''; // 如果去掉该行, 服务端无法正常收到消息
- $buffers[$id] = $event; // 如果去掉该行, 客户端强制断开再连接, 服务端无法正常收到消息
- }
- //read 事件回调函数, 参数分别是 EventBufferEvent,arg
- function ev_read($buffer, $id)
- {
- global $receive;
- global $master;
- global $buffers;
- // 该方法里的 $buffer 和 $buffers[$id]指向相同的内容
- // var_dump(func_get_args(), $buffers[$id], $master[$id]);
- // 循环读取并解析客户端消息
- while( 1 ) {
- $read = $buffer->read(65535);
- // var_dump($read);
- // 客户端异常断开; 这里可能返回 NULL
- if($read === '' || $read === false || $read === NULL){
- break;
- }
- $pos = strpos($read, "\n");
- if($pos === false)
- {
- $receive[$id] .= $read;
- echo "received:".$read.";not all package,continue recdiveing\n";
- }else{
- $receive[$id] .= trim(substr ($read,0,$pos+1));
- $read = substr($read,$pos+1);
- switch ( $receive[$id] ){
- case "quit":
- echo "client close conn\n";
- // 关闭客户端连接
- unset($master[$id]);// 断开客户端连接
- unset($buffers[$id]);// 删除事件
- break;
- default:
- // echo "all package:\n";
- echo $receive[$id]."\n";
- break;
- }
- $receive[$id]='';
- }
- }
- }
- function ev_write($buffer, $id)
- {
- echo "$id --" ."\n";
- }
- function ev_status($buffer, $events, $id)
- {
- echo "ev_status -".$events."\n";
- }
- // 创建全局 event base
- $base = new EventBase();
- // 创建并设置 event: 其中 $events 设置为 READ | PERSIST ; 回调事件为 ev_accept, 参数 $base
- //PERSIST 可以让注册的事件在执行完后不被删除, 直到调用 Event::del()删除.
- $event = new Event($base, $socket, Event::READ | Event::PERSIST, 'ev_accept', $base);
- $event->add();
- echo "start run...\n";
- // 进入事件循环
- $base->loop();
- // 下面这句不会被执行
- echo "This code will not be executed.\n";
定时器(Timer)
直接看示例:
- event_timer.php
- <?php
- /**
- * Created by PhpStorm.
- * User: 公众号: 飞鸿影的博客(fhyblog)
- * Date: 2018/6/23
- */
- $base = new EventBase ();
- $n = 2 ; //sec
- // 初始化定时器
- $e = Event :: timer ( $base , function( $arg ) use (& $e ) {
- echo "$arg seconds elapsed\n" ;
- $e -> delTimer ();
- }, $n );
- // 添加定时器
- $e -> addTimer ( $n ); //sec
- $base -> loop ();
运行:
- $ php event_timer.php
- 2 seconds elapsed
和 libevent 扩展一样, Event::timer 也是对 Event 的封装:
- <?php
- /**
- * Created by PhpStorm.
- * User: 公众号: 飞鸿影的博客(fhyblog)
- * Date: 2018/6/23
- */
- $base = new EventBase ();
- $n = 2 ; //sec
- // 初始化定时器
- $event = new Event($base, null, Event::TIMEOUT, 'ev_timer', $n );
- $event->add($n);//sec
- function ev_timer($fd, $what, $arg){
- echo "$arg seconds elapsed\n" ;
- global $event;
- $event->del();
- }
- $base->loop();
Event 提供的定时器精度是秒.
信号(Signal)
Event 扩展提供了信号 (Signal) 操作的函数.
- <?php
- /**
- * Created by PhpStorm.
- * User: 公众号: 飞鸿影的博客(fhyblog)
- * Date: 2018/6/23
- */
- $base = new EventBase ();
- // 初始化信号事件
- $e = Event :: signal ( $base , SIGUSR1, function( $signum , $arg ) use (& $e ) {
- echo "Caught signal $signum\n" ;
- $e->delSignal(); // 移除信号
- }, '');
- // 安装信号
- $e -> addSignal (); //sec
- // 发送信号
- posix_kill(posix_getpid (), SIGUSR1);
- $base -> loop ();
相比 pcntl_signal,Event :: signal 高效很多.
总结
Libevent 非常强大, Event 实现了其很多的接口供 PHP 调用, 我这里仅是使用了常用的几个特性. 由于 Event 能参考的资料实在是有限, 这章写起来也相对难一些, 例子里还是留了一些待再次理解.
来源: https://www.cnblogs.com/52fhy/p/9258040.html