玩转 PHP 网络编程全套之 libevent 框架首篇

LIBEVENT框架

此框架的扩展是LIBEVENT,php手册地址libevent,该框架了封装I/O事件,定时事件,中断信号事件,内核I/O复用函数支持EPOLL,POLL,SELECT,DEVPOLL,KQUEUE。框架官方网站libvent官网以下项目使用了该框架

玩转 PHP 网络编程全套之 libevent 框架

框架涉及到的知识点说明【非常重要,否则可能会复制粘贴跑起来了,但是相关知识点并没有完全的理解,更谈不上熟悉php撸的workerman框架了^_^】

  • TCP/IP
  • thread 线程
  • I/O复用
  • 事件处理模式
    • reactor 模式
    • Proactor 模式
  • 并发模式
    • 半同步/半异步模式
  • 定时器
  • 中断信号
  • I/O事件
  • 事件多路分发器EventDeumultiplexer
  • 事件处理器EventHandler
  • 低层知识
    • 网卡驱动
    • ARP协议【mac硬件物理地址交换】
    • 网络数据帧
  • 同步/异步线程

本人注解的网络框架libevent源码内核原理分析
相关测试源码和分析流程以及笔记可联系本人获取

源码框架安装说明

php libevent扩展安装地址

玩转 PHP 网络编程全套之 libevent 框架

如果认真看过PHP手册的人安装php扩展是非常容易的.
本人安装的扩展是event2.2.1版本
玩转 PHP 网络编程全套之 libevent 框架

运行个示例玩先

<?php

class MyListenerConnection {
    private $bev, $base;

    public function __destruct() {
        //将读写和异常回调清空同时释放BufferEvent相关内置的数据
        $this->bev->free();
    }
    public function __construct($base, $fd) {
        $this->base = $base;

        //创建BufferEvent对象
        //此对象内置了读写事件处理器,但并没有添加到I/O事件池中
        //同时该对象分别创建input/outpu对象【内置创建】主要用于数据读写【接收和发送】
        $this->bev = new EventBufferEvent($base, $fd, EventBufferEvent::OPT_CLOSE_ON_FREE);

        //设置读写异常回调函数 【写回调并未设置】
        $this->bev->setCallbacks(array($this, "echoReadCallback"), NULL,
            array($this, "echoEventCallback"), NULL);

        //将内置的写事件处理器添加到I/O事件池中,并且向内核事件表注册读就绪事件
        if (!$this->bev->enable(Event::READ)) {
            echo "Failed to enable READ\n";
            return;
        }
    }
    public function echoReadCallback($bev, $ctx) {
        //读就绪事件发生后,内置的读事件处理器运行,然后运行此函数
        //同时调用output,并把input【内置的读事件处理器读取的数据会放入到此input对象中】
        //直接将接受的数据写入到客户端
        $bev->output->addBuffer($bev->input);

    }
    public function echoEventCallback($bev, $events, $ctx) {
        //异常回调
        if ($events & EventBufferEvent::ERROR) {
            echo "Error from bufferevent\n";
        }

        if ($events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
            //$bev->free();
            $this->__destruct();
        }
    }
}

class MyListener {
    public $base, $listener, $socket;
    private $conn = array();

    public function __destruct() {
        foreach ($this->conn as &$c) $c = NULL;
    }

    public function __construct($port) {
        //创建event_base对象
        //内置了I/O事件处理器池和信号事件处理器池
        //同时也内置的定时时间堆
        $this->base = new EventBase();
        if (!$this->base) {
            echo "Couldn't open event base";
            exit(1);
        }

        //创建socket 并监听同时将此socket的读就绪事件注册到【经过I/O复用函数即事件多路分发器EventDemultiplexer管理】
        //此socket 内置了监听事件处理器,客户端连接后,会调用此事件处理器,然后再运行用户设置的回调函数acceptConnCallBack函数
        //EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE 标志位
        //EventListener::OPT_CLOSE_ON_FREE 此参数会关闭低层连接socket
        //EventListener::OPT_REUSEABLE 和前面说过的socket 选项有关【不清楚请翻阅之前我写过的东西】
        //后面2个参数为ip和端口用于生成socket
        $this->listener = new EventListener($this->base,
            array($this, "acceptConnCallback"), $this->base,
            EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE, -1,
            "0.0.0.0:$port");

        if (!$this->listener) {
            echo "Couldn't create listener";
            exit(1);
        }

        //设置此socket事件处理器的错误回调
        $this->listener->setErrorCallback(array($this, "accept_error_cb"));
    }

    public function dispatch() {
        //内置了event_base_loop进行循环处理
        //主要是调用如epoll的epoll_wait函数进行监听
        //当任意I/O产生了就绪事件则会通知此进程
        //此进程将会遍历就绪的I/O事件读取文件描述符
        //并从I/O事件处理器池读取对应的事件处理器队链
        //再将事件处理器插入到请求队列中
        //两从请求队列中获取到事件并循环一一处理
        //从而运行指定的回调函数
        $this->base->dispatch();
    }

    /**
     * @param $listener 上面的监听器
     * @param $fd 产生就绪事件的文件描述符
     * @param $address 客户端地址
     * @param $ctx 用户自定义传递的参数
     */
    public function acceptConnCallback($listener, $fd, $address, $ctx) {

        $base = $this->base;
        $this->conn[] = new MyListenerConnection($base, $fd);
    }

    public function accept_error_cb($listener, $ctx) {
        $base = $this->base;

        fprintf(STDERR, "Got an error %d (%s) on the listener. "
            ."Shutting down.\n",
            EventUtil::getLastSocketErrno(),
            EventUtil::getLastSocketError());

        $base->exit(NULL);
    }
}

$port = 12345;

if ($argc > 1) {
    $port = (int) $argv[1];
}
if ($port <= 0 || $port > 65535) {
    exit("Invalid port");
}

$l = new MyListener($port);
//event_base_loop持续阻塞
//直到内核事件表中的I/O事件就绪产生才会运行相就的回调函数
$l->dispatch();

玩转 PHP 网络编程全套之 libevent 框架

框架内部用到的数据结构和PHP关联的对象

new EventBase() 对应c内部的event_base结构体  
new EventListener 对应内部的evconnlistener结构体  
new EventBufferEvent 对应内部的bufferevent结构体  
更多相关的内容请阅读本人注解的内核libevent框架,不然你可能对这些知识点感到烧脑子  
本作品采用《CC 协议》,转载必须注明作者和本文链接

只会php crud的渣渣

勺颠颠
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 9

老哥稳,给大佬递黄鹤楼。就是代码乱码排版乱掉了~

2个月前 评论
勺颠颠

@晓鹤 兄弟我草稿里排版的时候是好的,放草稿里好长时间了,今天一下子把草稿全放了就变成这样了,我也不知道啊

2个月前 评论

PHP 界的 APUE, 向大佬看齐,有时间一起泡脚。 :joy:

2个月前 评论
勺颠颠

@晓鹤 没有没有,我只是渣渣,有空一起吃饭 :kissing_heart:

2个月前 评论

你到底存了多少货!

2个月前 评论
勺颠颠

@Remember 没有没有,就是无聊瞎乱写的,没有货,真的 :joy:

2个月前 评论
勺颠颠

@晓鹤

file
一发就乱了,这个真不能怪我

2个月前 评论
Summer

编辑器有个 Bug,已修复

2个月前 评论
勺颠颠

@Summer 刚开始我还以是我的问题,好多人说我排版乱了 :joy:

2个月前 评论

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!