Event可以认为是替代libevent最好的扩展,因为libevent已经很久不更新了,而Event一直在更新,而且Event支持更多特性,使用起来也比libevent简单。

Event地址: http://pecl.php.net/package/event
Event文档: http://docs.php.net/event

和libevent一样,系统需要先安装 Libevent 库,因为都是基于 Libevent 库开发的:

  1. yum install libevent-dev

然后安装PHP扩展。

PHP7安装:

  1. pecl install event

Event扩展不支持PHP5。

注:后面的代码示例均使用的php7.1 + event环境。

我们把libevent_tcp_server.php的例子改为Event实现的:

event_tcp_server.php

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: 公众号: 飞鸿影的博客(fhyblog)
  5. * Date: 2018/6/23
  6. */
  7. $receive = [];
  8. $master = [];
  9. $buffers = [];
  10. $socket = stream_socket_server ("tcp://0.0.0.0:9201", $errno, $errstr);
  11. if (false === $socket ) {
  12. echo "$errstr($errno)\n";
  13. exit();
  14. }
  15. if (!$socket) die($errstr."--".$errno);
  16. //stream_set_blocking($socket,0); //可选
  17. $id = (int)$socket;
  18. $master[$id] = $socket;
  19. echo "waiting client...\n";
  20. //accept事件回调函数,参数分别是$fd, $events, $arg
  21. function ev_accept($socket, $flag, $base){
  22. global $receive;
  23. global $master;
  24. global $buffers;
  25. $connection = stream_socket_accept($socket);
  26. stream_set_blocking($connection, 0);//必须
  27. $id = (int)$connection;
  28. echo "new Client $id\n";
  29. $event = new Event($base, $connection, Event::READ | Event::PERSIST, 'ev_read', $id);
  30. $event->add();
  31. $master[$id] = $connection; //根据业务可选
  32. $receive[$id] = ''; //根据业务可选
  33. $buffers[$id] = $event; //根据业务可选
  34. }
  35. //read事件回调函数
  36. function ev_read($buffer, $flag, $id)
  37. {
  38. global $receive;
  39. global $master;
  40. global $buffers;
  41. //该方法里的$buffer和$master[$id]指向相同的内容
  42. // var_dump(func_get_args(), $master[$id] );
  43. //循环读取并解析客户端消息
  44. while( 1 ) {
  45. $read = @fread($buffer, 1024);
  46. //客户端异常断开
  47. if($read === '' || $read === false){
  48. break;
  49. }
  50. $pos = strpos($read, "\n");
  51. if($pos === false)
  52. {
  53. $receive[$id] .= $read;
  54. // echo "received:".$read.";not all package,continue recdiveing\n";
  55. }else{
  56. $receive[$id] .= trim(substr ($read,0,$pos+1));
  57. $read = substr($read,$pos+1);
  58. switch ( $receive[$id] ){
  59. case "quit":
  60. echo "client close conn\n";
  61. //关闭客户端连接
  62. unset($master[$id]);//断开客户端连接
  63. unset($buffers[$id]);//删除事件
  64. break;
  65. default:
  66. // echo "all package:\n";
  67. echo $receive[$id]."\n";
  68. break;
  69. }
  70. $receive[$id]='';
  71. }
  72. }
  73. }
  74. //创建全局event base
  75. $base = new EventBase();
  76. //创建并设置 event:其中$events设置为READ | PERSIST ;回调事件为ev_accept,参数 $base
  77. //PERSIST可以让注册的事件在执行完后不被删除,直到调用Event::del()删除.
  78. $event = new Event($base, $socket, Event::READ | Event::PERSIST, 'ev_accept', $base);
  79. $event->add();
  80. echo "start run...\n";
  81. //进入事件循环
  82. $base->loop();
  83. //下面这句不会被执行
  84. echo "This code will not be executed.\n";

可以发现做的改动非常小,而且代码更简洁了。运行脚本后,我们使用telnet测试,效果一模一样。

直接看例子吧,还是基于上面的例子改的,注释里写得很清楚了:

event_buffer_tcp_server.php

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: 公众号: 飞鸿影的博客(fhyblog)
  5. * Date: 2018/6/23
  6. */
  7. $receive = [];
  8. $master = [];
  9. $buffers = [];
  10. $socket = stream_socket_server ("tcp://0.0.0.0:9201", $errno, $errstr);
  11. if (false === $socket ) {
  12. echo "$errstr($errno)\n";
  13. exit();
  14. }
  15. if (!$socket) die($errstr."--".$errno);
  16. //stream_set_blocking($socket,0);//可选
  17. $id = (int)$socket;
  18. $master[$id] = $socket;
  19. echo "waiting client...\n";
  20. //accept事件回调函数,参数分别是$fd, $events, $arg
  21. function ev_accept($socket, $flag, $base){
  22. global $receive;
  23. global $master;
  24. global $buffers;
  25. $connection = stream_socket_accept($socket);
  26. //stream_set_blocking($connection, 0);//可选
  27. $id = (int)$connection;
  28. echo "new Client $id\n";
  29. //新建EventBuffer 事件
  30. $event = new EventBufferEvent($base, $connection, 0, 'ev_read', 'ev_write', 'ev_status', $id);
  31. $event->setTimeouts(30, 30); //read and write timeout
  32. $event->setWatermark ( Event::READ, 0, 0xffffff ); //Adjusts read and/or write watermarks
  33. $event->setPriority(10);
  34. $event->enable(Event::READ | Event::PERSIST);
  35. $master[$id] = $connection; //如果去掉该行,客户端直接被断开
  36. $receive[$id] = ''; //如果去掉该行,服务端无法正常收到消息
  37. $buffers[$id] = $event; //如果去掉该行,客户端强制断开再连接,服务端无法正常收到消息
  38. }
  39. //read事件回调函数,参数分别是EventBufferEvent,arg
  40. function ev_read($buffer, $id)
  41. {
  42. global $receive;
  43. global $master;
  44. global $buffers;
  45. //该方法里的$buffer和$buffers[$id]指向相同的内容
  46. // var_dump(func_get_args(), $buffers[$id], $master[$id]);
  47. //循环读取并解析客户端消息
  48. while( 1 ) {
  49. $read = $buffer->read(65535);
  50. // var_dump($read);
  51. //客户端异常断开;这里可能返回NULL
  52. if($read === '' || $read === false || $read === NULL){
  53. break;
  54. }
  55. $pos = strpos($read, "\n");
  56. if($pos === false)
  57. {
  58. $receive[$id] .= $read;
  59. echo "received:".$read.";not all package,continue recdiveing\n";
  60. }else{
  61. $receive[$id] .= trim(substr ($read,0,$pos+1));
  62. $read = substr($read,$pos+1);
  63. switch ( $receive[$id] ){
  64. case "quit":
  65. echo "client close conn\n";
  66. //关闭客户端连接
  67. unset($master[$id]);//断开客户端连接
  68. unset($buffers[$id]);//删除事件
  69. break;
  70. default:
  71. // echo "all package:\n";
  72. echo $receive[$id]."\n";
  73. break;
  74. }
  75. $receive[$id]='';
  76. }
  77. }
  78. }
  79. function ev_write($buffer, $id)
  80. {
  81. echo "$id -- " ."\n";
  82. }
  83. function ev_status($buffer, $events, $id)
  84. {
  85. echo "ev_status - ".$events."\n";
  86. }
  87. //创建全局event base
  88. $base = new EventBase();
  89. //创建并设置 event:其中$events设置为READ | PERSIST ;回调事件为ev_accept,参数 $base
  90. //PERSIST可以让注册的事件在执行完后不被删除,直到调用Event::del()删除.
  91. $event = new Event($base, $socket, Event::READ | Event::PERSIST, 'ev_accept', $base);
  92. $event->add();
  93. echo "start run...\n";
  94. //进入事件循环
  95. $base->loop();
  96. //下面这句不会被执行
  97. echo "This code will not be executed.\n";

直接看示例:
event_timer.php

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: 公众号: 飞鸿影的博客(fhyblog)
  5. * Date: 2018/6/23
  6. */
  7. $base = new EventBase ();
  8. $n = 2 ; //sec
  9. //初始化定时器
  10. $e = Event :: timer ( $base , function( $arg ) use (& $e ) {
  11. echo " $arg seconds elapsed\n" ;
  12. $e -> delTimer ();
  13. }, $n );
  14. //添加定时器
  15. $e -> addTimer ( $n ); //sec
  16. $base -> loop ();

运行:

  1. $ php event_timer.php
  2. 2 seconds elapsed

libevent扩展一样,Event::timer也是对Event的封装:

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: 公众号: 飞鸿影的博客(fhyblog)
  5. * Date: 2018/6/23
  6. */
  7. $base = new EventBase ();
  8. $n = 2 ; //sec
  9. //初始化定时器
  10. $event = new Event($base, null, Event::TIMEOUT, 'ev_timer', $n );
  11. $event->add($n);//sec
  12. function ev_timer($fd, $what, $arg){
  13. echo " $arg seconds elapsed\n" ;
  14. global $event;
  15. $event->del();
  16. }
  17. $base->loop();

Event提供的定时器精度是秒。

Event 扩展提供了信号(Signal)操作的函数。

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: 公众号: 飞鸿影的博客(fhyblog)
  5. * Date: 2018/6/23
  6. */
  7. $base = new EventBase ();
  8. //初始化信号事件
  9. $e = Event :: signal ( $base , SIGUSR1, function( $signum , $arg ) use (& $e ) {
  10. echo " Caught signal $signum\n" ;
  11. $e->delSignal(); //移除信号
  12. }, '');
  13. //安装信号
  14. $e -> addSignal (); //sec
  15. //发送信号
  16. posix_kill(posix_getpid (), SIGUSR1);
  17. $base -> loop ();

相比pcntl_signalEvent :: signal 高效很多。

Libevent 非常强大,Event实现了其很多的接口供PHP调用,我这里仅是使用了常用的几个特性。由于Event能参考的资料实在是有限,这章写起来也相对难一些,例子里还是留了一些待再次理解。

版权声明:本文为52fhy原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/52fhy/p/9258040.html