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;
}
