PHP之高性能I/O框架:Libevent(二)
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能参考的资料实在是有限,这章写起来也相对难一些,例子里还是留了一些待再次理解。
(未完待续)
推荐
内容概要:Redis 最为目前炙手可热的 Key-Value 数据库,常用做缓存、Session共享中间件,分布式锁等等。
本系列课程包括:

讲师是CSDN 博客专家,多年 Redis 使用经验。感兴趣的朋友可以点击试看!
PHP之高性能I/O框架:Libevent(二)的更多相关文章
- PHP之高性能I/O框架:Libevent(一)
Libevent 是一个用C语言编写的.轻量级的开源高性能I/O框架,支持多种 I/O 多路复用技术: epoll. poll. dev/poll. select 和 kqueue 等:支持 I/O, ...
- 【原创】NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示
前言 NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2.而Netty的主要版本是Netty3和Netty ...
- 高性能分布式执行框架——Ray
Ray是UC Berkeley AMP实验室新推出的高性能分布式执行框架,它使用了和传统分布式计算系统不一样的架构和对分布式计算的抽象方式,具有比Spark更优异的计算性能. Ray目前还处于实验室阶 ...
- 新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析
1.引言 Netty 是一个广受欢迎的异步事件驱动的Java开源网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件 ...
- Netty高性能原理和框架架构解析
1.引言 Netty 是一个广受欢迎的异步事件驱动的Java开源网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件 ...
- 【转】目前为止最透彻的的Netty高性能原理和框架架构解析
转自:https://zhuanlan.zhihu.com/p/48591893 1.引言 Netty 是一个广受欢迎的异步事件驱动的Java开源网络应用程序框架,用于快速开发可维护的高性能协议服务器 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- JavaScript 框架设计(二)
JavaScript 高级框架设计 (二) 上一篇,JavaScript高级框架设计(一)我们 实现了对tag标签的选择 下来我们实现对id的选择,即id选择器. 我们将上一篇的get命名为getTa ...
- “Zhuang.Data”轻型数据库访问框架(二)框架的入口DbAccessor对象
目录: “Zhuang.Data”轻型数据库访问框架(一)开篇介绍 “Zhuang.Data”轻型数据库访问框架(二)框架的入口DbAccessor对象 先来看一段代码 DbAccessor dba ...
- 开源软件实践之linux高性能服务器编程框架和选型
很多人学习编程技术一般都通过一本编程语言的入门书籍,然后尝试做一些例子和小项目.但是这些都不能让我们深入的学习很多的编程技巧和高深技术,当然这个时候很多有经验的学习人员就会告诉大家,找一个好的开源软件 ...
随机推荐
- 从晶体管到web浏览器
几年前写了这个,想放到网上,招聘很困难,很多现代计算机科学教育真的很糟,很难找到懂得现代计算机堆栈的第一(基本)原理的人. 如果我曾经再次得到了12周的自由,我会提供这个作为课余学习的play,我也想 ...
- day18(javaEE三大组件之一servlet(简介(一)))
Servlet servlet是小型服务器语言,使用它可以处理前台传递来的信息,servlet进行处理后在响应给前台,其中servlet起到了关键性的作用.前端输入的信息可以持久化的存储在数据库中,并 ...
- (动态规划)Worm -- hdu -- 2151
http://acm.hdu.edu.cn/showproblem.php?pid=2151 Worm Time Limit: 1000/1000 MS (Java/Others) Memory ...
- java实现在图片上编辑文本内容
package com.yin.text; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; impor ...
- 20155326 2017-2018-1 《信息安全系统设计基础》第2周学习及课堂总结myod
20155326 2017-2018-1 <信息安全系统设计基础>第1次学习及课堂总结myod 虚拟机之前出了一些问题,然后我重新弄了一个新的虚拟机. 先在虚拟机里面安装了git. 安完以 ...
- SSM_CRUD新手练习(4)修改生成的mapper.xml映射文件
我们为什么要修改呢,这是因为我们查询的时候,我们有时候需要连表查询,例如我们需要查询出员工表的信息(emp_id,emp_name...)与此同时,我们还想查询出该员工所在的部门(dept_name) ...
- include require include_once require_once
include 使用方法: include "test.php"; 一般是放在流程控制的处理部分中使用,将文件内容引入.PHP程序在遇到include语句时,才将它读进来,这种方式 ...
- poj 3258 3273
poj3258 题目 (最大化最小值)(最小值最大化) 题意:牛要到河对岸,在与河岸垂直的一条线上,河中有N块石头,给定河岸宽度L,以及每一块石头离牛所在河岸的距离,现在去掉M块石头,要求去掉M块石 ...
- bootstrap 警告框单个删除
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...
- Stringbuffer与substring
1. Stringbuffer 有append()方法 Stringbuffer 其实是动态字符串数组 append()是往动态字符串数组添加,跟“xxxx”+“yyyy”相当那个‘+’号 跟Stri ...