目录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