TCP 非同步風格伺服器 非同步風格伺服器通過監聽事件的方式來編寫程式。當對應的事件發生時底層會主動回調指定的函數。 由於預設開啟協程化,在回調函數內部會自動創建協程,遇到 IO 會產生協程調度,非同步風格伺服器無法保證調度順序,所以在遇到併發時無法保證事件執行順序。 # server.php // 創建 ...
TCP 非同步風格伺服器
非同步風格伺服器通過監聽事件的方式來編寫程式。當對應的事件發生時底層會主動回調指定的函數。
由於預設開啟協程化,在回調函數內部會自動創建協程,遇到 IO 會產生協程調度,非同步風格伺服器無法保證調度順序,所以在遇到併發時無法保證事件執行順序。
# server.php
// 創建 TCP 伺服器對象,監聽 0.0.0.0:9501埠
$serv = new Swoole\Server("0.0.0.0", 9501);
// 設置伺服器運行參數
$serv->set(array(
'daemonize' => 1, // 作為守護進程運行,需同時設置log_file
'log_file' => '/www/logs/swoole.log', // 指定標準輸出和錯誤日誌寫入的文件
));
// 監聽連接進入事件
// $fd 為客戶端連接的唯一標識符
$serv->on('Connect', function ($serv, $fd) {
echo "Client: Connect: {$fd} .\n";
});
// 監聽數據接收事件
$serv->on('Receive', function ($serv, $fd, $from_id, $data) {
// 向指定的客戶端連接發送數據
$serv->send($fd, "Server: " . $data);
});
// 監聽連接關閉事件
$serv->on('Close', function ($serv, $fd) {
echo "Client: Close: {$fd} .\n";
});
// 監聽伺服器正常關閉事件
// 使用 kill -15 發送 SIGTREM 信號到主進程正常關閉時觸發
// kill -9 或 Ctrl+C 不會觸發該事件
$serv->on('Shutdown', function ($serv) {
echo "伺服器正常關閉.\n";
});
// 啟動伺服器
$serv->start();
運行並測試 TCP 非同步風格伺服器
# 如果程式已經運行,先結束進程
kill -9 11590
# 在 cli 命令行環境運行服務端
php server.php
# 查看伺服器監聽的埠
netstat -an | grep 9501
# 使用Telnet測試連接服務端
telnet 127.0.0.1 9501
# 發送數據
hello
# 接收數據
Server: hello
TCP 協程風格伺服器
協程風格伺服器處理連接的過程是完全同步的,程式可以順序處理 Connect
、Receive
、Close
事件,可以保證事件執行順序。
# server.php
// 在進程池中創建兩個進程
$pool = new Swoole\Process\Pool(2);
// 讓每個 OnWorkerStart 回調都自動創建一個協程
$pool->set(['enable_coroutine' => true]);
// 進程綁定工作進程啟動事件
$pool->on('workerStart', function ($pool, $id) {
// 每個進程都監聽9501埠,不使用ssl,開啟埠重用
$server = new Swoole\Coroutine\Server('0.0.0.0', '9501', false, true);
// 接收 kill -15 信號關閉伺服器
Swoole\Process::signal(SIGTERM, function () use ($server) {
echo "伺服器正常關閉.\n";
$server->shutdown();
});
// 設置連接處理函數,接收到新的連接請求並自動創建一個協程並執行回調函數
// 回調函數會在協程空間中執行
$server->handle(function(Swoole\Coroutine\Server\Connection $conn) {
echo "Client: Connect.\n";
// 迴圈接收和返回數據
while (true) {
// 接收數據
$data = $conn->recv();
if (empty($data)) {
// 關閉連接
$conn->close();
break;
}
//發送數據
$conn->send("Server: " . $data);
\Co::sleep(1);
}
});
// 啟動伺服器
$server->start();
});
$pool->start();
TCP 同步阻塞客戶端
// 同步阻塞客戶端可以用於 PHP-FPM 環境下
$client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
if (!$client->connect('127.0.0.1', 9501, -1)) {
exit("connect failed. Error: {$client->errCode}\n");
}
$client->send("hello world\n");
// 同步阻塞等待服務端返回內容
echo $client->recv();
$client->close();
TCP 協程客戶端
// 設置要 Hook 的函數的範圍
Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]);
// 協程客戶端,底層自動使用協程調度實現非同步IO,用於代替非同步客戶端
Co\run(function(){
$client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP);
$client->set(array(
'timeout' => 1.5, //總超時,包括連接、發送、接收所有超時
'connect_timeout' => 1.0, //連接超時,會覆蓋第一個總的 timeout
'write_timeout' => 2.0, //發送超時,會覆蓋第一個總的 timeout
'read_timeout' => 0.5, //接收超時,會覆蓋第一個總的 timeout
));
if (!$client->connect('127.0.0.1', 9501, 0.5)) {
exit("connect failed. Error: {$client->errCode}\n");
}
$client->send("hello world.\n");
echo $client->recv();
$client->close();
});