php libevent擴展具有很強大的功能。以下摘自百度百科:
Libevent 是一個用C語言編寫的、輕量級的開源高性能網絡庫,主要有以下幾個亮點:事件驅動( event-driven),高性能;輕量級,專注於網絡,不如 ACE 那么臃腫龐大;
源代碼相當精煉、易讀;跨平台,支持 Windows、 Linux、 *BSD 和 Mac Os;支持多種 I/O 多路復用技術, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,
定時器和信號等事件;注冊事件優先級。
在php 的libevent擴展具有如下函數:
event_base_free() 釋放資源,這不能銷毀綁定事件
event_base_loop() 處理事件,根據指定的base來處理事件循環
event_base_loopbreak() 立即取消事件循環,行為和break語句相同
event_base_loopexit() 在指定的時間后退出循環
event_base_new() 創建並且初始事件
event_base_priority_init() 設定事件的優先級
event_base_set() 關聯事件到事件base
event_buffer_base_set() 關聯緩存的事件到event_base
event_buffer_disable() 禁用一個緩存的事件
event_buffer_enable() 啟用一個指定的緩存的事件
event_buffer_fd_set() 改變一個緩存的文件系統描述
event_buffer_free() 釋放緩存事件
event_buffer_new() 建立一個新的緩存事件
event_buffer_priority_set() 緩存事件的優先級設定
event_buffer_read() 讀取緩存事件中的數據
event_buffer_set_callback() 給緩存的事件設置或重置回調hansh函數
event_buffer_timeout_set() 給一個緩存的事件設定超時的讀寫時間
event_buffer_watermark_set 設置讀寫事件的水印標記
event_buffer_write() 向緩存事件中寫入數據
event_add() 向指定的設置中添加一個執行事件
event_del() 從設置的事件中移除事件
event_free() 清空事件句柄
event_new() 創建一個新的事件
event_set() 准備想要在event_add中添加事件
event_set一些參數的解釋:
(a) EV_TIMEOUT: 超時
(b) EV_READ: 只要網絡緩沖中還有數據,回調函數就會被觸發
(c) EV_WRITE: 只要塞給網絡緩沖的數據被寫完,回調函數就會被觸發
(d) EV_SIGNAL: POSIX信號量
(e) EV_PERSIST: 不指定這個屬性的話,回調函數被觸發后事件會被刪除
(f) EV_ET: Edge-Trigger邊緣觸發
下面看一下一個簡單的用例:
<?php $socket = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr); $base = event_base_new(); $event = event_new(); function read_cb($socket, $flag, $base) { fread($socket); fwrite("hello world\n"); } function accept_cb($socket, $flag, $base) { $conn = stream_socket_accept($socket, 0); stream_set_blocking($conn, 0); $event = event_new(); event_set($event, $conn, EV_READ | EV_PERSIST, 'read_cb', $base); event_base_set($event, $base); event_add($event); } event_set($event, $socket, EV_READ | EV_PERSIST, 'accept_cb', $base); event_base_set($event, $base); event_add($event); event_base_loop($base);
這一段是摘抄自 韓天峰 的一個PPT上的簡單用例,我來給大家解釋一下:
首先創建一個 tcp服務,綁定 8000端口。創建一個 event_base,然后創建一個 event,通過 event_set設置 讓event監聽新創建的socket,並給這個event事件設置屬性,可以看到其中指定了回調函數 accept_cb
然后將這個event與base進行綁定,並加入到監聽事件中取,啟動事件循環。
可以發現 accept_cb做了接受客戶端鏈接,並且又創建了一個event事件,做了跟之前一樣的事情,並且指定了該事件的回調函數是read_cb。 而read_cb函數 做了讀取和寫入操作。
下面我自己通過整理寫一個客戶端 client.php
<?php /** * author: NickBai * createTime: 2016/12/17 0017 下午 3:00 */ $socket_client = stream_socket_client('tcp://127.0.0.1:2000', $errno, $errstr, 30); fwrite($socket_client, "hello world!"); sleep(1); $return = fread($socket_client, 1024); echo "come from server : " . $return . PHP_EOL; sleep(2); fwrite($socket_client, "send again!"); $return = fread($socket_client, 1024); echo "come from server : " . $return . PHP_EOL;
我重新整理了一個具有實際可操作的 server 服務端代碼如下
server.php
<?php /** * Created by PhpStorm. * User: Administrator * Date: 2016/12/17 * Time: 20:59 */ $socket = stream_socket_server("tcp://0.0.0.0:2000", $errno, $errstr); $base = event_base_new(); $event = event_new(); event_set($event, $socket, EV_READ | EV_PERSIST, 'accept_cb', $base); event_base_set($event, $base); event_add($event); event_base_loop($base); function read_cb($buffer) { static $ct = 0; $ct_last = $ct; $ct_data = ''; while ($read = event_buffer_read($buffer, 1024)) { $ct += strlen($read); $ct_data .= $read; } $ct_size = ($ct - $ct_last) * 8; echo "client say : " . $ct_data .PHP_EOL; event_buffer_write($buffer, "Received $ct_size byte data"); } function write_cb($buffer) { echo "我在打醬油 " . PHP_EOL; } function error_cb($buffer, $error) { // 客戶端斷開連接之后,清除 event_buffer_disable($GLOBALS['buffer'], EV_READ | EV_WRITE); event_buffer_free($GLOBALS['buffer']); fclose($GLOBALS['connection']); unset($GLOBALS['buffer'], $GLOBALS['connection']); } function accept_cb($socket, $flag, $base) { $connection = stream_socket_accept($socket); stream_set_blocking($connection, 0); $buffer = event_buffer_new($connection, 'read_cb', 'write_cb', 'error_cb'); event_buffer_base_set($buffer, $base); event_buffer_timeout_set($buffer, 30, 30); event_buffer_watermark_set($buffer, EV_READ, 0, 0xffffff); event_buffer_priority_set($buffer, 10); event_buffer_enable($buffer, EV_READ | EV_PERSIST); // 必須將 $connection 和 $buffer 賦值給一個全局變量,否則無法生效 $GLOBALS['connection'] = $connection; $GLOBALS['buffer'] = $buffer; }