本文主要講述在 Ubuntu 下編譯安裝 Swoole,並根據官方文檔給出的demo進行了測試和搬運,包括:TCP服務器、UDP服務器、HTTP服務器、WebSocket服務器、異步客戶端、定時器和協程相關,通過模仿官方例子領略Swoole給PHPer帶來全新的編程模式和思想。
它彌補PHP在網絡編程的不足。
一、說明
運行環境:
win10
下的Ubuntu
、PHP7.2
、Swoole4.3
二、安裝Swoole
- 下載解壓
sudo wget https://github.com/swoole/swoole-src/archive/v4.3.6.tar.gz
cp v4.3.6.tar.gz swoole-v4.3.6.tar.gz
tar -zxvf swoole-v4.3.6.tar.gz
cd swoole-v4.3.6
- 安裝依賴
# 根據下面的編譯提示進行選擇安裝
sudo apt-get install php-dev
sudo apt-get install autoconf
- 編譯安裝
# 根據自己 php 安裝的目錄
cd swoole-v4.3.6
/usr/local/php/bin/phpize
./configure
make
sudo make install
make
的結果:
...
----------------------------------------------------------------------
Libraries have been installed in:
/home/fly/swoole-src-4.3.6/modules
If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
- add LIBDIR to the `LD_LIBRARY_PATH' environment variable
during execution
- add LIBDIR to the `LD_RUN_PATH' environment variable
during linking
- use the `-Wl,--rpath -Wl,LIBDIR' linker flag
- have your system administrator add LIBDIR to `/etc/ld.so.conf'
See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
Build complete.
Don't forget to run 'make test'.
sudo make install
的結果:
Installing shared extensions: /usr/local/php/lib/php/extensions/no-debug-non-zts-20170718/
Installing header files: /usr/local/php/include/php/
- 配置 php.ini
# 編譯安裝成功后,在 php.ini 文件加入
extension=swoole.so
# extension=/usr/lib/php/20170718/swoole.so
查看是否加載 swoole
擴展
php -m
- 錯誤排查(可忽略)
下面錯誤很明顯,需要指定 php-conf
路徑:
configure: error: Cannot find php-config. Please use --with-php-config=PATH
解決方法:
./configure --with-php-config=/usr/local/php/bin/php-config
三、快速入門
a) TCP 服務器
# 創建 php 文件
vi tcp_server.php
<?php
//創建Server對象,監聽 127.0.0.1:9501端口
$serv = new Swoole\Server("127.0.0.1", 9501);
//監聽連接進入事件
$serv->on('Connect', function ($serv, $fd) {
echo "Client: Connect.\n";
});
//監聽數據接收事件
$serv->on('Receive', function ($serv, $fd, $from_id, $data) {
$serv->send($fd, "Server: ".$data);
});
//監聽連接關閉事件
$serv->on('Close', function ($serv, $fd) {
echo "Client: Close.\n";
});
//啟動服務器
$serv->start();
# 程序運行測試
# 運行 server
php tcp_server.php
# 使用telnet 測試(退出telnet:Ctrl+] 回車,輸入quit 回車)
telnet 127.0.0.1 9501
b) UDP 服務器
vi udp_server.php
<?php
//創建Server對象,監聽 127.0.0.1:9502端口,類型為SWOOLE_SOCK_UDP
$serv = new swoole_server("127.0.0.1", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP);
//監聽數據接收事件
$serv->on('Packet', function ($serv, $data, $clientInfo) {
$serv->sendto($clientInfo['address'], $clientInfo['port'], "Server ".$data);
var_dump($clientInfo);
});
//啟動服務器
$serv->start();
# 運行
php udp_server.php
# 使用 netcat 連接
netcat -u 127.0.0.1 9502
c) HTTP 服務器
vi http_server.php
<?php
$http = new Swoole\Http\Server("0.0.0.0", 9501);
$http->on('request', function ($request, $response) {
if ($request->server['path_info'] == '/favicon.ico' || $request->server['request_uri'] == '/favicon.ico') {
return $response->end();
}
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();
# 程序運行測試
php http_server.php
# 瀏覽器訪問
127.0.0.1:9501
d) WebSocket 服務器
WebSocket服務器是建立在Http服務器之上的長連接服務器,客戶端首先會發送一個Http的請求與服務器進行握手。握手成功后會觸發onOpen事件,表示連接已就緒,onOpen函數中可以得到$request對象,包含了Http握手的相關信息,如GET參數、Cookie、Http頭信息等。
建立連接后客戶端與服務器端就可以雙向通信了。
vi ws_server.php
<?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();
# 程序運行
php ws_server.php
//使用瀏覽器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);
};
e) 定時器
swoole提供了類似JavaScript的setInterval/setTimeout異步高精度定時器,粒度為毫秒級。
vi timer_tick.php
//每隔2000ms觸發一次
$timerId = swoole_timer_tick(2000, function ($timer_id) {
echo "tick-2000ms\n";
});
//9000ms后執行此函數
swoole_timer_after(9000, function () use ($timerId) {
echo "after-9000ms.\n";
//清除定時器
swoole_timer_clear($timerId);
});
php timer_tick.php
f) 同步、異步 TCP 客戶端
vi tcp_client.php
<?php
$client = new swoole_client(SWOOLE_SOCK_TCP);
//連接到服務器
if (!$client->connect('127.0.0.1', 9501, 0.5))
{
die("connect failed.");
}
//向服務器發送數據
if (!$client->send("hello world"))
{
die("send failed.");
}
//從服務器接收數據
$data = $client->recv();
if (!$data)
{
die("recv failed.");
}
echo $data;
//關閉連接
$client->close();
# 異步只能用於cli
vi tcp_async_client.php
<?php
$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
//注冊連接成功回調
$client->on("connect", function($cli) {
$cli->send("hello world\n");
});
//注冊數據接收回調
$client->on("receive", function($cli, $data){
echo "Received: ".$data."\n";
});
//注冊連接失敗回調
$client->on("error", function($cli){
echo "Connect failed\n";
});
//注冊連接關閉回調
$client->on("close", function($cli){
echo "Connection close\n";
});
//發起連接
$client->connect('127.0.0.1', 9501, 0.5);
g) 協程客戶端
vi xxx.php
<?php
$http = new swoole_http_server("0.0.0.0", 9501);
$http->on('request', function ($request, $response) {
$db = new Swoole\Coroutine\MySQL();
$db->connect([
'host' => '127.0.0.1',
'port' => 3306,
'user' => 'user',
'password' => 'pass',
'database' => 'test',
]);
$data = $db->query('select * from test_table');
$response->end(json_encode($data));
});
$http->start();
h) 協程:並發 shell_exec
vi xxx.php
<?php
$c = 10;
while($c--) {
go(function () {
//這里使用 sleep 5 來模擬一個很長的命令
co::exec("sleep 5");
});
}