Swoole
Swoole
里也提供了一些直接操作底層epoll/kqueue
事件循環的接口,可將其他擴展創建的socket
、PHP代碼中stream/socket
擴展創建的socket
等加入到Swoole的EventLoop
中。
文檔:https://wiki.swoole.com/wiki/page/242.html
這里我也簡單介紹一下。
基本使用
swoole_tcp_server.php
<?php
/**
* Created by PhpStorm.
* User: 公眾號: 飛鴻影的博客(fhyblog)
* Date: 2018/6/30
*/
use Swoole\Event;
$socket = stream_socket_server ("tcp://0.0.0.0:9201", $errno, $errstr);
if (false === $socket ) {
echo "$errstr($errno)\n";
exit();
}
if (!$socket) die($errstr."--".$errno);
// stream_set_blocking($socket,0); //可以去掉,沒有涉及到read這個socket
echo "waiting client...\n";
//accept事件回調函數,參數分別是$fd
function ev_accept($socket){
global $master;
$connection = stream_socket_accept($socket);
//參數的設置將會影響到像 fgets() 和 fread() 這樣的函數從資源流里讀取數據。
//在非阻塞模式下,調用 fgets() 總是會立即返回;而在阻塞模式下,將會一直等到從資源流里面獲取到數據才能返回。
stream_set_blocking($connection, 0);//如果不設置,后續讀取會阻塞
$id = (int)$connection;
echo "new Client $id\n";
Event::add($connection, 'ev_read', null, SWOOLE_EVENT_READ);
}
//read事件回調函數,參數是fd
function ev_read($buffer)
{
$receive = '';
//循環讀取並解析客戶端消息
while( 1 ) {
$read = @fread($buffer, 2);
//客戶端異常斷開
if($read === '' || $read === false){
break;
}
$pos = strpos($read, "\n");
if($pos === false)
{
$receive .= $read;
// echo "received:".$read.";not all package,continue recdiveing\n";
}else{
$receive .= trim(substr ($read,0,$pos+1));
$read = substr($read,$pos+1);
switch ( $receive ){
case "quit":
echo "client close conn\n";
//關閉客戶端連接
Event::del($buffer);//刪除事件
fclose($buffer);//斷開客戶端連接
break;
default:
// echo "all package:\n";
echo $receive."\n";
break;
}
$receive='';
}
}
}
Event::add($socket, 'ev_accept', null, SWOOLE_EVENT_READ);
echo "start run...\n";
//進入事件循環
Event::wait(); //PHP5.4或更高版本不需要加此函數
//下面這句不會被執行
echo "This code will not be executed.\n";
Event::add()
等方法也有對應的函數,例如swoole_event_add()
。
定時器
swoole提供了兩個定時器:
swoole_timer_tick
周期定時器,面向對象寫法:Timer::tick
swoole_timer_after
一次性定時器,面向對象寫法:Timer::after
swoole定時器的精度是毫秒。
示例:
<?php
/**
* Created by PhpStorm.
* User: 公眾號: 飛鴻影的博客(fhyblog)
* Date: 2018/6/30
*/
use Swoole\Timer;
Timer::after(1000, function(){
echo time(). " hello\n";
Timer::tick(1000, function($timer_id, $params){
static $c = 0;
echo time(). " hello $params $c\n";
$c++;
if($c > 5){
Timer::clear($timer_id);
}
}, 'this is param');
});
說明:Timer::after
回調函數沒有參數,Timer::tick
回調函數參數是定時器ID及附加參數。
總結
我曾經查閱了Workerman
的源碼,看到作者把常見的Event(系統自帶Select、libevet、Event、Ev、Swoole)進行了一次封裝,對外暴露了統一的接口(add、del、loop),大家有興趣可以看看。
參考
1、php libevent擴展的簡單用例 - NickBai - 博客園
https://www.cnblogs.com/nickbai/articles/6197689.html
2、PHP使用pcntl和libevent 實現Timer功能 | 博客水木
http://www.4u4v.net/php-uses-pcntl-and-libevent-achieve-timer-function.html
3、socket服務的模型以及實現(4)–單進程IO復用libevent
http://www.xtgxiso.com/socket服務的模型以及實現4-單進程io復用libevent/
4、libevent中的bufferevent原理 - nengm - 博客園
https://www.cnblogs.com/nengm1988/p/8203784.html
5、EventLoop-Swoole-Swoole文檔中心
https://wiki.swoole.com/wiki/page/242.html