目錄php
swoole算是nodejs在php中的一種實現,異步響應請求,性能超強html
php-5.3.10 或更高版本
gcc-4.4 或更高版本
make
autoconf
pcre (centos系統能夠執行命令:yum install pcre-devel)node
下載地址 https://github.com/swoole/swoole-src/releases
進入頁面後選擇download連接下的tar.gz的壓縮包
下載源代碼包後,解壓laravel
tar xzvf xxx.tar.gz
在終端進入源碼目錄,執行下面的命令進行編譯和安裝git
cd swoole phpize ./configure --enable-swoole-debug make sudo make install
編譯參數根據本身的需求選擇,詳情參看官方文檔。github
在php.ini中加入 extension=swoole.so
經過在命令行使用 php-m查看,是否安裝了swooleweb
注意:如通從新編譯的話須要 make cleanjson
(來自w3cschool教程https://www.w3cschool.cn/swoole/bnte1qcd.html)centos
服務端代碼:Server.php瀏覽器
<?php // Server class Server { private $serv; public function __construct() { $this->serv = new swoole_server("0.0.0.0", 9501); $this->serv->set(array( 'worker_num' => 8, 'daemonize' => false, 'max_request' => 10000, 'dispatch_mode' => 2, 'debug_mode'=> 1 )); $this->serv->on('Start', array($this, 'onStart')); $this->serv->on('Connect', array($this, 'onConnect')); $this->serv->on('Receive', array($this, 'onReceive')); $this->serv->on('Close', array($this, 'onClose')); $this->serv->start(); } public function onStart( $serv ) { echo "Start\n"; } public function onConnect( $serv, $fd, $from_id ) { $serv->send( $fd, "Hello {$fd}!" ); } public function onReceive( swoole_server $serv, $fd, $from_id, $data ) { echo "Get Message From Client {$fd}:{$data}\n"; } public function onClose( $serv, $fd, $from_id ) { echo "Client {$fd} close connection\n"; } } // 啓動服務器 $server = new Server();
從代碼中能夠看出,建立一個swoole_server基本分三步:
客戶端的代碼:Client.php
<?php class Client { private $client; public function __construct() { $this->client = new swoole_client(SWOOLE_SOCK_TCP); } public function connect() { if( !$this->client->connect("127.0.0.1", 9501 , 1) ) { echo "Error: {$fp->errMsg}[{$fp->errCode}]\n"; } $message = $this->client->recv(); echo "Get Message From Server:{$message}\n"; fwrite(STDOUT, "請輸入消息:"); $msg = trim(fgets(STDIN)); $this->client->send( $msg ); } } $client = new Client(); $client->connect();
這裏,經過swoole_client建立一個基於TCP的客戶端實例,並調用connect函數向指定的IP及端口發起鏈接請求。隨後便可經過recv()和send()兩個函數來接收和發送請求。須要注意的是,這裏我使用了默認的同步阻塞客戶端,所以recv和send操做都會產生網絡阻塞。
使用方法
進入到文件目錄,在窗口1先啓動php Serve.php,而後再開一個窗口(窗口2)啓動php Client.php
窗口1內容:
# root @ WENGINE in /data/learnSwoole [9:24:57] C:130 $ php Server.php Start Get Message From Client 1:ceshi1 Client 1 close connection
窗口2內容:
# root @ WENGINE in /data/learnSwoole [9:23:07] $ php Client.php Get Message From Server:Hello 1! 請輸入消息:ceshi1
服務端代碼 http_server.php
$http = new swoole_http_server("0.0.0.0", 9501); $http->on('request', function ($request, $response) { var_dump($request->get, $request->post); $response->header("Content-Type", "text/html; charset=utf-8"); $response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>"); }); $http->start();
Http服務器只須要關注請求響應便可,因此只須要監聽一個onRequest事件。當有新的Http請求進入就會觸發此事件。事件回調函數有2個參數,一個是$request對象,包含了請求的相關信息,如GET/POST請求的數據。
另一個是response對象,對request的響應能夠經過操做response對象來完成。$response->end()方法表示輸出一段HTML內容,並結束此請求。
● 0.0.0.0 表示監聽全部IP地址,一臺服務器可能同時有多個IP,如127.0.0.1本地迴環IP、192.168.1.100局域網IP、210.127.20.2 外網IP,這裏也能夠單獨指定監聽一個IP
● 9501 監聽的端口,若是被佔用程序會拋出致命錯誤,中斷執行。
服務端程序代碼 ws_server.php
//建立websocket服務器對象,監聽0.0.0.0:9502端口 $ws = new swoole_websocket_server("0.0.0.0", 9502); //監聽WebSocket鏈接打開事件 $ws->on('open', function ($ws, $request) { var_dump($request->fd, $request->get, $request->server); $ws->push($request->fd, "hello, welcome\n"); }); //監聽WebSocket消息事件 $ws->on('message', function ($ws, $frame) { echo "Message: {$frame->data}\n"; $ws->push($frame->fd, "server: {$frame->data}"); }); //監聽WebSocket鏈接關閉事件 $ws->on('close', function ($ws, $fd) { echo "client-{$fd} is closed\n"; }); $ws->start();
WebSocket服務器是創建在Http服務器之上的長鏈接服務器,客戶端首先會發送一個Http的請求與服務器進行握手。握手成功後會觸發onOpen事件,表示鏈接已就緒,onOpen函數中能夠獲得$request對象,包含了Http握手的相關信息,如GET參數、Cookie、Http頭信息等。
創建鏈接後客戶端與服務器端就能夠雙向通訊了。
● 客戶端向服務器端發送信息時,服務器端觸發onMessage事件回調
● 服務器端能夠調用$server->push()向某個客戶端(使用$fd標識符)發送消息
● 服務器端能夠設置onHandShake事件回調來手工處理WebSocket握手
運行程序
客戶端的代碼
可使用Chrome瀏覽器進行測試,JS代碼爲:
var wsServer = 'ws://127.0.0.1:9502'; var websocket = new WebSocket(wsServer); websocket.onopen = function (evt) { console.log("Connected to WebSocket server."); }; websocket.onclose = function (evt) { console.log("Disconnected"); }; websocket.onmessage = function (evt) { console.log('Retrieved data from server: ' + evt.data); }; websocket.onerror = function (evt, e) { console.log('Error occured: ' + evt.data); };
關於onRequest回調
swoole_websocket_server 繼承自 swoole_http_server
能夠建立更多的服務器 參照官方文檔嘗試
https://wiki.swoole.com/wiki/page/475.html
主要思路是使用php artisan 自建命令控制服務端,使用HTML5的websocket實現客戶端功能
服務端:app/Console/Commands/Websocket.php內容
<?php namespace App\Console\Commands; use Illuminate\Console\Command; use swoole_http_request; use swoole_http_response; use swoole_websocket_server; class WebSocket extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'websocket {cmd=start : can use start|stop|status|restart} {--daemon : set to run in daemonize mode} '; /** * The console command description. * * @var string */ protected $description = 'swoole server control'; /** * server * * @var swoole_websocket_server */ private $server; /** * * TYPE_ADMIN * */ const TYPE_ADMIN = 0X00; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * 處理不一樣command信息 * * @return mixed */ public function handle() { $command = $this->argument('cmd'); $option = $this->option('daemon'); switch ($command) { case 'start': $this->initWs($option); break; case 'stop': $res = $this->sendAdminRequest($command); if ($res){ $this->info('stop the server successfully'); } else { $this->info('the server is not running'); } break; case 'status': $res = $this->sendAdminRequest($command); if ($res){ $this->info($res); } else { $this->info('the server is not running'); } break; case 'restart': $res = $this->sendAdminRequest($command); if ($res){ $this->info('restart the server successfully'); } else { $this->info('the server is not running'); } break; default: $this->info('請按照下面格式輸入命令:php artisan websocket {start|stop|status|restart}'); break; } } //初始化服務端 public function initWs($daemonize = false) { if ($daemonize) { $this->info('Starting Websocke server in daemon mode...'); } else { $this->info('Starting Websocke server in interactive mode...'); } $server = new swoole_websocket_server('0.0.0.0', 9501); $server->set([ 'daemonize' => $daemonize, 'log_file' => '/var/log/websocket.log' ]); $server->on('close', function ($server, $fd) { $this->info('close websocket server'); }); $server->on('open', function (swoole_websocket_server $server, $request) { $this->info('websocket open'); }); $server->on('open', [$this, 'onOpen']); $server->on('close', [$this, 'onClose']); $server->on('message', [$this, 'onMessage']); $server->on('request', [$this, 'onRequest']); $this->server = $server; $this->server->start(); } public function onOpen(swoole_websocket_server $server, $request) { $this->info('websocket open'); } public function onClose($server, $fd) { $this->info('close websocket server'); } public function onMessage(swoole_websocket_server $server, $frame) { $this->info($frame->data); $data = json_decode($frame->data, true); //對data進行邏輯處理 $reply = '發送的信息是:' . $data['message']; $response = [ 'status' => true, 'data' => $reply ]; $server->push($frame->fd, json_encode($response)); } //websocket客戶端一樣支持http協議 public function onRequest(swoole_http_request $request, swoole_http_response $response) { if ($request->post['type'] == self::TYPE_ADMIN) { $ret = json_encode($this->commandHandle($request->post['content'])); return $response->end($ret); } } //操做命名 public function commandHandle($command) { if ($command == 'status') { $this->info('handle status'); return $this->server->stats(); } if ($command == 'restart') { $this->info('handle restart'); return $this->server->reload(); } if ($command == 'stop') { $this->info('handle stop'); return $this->server->stop() && $this->server->shutdown(); } return 'Unknown Command'; } //發送http請求 public function sendAdminRequest($content) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:9501"); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Expect:']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, [ 'type' => self::TYPE_ADMIN, 'content' => $content ]); $response = curl_exec($ch); curl_close($ch); return $response; } }
客戶端內容
<!doctype html> <html lang="{{ app()->getLocale() }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>websocket client</title> </head> <body> <div> <input id="message-content" type="text" name="message" /> <button onclick="sendMessage()">發送消息</button> </div> </body> <script> var wsServer = 'ws://115.159.81.46:9501'; var websocket = new WebSocket(wsServer); websocket.onopen = function (evt) { console.log("Connected to WebSocket server."); }; websocket.onclose = function (evt) { console.log("Disconnected"); }; websocket.onmessage = function (evt) { console.log('從服務器接收到json信息: ' + evt.data); alert('服務器返回信息:' + JSON.parse(evt.data).data); }; websocket.onerror = function (evt, e) { console.log('Error occured: ' + evt.data); }; function sendMessage(){ var content = document.getElementById('message-content').value; var data = { message : content, } websocket.send(JSON.stringify(data)); }; </script> </html>
啓動websocket服務器
進入系統根目錄,
php artisan websocket [--daemon] //是否使用daemon模式 php artisan websocket start|stop|status|restart //默認是start