swoole client php,Swoole Client
PHP常用socket創(chuàng)建TCP連接,使用CURL創(chuàng)建HTTP連接,為了簡化操作,Swoole提供了Client類用于實現(xiàn)客戶端功能,并增加了異步非阻塞模式,讓用戶在客戶端也能使用事件循環(huán)。
作為客戶端使用,Swoole Client可以在FPM環(huán)境下或 Apache中使用,但不允許使用Async異步模式,只能使用同步非阻塞模式,異步非阻塞模式僅限CLI模式下使用。
Client提供了TCP/UDP socket的客戶端的封裝代碼,使用時僅需new Swoole\Client。
Swoole的socket客戶端對比PHP提供的stream族函數(shù)有什么優(yōu)勢呢?
stream函數(shù)存在超時設置的陷阱和Bug,一旦沒有處理好將會導致服務器長時間阻塞。
stream和fread有8192長度限制,無法支持UDP大包。
swoole_client客戶端支持waitall,在知道包長度的情況下可以一次取完不必循環(huán)獲取。
swoole_client支持UDP的connect解決了UDP串包的問題
swoole_client是純C的代碼
swoole_client支持異步非阻塞回調(diào)
構(gòu)造方法 constructor
Swoole的Client功能封裝為一個swoole_client類,可通過new swoole_client來創(chuàng)建一個客戶端實例。
原型
swoole_client->__construct(
int $sock_type,
int $is_async = SWOOLEL_SOCK_SYNC,
string $key
);
參數(shù)
參數(shù)1:int $sock_type
$sock_type 表示socket類型,可使用swoole提供的宏來指定類型。
SWOOLE_TCP 創(chuàng)建TCP Socket
SWOOLE_TCP6 創(chuàng)建IPv6 TCP Socket
SWOOLE_UDP 創(chuàng)建UDP Socket
SWOOLE_UDP6 創(chuàng)建IPv6 UDP Socket
SWOOLE_SSL 開啟SSL加密
SWOOLE_KEEP 開啟連接復用
其中SWOOLE_SSL與SWOOLE_KEEP不能單獨使用,需要與前四個選項共同作用。
// 創(chuàng)建并開啟SSL加密的TCP客戶端
$client = new swoole_client(SWOOLE_TCP | SWOOLE_SSL);
SWOOLE_KEEP
swoole_client支持在PHP-FPM或Apache中建立一個TCP長連接到服務器,當客戶端啟用SWOOLE_KEEP選項后,一個請求結(jié)束不會關閉socket,下一次再進行connect連接時會自動復用上次創(chuàng)建的連接。如果執(zhí)行connect連接時發(fā)現(xiàn)連接已經(jīng) 被服務器斷開,那么connect將會創(chuàng)建新的連接。
// 創(chuàng)建一個可以在FPM中使用的長連接客戶端
$client = new swoole_client(SWOOLE_TCP | SWOOLE_KEEP);
SWOOLE_KEEP建立TCP長連接有什么優(yōu)勢呢?
TCP長連接可以減少connect建立連接時的三次握手和close斷開連接的四次揮手所帶來的額外IO開銷
降低服務器connect建立連接和close斷開連接的次數(shù)
例如:在PHP-FPM模式下可以使用同步模式的swoole_client向后端發(fā)送數(shù)據(jù),為了能夠讓不同的連接復用同一個客戶端client,可以開啟SWOOLE_KEEP選項并指定對應的key。這樣對于同一個邏輯就不需要為每個請求都創(chuàng)建一個新的連接了。這里需要注意的是,雖然共用了同一個連接,但是每個請求仍然會創(chuàng)建一個新的swoole_client實例,只是這些實例底層會復用同一個TCP連接。因此,如果開啟了SWOOLE_KEEP選項也就不能隨便調(diào)用close關閉客戶端,并且在使用時可使用isConnect方法來判斷連接是否可用。
//獲取參數(shù)
$data = isset($_GET["data"])&&!empty($_GET["data"]) ? $_GET["data"] : "";
//創(chuàng)建同步阻塞TCP客戶端,并開啟連接復用。
$host = "127.0.0.1";
$port = 9501;
$key = $host.":".$port;//默認使用IP:PORT作為長連接的key,具有相同key的連接會被復用。
$client = new swoole_client(SWOOLE_TCP | SWOOLE_KEEP, SWOOLE_SYNC, $key);
//判斷客戶端時已連接
if(!$client->isConnect()){
$client->connect($host, $port);
}
//發(fā)送數(shù)據(jù)
$client->send($data);
//接收打印
echo $client->recv();
參數(shù)2:int $is_sync
$is_sync表示同步阻塞或異步非阻塞,默認為同步阻塞。模式不同決定了可以使用的API的不同。
SWOOLE_SOCK_SYNC
SWOOLE_SOCK_SYNC 表示創(chuàng)建一個同步阻塞客戶端,默認設置。
swoole_client(SWOOLE_TCP, SWOOLE_SYNC, $key);
當設定swoole_client為同步模式后,可以像使用PHP的sockets擴展也一樣使用swoole_client來創(chuàng)建socket連接。由于是同步阻塞模式,所以connect、recv、send這樣的方法都會阻塞進程。相比較PHP的sockets擴展提供的方法,swoole_client的API更為簡潔,使用起來更加方便。
/**
* 創(chuàng)建同步阻塞模式下的TCP客戶端
* 同步阻塞模式下connect/send/recv會等待IO完成后再返回,服務端返回后才會向下執(zhí)行。
* 同步阻塞模式下并不會消耗CPU資源,IO操作未完成當前進程會自動轉(zhuǎn)入sleep模式。
* 當IO完成后操作系統(tǒng)會喚醒當前進程,繼續(xù)向下執(zhí)行代碼。
* */
$client = new swoole_client(SWOOLE_TCP);
//連接到服務器
$host = "127.0.0.1";
$port = 9501;
$timeout = 1;//超過與服務器交互的超時秒數(shù)會自動斷開
if(!$client->connect($host, $port, $timeout)){
die("[connect] failed".PHP_EOL);
}
//發(fā)送數(shù)據(jù)
$message = "hello world";
if(!$client->send($message)){
die("[send] failed".PHP_EOL);
}
//接收數(shù)據(jù)
if(!$data = $client->recv()){
die("[recv] failed".PHP_EOL);
}
echo $data.PHP_EOL;
//關閉連接
$client->close();
SWOOLE_SOCK_ASYNC
SWOOLE_SOCK_ASYNC 表示創(chuàng)建一個異步非阻塞客戶端
需要通過on方法注冊異步回調(diào)函數(shù)
多個swoole_client客戶端可以嵌套回調(diào)
異步模式僅可用于CLI命令行模式
swoole_client(SWOOLE_TCP, SWOOLE_SOCK_ASYNC)
當設置swoole_client為異步模式后,swoole_client就不能再使用recv方法了,而是需要通過on方法提供指定的回調(diào)函數(shù),然后在回調(diào)函數(shù)當中處理。
異步模式的swoole_client必須設置四種回調(diào)函數(shù)connect、receive、error、close,不能夠缺省,否則在調(diào)用connect連接時會提示回調(diào)未設置。同樣,因為已經(jīng)有了onConnect回調(diào),因此異步模式的swoole_client調(diào)用connect方法時不再阻塞,connect方法也只會返回true,此時需要在onConnect回調(diào)中確定連接成功,或者在onError回調(diào)中確定連接失敗。
/**
* 創(chuàng)建異步非阻塞模式下的TCP客戶端
* 必須設置onConnect、onError、onReceive、onClose
* */
$client = new swoole_client(SWOOLE_TCP, SWOOLE_SOCK_ASYNC);
//連接成功時回調(diào)
$client->on("connect", function(swoole_client $client){
//向服務器發(fā)送數(shù)據(jù)
$message = "hello world".PHP_EOL;
$client->send($message);
});
//數(shù)據(jù)接收時回調(diào)
$client->on("receive", function(swoole_client $client, $data){
if(empty($data)){
$client->close();
}else{
echo "[receive] $data".PHP_EOL;
sleep(1);
$client->send(time());
}
});
//連接失敗時回調(diào)
$client->on("error", function(swoole_client $client){
echo "[error] connection failed".PHP_EOL;
});
//注冊關閉連接時回調(diào)
$client->on("close", function(swoole_client $client){
echo "[close] connection close".PHP_EOL;
});
//連接到服務器
$host = "127.0.0.1";
$port = 9501;
$timeout = 0.5;//超過與服務器交互的超時秒數(shù)會自動斷開
if(!$client->connect($host, $port, $timeout)){
die("[connect] failed".PHP_EOL);
}
異步模式的swoole_client跟swoole_server一樣以事件作為驅(qū)動,而無法發(fā)像同步模式一樣來驅(qū)動。只要提供需要的事件就能過夠處理邏輯,如swoole的定時器、事件循環(huán)等。
例如:實現(xiàn)客戶端,每秒向服務器發(fā)送一個心跳包,在接收到終端輸入的內(nèi)容后,該客戶端會將終端輸入的內(nèi)容發(fā)送給服務器。
服務器
$ vim server.php
//創(chuàng)建TCP服務器并設置IP和端口
$host = "0.0.0.0";
$port = 9501;
$serv = new swoole_server($host, $port);
//設置服務器運行時配置
$configs = [];
$configs["worker_num"] = 2;//設置Worker工作進程數(shù)量
$configs["task_worker_num"] = 2;//設置Task異步任務的進程數(shù)量
$serv->set($configs);
//Master主進程 當服務器啟動時觸發(fā)
$serv->on("start", function(swoole_server $serv){
echo PHP_EOL."[start] master {$serv->master_pid} manager {$serv->manager_pid}".PHP_EOL;
});
//Worker工作進程 當客戶端有新TCP連接時觸發(fā)
$serv->on("connect", function(swoole_server $serv, $fd, $reactor_id){
echo PHP_EOL."[connect] worker {$serv->worker_pid} reactor {$reactor_id} client {$fd}".PHP_EOL;
});
//Worker工作進程 當客戶端連接向服務器發(fā)送數(shù)據(jù)時觸發(fā)
$serv->on("receive", function(swoole_server $serv, $fd, $reactor_id, $data){
echo "[receive] worker {$serv->worker_pid} reactor {$reactor_id} client {$fd} data:{$data}".PHP_EOL;
if($data !== "PING"){
//投遞異步任務給TaskWorker異步任務進程,程序會立即返回向下執(zhí)行后續(xù)代碼。
$task_id = $serv->task($data);
echo "[receive] task {$task_id} data:{$data}".PHP_EOL;
//向客戶端文件描述符發(fā)送字符串信息
$message = "success";
$serv->send($fd, $message);
echo "[receive] client {$fd} send:{$message}".PHP_EOL;
}
});
//TaskWorker任務進程 處理異步任務
$serv->on("task", function(swoole_server $serv, $task_id, $reactor_id, $data){
echo "[task] task {$task_id} reactor {$reactor_id} data:{$data}".PHP_EOL;
sleep(10);//模擬異步操作執(zhí)行時長10秒
//返回任務執(zhí)行的結(jié)果
$serv->finish("finish");
});
//Worker進程 處理異步任務完成的結(jié)果
$serv->on("finish", function(swoole_server $serv, $task_id, $data){
echo "[finish] worker {$serv->worker_pid} task {$task_id} data:{$data}".PHP_EOL;
});
//Worker進程 監(jiān)聽斷開連接時觸發(fā)
$serv->on("close", function(swoole_server $serv, $fd, $reactor_id){
//連接斷開類型
$disconnect_type = "client";//客戶端主動斷開
if($reactor_id < 0){
$disconnect_type = "server";//服務器主動斷開
}
echo "[close] worker {$serv->manager_pid} client {$fd} disconnect {$disconnect_type}".PHP_EOL;
});
//啟動服務器
$serv->start();
客戶端
$ vim client.php
class Client
{
private $client;
private $timer_id;
/**構(gòu)造函數(shù) */
public function __construct($host, $port, $timeout=1)
{
//構(gòu)建異步非阻塞客戶端對象
$this->client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
//綁定事件回調(diào)函數(shù)
$this->client->on("Connect", [$this, "onConnect"]);
$this->client->on("Receive", [$this, "onReceive"]);
$this->client->on("Close", [$this, "onClose"]);
$this->client->on("Error", [$this, "onError"]);
//連接服務器
$this->client->connect($host, $port, $timeout=1);
}
/**連接服務器 */
public function connect($host, $port, $timeout=1)
{
//連接服務器
$fp = $this->client->connect($host, $port, $timeout);
if(!$fp){
echo "[connect] error{$fp->errCode} {$fp->errMsg}".PHP_EOL;
return;
}
}
/**連接成功回調(diào) */
public function onConnect($client)
{
//將標準輸入添加到swoole的事件監(jiān)聽中
swoole_event_add(STDIN, function($fp){
//讀取標準輸入
$message = trim(fgets(STDIN));
if(!empty($message)){
//發(fā)送數(shù)據(jù)
$this->client->send($message);
}
});
//添加定時器,每秒向服務器發(fā)送一個心跳包
$this->timer_id = swoole_timer_tick(1000, function(){
$this->client->send("PING");
});
}
public function send($message)
{
$this->client->send($message);
}
public function onReceive(swoole_client $client, $data)
{
echo "[receive] {$data}".PHP_EOL;
}
public function onClose(swoole_client $client)
{
$data = json_encode($client);
echo "[close] {$data}".PHP_EOL;
//斷開連接時清除定時器
swoole_timer_clear($this->timer_id);
}
public function onError(swoole_client $client)
{
$data = json_encode($client);
echo "[error] {$data}".PHP_EOL;
}
}
$client = new Client("127.0.0.1", 9501, 1);
參數(shù)3:string $key
$key 表示用于長連接的key,默認使用IP:PORT作為key,相同key的連接會被復用。
對象屬性
$client = new swoole_client(SWOOLE_TCP, SWOOLE_ASYNC);
echo json_encode($client).PHP_EOL;
{
"errCode":0,
"sock":-1,
"reuse":false,
"reuseCount":0,
"type":1025,
"id":null,
"setting":null
}
錯誤碼 int swoole_client->errCode
當connect、send、recv、close失敗時會自動設置$swoole_client->errCode的值,errCode的值等于Linux的errno,可以使用socket_strerror將錯誤碼轉(zhuǎn)換為錯誤信息。
$client->on("close", function(swoole_client $client){
if(!$client->errCode){
$error = socket_strerror($client->errCode);
echo "error: {$error}".PHP_EOL;
}
echo "close".PHP_EOL;
});
Socket文件描述符 int swoole_client->sock
sock屬性值為整型的socket文件描述符,可轉(zhuǎn)換int作為數(shù)組的key。
// 在PHP代碼中可以使用
$sock = fopen("php://fd/".$swoole_client->sock);
$client->sock屬性值僅在$client->connect后才能取出,在未連接服務器之前此屬性值為null。
將swoole_client的socket轉(zhuǎn)換為一個stream socket可調(diào)用fread、fwrite、fclose等函數(shù)進行操作。
swoole_server中的$fd文件描述符不能使用sock()方法轉(zhuǎn)換,因為$fd文件描述符只是一個屬于主進程的數(shù)字。
是否復用 bool swoole_client->reuse
reuse屬性值表示連接是新連接的還是復用已存在的,是一個布爾值,需要與SWOOLE_KEEP配合使用。
例如:WebSocket客戶端與服務器建立連接后需要進行握手,如果連接是復用的,就無需再次進行握手,直接發(fā)送WebSocket數(shù)據(jù)幀即可。
if($client->reuse){
$client->send($data);
}
建立連接 connect
connect方法用于建立連接到遠程服務器
原型
bool $server_client->connect(
string $host,
int $port,
float $timeout = 0.5,
int $flag = 0
);
參數(shù)
參數(shù)1:string $host 表示遠程服務器的地址,swoole1.10.0+已支持自動異步解析域名即可直接傳入域名。
參數(shù)2:int $port 表示遠程服務器的端口
參數(shù)3:float $timeout 表示網(wǎng)絡IO超時秒數(shù),包括connect、send、recv,單位為秒并支持浮點數(shù),默認0.5s即500ms。
參數(shù)4:int $flag
在UDP類型時表示是否啟用udp_connect設定此選項后將綁定$host與$port,此UDP將會丟棄非指定host/port的數(shù)據(jù)包。
在TCP類型中$flag=1表示設置為非阻塞socket,connect會立即返回。如果將$flag設置為1則在send/recv前必須使用swoole_client_select來檢測是否完成了連接。
模式
同步模式
connect方法在同步模式下會發(fā)生阻塞,直到連接成功并返回true。這個時候就可以向服務器發(fā)送數(shù)據(jù)或接收數(shù)據(jù)了。如果連接失敗則會返回false。另外,同步TCP客戶端在執(zhí)行close之后,可以再次發(fā)起connect創(chuàng)建新連接到服務器。
if($client->connect($host, $port)){
$client->send($message);
}else{
echo "connect failed";
}
異步模式
異步模式下connect連接會立即返回true,但實際上連接并未建立,所以不能在connect后使用send。此時使用fsConnected()判斷也是false。當連接創(chuàng)建成功后,系統(tǒng)會自動回調(diào)onConnect,此時才可以使用send方法向服務器發(fā)送數(shù)據(jù)。
異步客戶端執(zhí)行connect時會增加一次引用計數(shù),當連接關閉時會減少引用計數(shù)。
版本
小于swoole1.9.11版本中$timeout超時設置在異步客戶端中是無效的,應用層需要使用Timer::after自行添加定時器來實現(xiàn)異步客戶端的鏈接超時控制。
大于等于swoole1.9.11版本中,底層會自動添加定時器,在規(guī)定的時間內(nèi)未連接成功時,底層會觸發(fā)onError連接失敗事件,錯誤碼為ETIMEOUT(110)。
失敗重連
connect失敗后如果希望重連一次,必須先進行close關閉舊的socket,否則會返回EINPROCESS錯誤,因為當前的socket正在連接服務器,客戶端并不知道是否連接成功,所以無法再次執(zhí)行connect。
調(diào)用close會關閉當前的socket,底層會重新創(chuàng)建新的socket來進行連接。
啟用SWOOLE_KEEP長連接后,close調(diào)用的第一個參數(shù)需要設置為true來表示強行銷毀長連接socket。
//連接失敗
if($socket->connect($host, $port) === false){
$socket->close(true);//關閉舊的socket
$socket->connect($host, $port);//重連
}
UDP連接
UDP連接時默認底層并不會啟用,一個UDP客戶端執(zhí)行連接時,底層在創(chuàng)建socket后會立即返回成功,此時的socket綁定的地址為0.0.0.0,任何其它對端均可向此端口發(fā)送數(shù)據(jù)包。
$client->connect("192.168.1.100", 9502)
此時操作系統(tǒng)會為客戶端socket隨機分配一個端口,其它機器可向這個端口發(fā)送數(shù)據(jù)包。
未開啟UDP連接時調(diào)用getsockname返回的host為0.0.0.0。
將connect的第四項參數(shù)$flag設置為1時將啟用UDP連接。
$client->connect("192.168.1.100", 9501, 1, 1)
此時將會綁定客戶端和服務器,底層會根據(jù)服務器的地址來綁定socket綁定的地址,例如連接了192.168.1.100,當前socket會被綁定到192.168.1.*的本地機器上。啟用UDP連接后客戶端將不再接收其它主機向此端口發(fā)送的數(shù)據(jù)包。
發(fā)送數(shù)據(jù) send
send方法用于建立連接后發(fā)送數(shù)據(jù)到遠程服務器
原型
int $swoole_client->send(string $data);
參數(shù)
參數(shù):string $data 發(fā)送的數(shù)據(jù),格式為字符串,支持二進制。
返回
成功發(fā)送返回已發(fā)送數(shù)據(jù)的長度
失敗返回false,并設置$swoole_client->errCode錯誤碼。
模式
同步
發(fā)送的數(shù)據(jù)沒有長度閑置
發(fā)送的數(shù)據(jù)太多時socket緩存區(qū)塞滿,底層會阻塞等待可寫。
異步
發(fā)送數(shù)據(jù)的長度收到socket_buffer_size限制
如果socket緩存區(qū)已滿,swoole的處理邏輯參考swoole_event_write。
注意
如果沒有執(zhí)行連接connect直接調(diào)用send會觸發(fā)PHP警告
接收數(shù)據(jù) recv
recv方法用于從服務器接收數(shù)據(jù)
原型
swoole1.7.22-
string $swoole_client->recv(int $size = 655335, bool $waitall = 0)
swoole1.7.22+
string $swoole_client->recv(int $size = 65535, int $flag = 0);
swoole1.7.22版本后,將原來第二個參數(shù)$waitall參數(shù)修改為$flags可以接收一些特殊的socket接收設置,為了兼容舊的接口,如果$flag = 1表示 $flag = swoole_client::MSG_WAITALL。
$client->recv(8192, swoole_client::MSG_PEEK | swoole_client::MSG_WAITALL);
參數(shù)
int $size 表示接收數(shù)據(jù)的緩存區(qū)最大長度,此參數(shù)設置過大會占用較大內(nèi)存。
bool $waitall 表示是否等待所有數(shù)據(jù)到達后返回
如果設置了$waitall就必須設定準確的$size否則會一直等待,直到接收的數(shù)據(jù)長度達到$size。如果未設置$waitall = true時$size最大未64K,如果設置了錯誤的$size將會導致recv超時而返回false。
返回
成功:成功接收到數(shù)據(jù)則返回字符串
失敗:返回false需設置錯誤碼$client->errCode屬性
連接關閉:返回空字符串
EOL/Length
如果客戶端啟用了EOF/Length檢測后,無需設置$size和$waitall參數(shù),擴展層會返回完整的數(shù)據(jù)包或返回false。當接收到錯誤的包頭或包頭中長度超過package_max_length設置時,recv會返回空字符串,PHP代碼中應當關閉此連接。
關閉連接 close
close用于關閉客戶端連接,操作成功后返回true。當swoole_client客戶端連接被close關閉后不要再次發(fā)起連接close。正確的做法是銷毀當前的swoole_client,然后再重新創(chuàng)建一個swoole_client并發(fā)起新的連接。
bool $swoole_client->close(bool $force = false);
參數(shù):bool $force 表示是否強制關閉連接,可用于關閉SWOOLE_KEEP長連接。
注意:swoole_client對象在析構(gòu)時會自動關閉
異步非阻塞客戶端close關閉時會立即關閉連接,如果發(fā)送隊列中仍然有數(shù)據(jù)底層會丟棄。所以請勿在大量發(fā)送數(shù)據(jù)后立即close關閉,否則發(fā)送的數(shù)據(jù)未必能真正到達服務器端。
服務器
$ vim server.php
//創(chuàng)建TCP服務器并設置IP和端口
$host = "0.0.0.0";
$port = 9501;
$server = new swoole_server($host, $port);
//設置服務器運行時配置
$configs = [];
$configs["worker_num"] = 2;//設置Worker工作進程數(shù)量
$configs["task_worker_num"] = 2;//設置Task異步任務的進程數(shù)量
$server->set($configs);
//Master主進程 當服務器啟動時觸發(fā)
$server->on("start", function(swoole_server $server){
echo PHP_EOL."[start] master {$server->master_pid} manager {$server->manager_pid}".PHP_EOL;
});
//Worker工作進程 當客戶端有新TCP連接時觸發(fā)
$server->on("connect", function(swoole_server $server, $fd, $reactor_id){
echo PHP_EOL."[connect] worker {$server->worker_pid} reactor {$reactor_id} client {$fd}".PHP_EOL;
});
//Worker工作進程 當客戶端連接向服務器發(fā)送數(shù)據(jù)時觸發(fā)
$server->on("receive", function(swoole_server $server, $fd, $reactor_id, $data){
echo "[receive] worker {$server->worker_pid} reactor {$reactor_id} client {$fd} data:{$data}".PHP_EOL;
if($data !== "PING"){
//投遞異步任務給TaskWorker異步任務進程,程序會立即返回向下執(zhí)行后續(xù)代碼。
$task_id = $server->task($data);
echo "[receive] task {$task_id} data:{$data}".PHP_EOL;
//向客戶端文件描述符發(fā)送字符串信息
$message = "success";
$server->send($fd, $message);
echo "[receive] client {$fd} send:{$message}".PHP_EOL;
}
});
//TaskWorker任務進程 處理異步任務
$server->on("task", function(swoole_server $server, $task_id, $reactor_id, $data){
echo "[task] task {$task_id} reactor {$reactor_id} data:{$data}".PHP_EOL;
sleep(10);//模擬異步操作執(zhí)行時長10秒
//返回任務執(zhí)行的結(jié)果
$server->finish("finish");
});
//Worker進程 處理異步任務完成的結(jié)果
$server->on("finish", function(swoole_server $server, $task_id, $data){
echo "[finish] worker {$server->worker_pid} task {$task_id} data:{$data}".PHP_EOL;
});
//Worker進程 監(jiān)聽斷開連接時觸發(fā)
$server->on("close", function(swoole_server $server, $fd, $reactor_id){
//連接斷開類型
$disconnect_type = "client";//客戶端主動斷開
if($reactor_id < 0){
$disconnect_type = "server";//服務器主動斷開
}
echo "[close] worker {$server->manager_pid} client {$fd} disconnect {$disconnect_type}".PHP_EOL;
});
//啟動服務器
$server->start();
客戶端
$ vim client.php
客戶端發(fā)送4MB的數(shù)據(jù),實際傳輸可能需要一段時間,此時如果立即進行close關閉操作,可能只有小部分數(shù)據(jù)傳輸成功,大部分數(shù)據(jù)在發(fā)送隊列中排隊等待發(fā)送,close關閉時會丟失這些數(shù)據(jù)。
$client = new swoole_client(SWOOLE_TCP, SWOOLE_ASYNC);
$client->on("connect", function(swoole_client $client){
echo "connect".PHP_EOL;
//將標準輸入添加到事件監(jiān)聽中
swoole_event_add(STDIN, function($fp) use($client){
$msg = trim(fgets(STDIN));
$client->send($msg);
});
});
$client->on("receive", function(swoole_client $client, $data){
echo "receive:{$data}".PHP_EOL;
$client->send(str_repeat("X", 1024*1024*4).PHP_EOL);
$client->close();
});
$client->on("error", function(swoole_client $client){
echo "error".PHP_EOL;
});
$client->on("close", function(swoole_client $client){
echo "close".PHP_EOL;
});
$client->connect("127.0.0.1", 9501);
解決方案
配合使用onBufferEmpty等待發(fā)送隊列為空時進行close操作
$client = new swoole_client(SWOOLE_TCP, SWOOLE_ASYNC);
$client->on("connect", function(swoole_client $client){
echo "connect".PHP_EOL;
//將標準輸入添加到事件監(jiān)聽中
swoole_event_add(STDIN, function($fp) use($client){
$msg = trim(fgets(STDIN));
$client->send($msg);
});
});
$client->on("receive", function(swoole_client $client, $data){
echo "receive:{$data}".PHP_EOL;
$client->send(str_repeat("X", 1024*1024*4).PHP_EOL);
$client->close();
});
$client->on("error", function(swoole_client $client){
echo "error".PHP_EOL;
});
$client->on("close", function(swoole_client $client){
echo "close".PHP_EOL;
});
//配合使用onBufferEmpty等待發(fā)送隊列為空時進行關閉close操作
$client->on("bufferEmpty", function(swoole_client $client){
$client->close();
});
$client->connect("127.0.0.1", 9501);
協(xié)議設計為onReceive收到數(shù)據(jù)后主動關閉連接,發(fā)送數(shù)據(jù)時對端主動關閉連接。
總結(jié)
以上是生活随笔為你收集整理的swoole client php,Swoole Client的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql脚本简书,mysql一键安装脚
- 下一篇: PHP中普通方法如何调用静态属性,php