专栏首页PHP饭米粒玩转 PHP 网络编程全套之 libevent 框架首篇

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

LIBEVENT框架

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

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

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

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

源码框架安装说明

php libevent扩展安装地址

如果认真看过PHP手册的人安装php扩展是非常容易的. 本人安装的扩展是event2.2.1版本

先运行个示例玩

<?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关联的对象

new EventBase() 对应c内部的event_base结构体
new EventListener 对应内部的evconnlistener结构体
new EventBufferEvent 对应内部的bufferevent结构体
更多相关的内容请阅读本人注解的内核libevent框架,不然你可能对这些知识点感到烧脑子

本文分享自微信公众号 - PHP饭米粒(phpfamily)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Golang语言社区--手游服务器开发技术详解

    大家好,我是Golang语言社区(www.golang.ltd)主编彬哥,本篇给大家带来一篇关注手机游戏开发相关的文章。

    李海彬
  • 用PHP实现高并发服务器

    一提到高并发,就没有办法绕开I/O复用,再具体到特定的平台linux, 就没办法绕开epoll. epoll为啥高效的原理就不讲了,感兴趣的同学可以自行搜索研究...

    猿哥
  • PHP网络编程之深入Libevent(十五节)

    大家周末好,这里有趣有用广告少的公众号高性能API社区,我是老李,本文属于《PHP网络编程》系列中的一个章节。

    老李秀
  • PHP网络编程之epoll开启篇

    这个公众号自从去年6月份到现在已经半年了,将近80篇的原创大概换来了550元的广告费。这不是我一个人的钱(主要是大家浏览量带来的支持),再加上我们最近遇到的事情...

    老李秀
  • 【专业技术】如何搭建游戏服务器?

    存在问题: 手游越来越火了,听听业内人士的分析,他山之石,多多借鉴,那么手游的服务器到底如何搭建的? 解决方案: 从事游戏服务器开发差不多两年时间,两年间参与了...

    程序员互动联盟
  • 【谁会是下一个王者农药】云服务器如何搭建游戏服务器?

    https://cloud.tencent.com/redirect.php?redirect=1014&cps_key=6f5f5aedea72d213ca3...

    勤劳的小蜜蜂
  • 玩转 PHP 网络编程全套之数据接收与发送

    它是一个复杂的协议族,但是经过层层封装之后转换为网络数据帧经过网卡发送出去的,当然在发送之前会先发起一次ARP请求查询一下对方的mac物理地址,对方响应后返回以...

    桶哥
  • 搭建你的物联网 : Workerman Tcp 服务器

    想不想让家里的设备联网?想不想远程控制家里的空调?单身狗想不想一回家就吃到热腾腾的饭菜?除了购买现成的产品,你还可以这样做哦!

    Techeek
  • 玩转 PHP 网络编程全套阻塞与非阻塞 IO

    上一篇我们撸了不咋样的TCP server,然后还扯了半天的口水,现在呢我们来继续撸客户端

    桶哥

扫码关注云+社区

领取腾讯云代金券