玩轉 PHP 網路程式設計全套之 libevent 框架首篇

勺顛顛發表於2020-04-25

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的渣渣

相關文章