一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

【Swoole系列4.7】協(xié)程服務(wù)客戶端

 硬核項(xiàng)目經(jīng)理 2022-09-05 發(fā)布于湖南

協(xié)程服務(wù)客戶端

協(xié)程的學(xué)習(xí)依然還在繼續(xù),要知道,Swoole 現(xiàn)在最核心的就是協(xié)程,或者說(shuō),整個(gè)軟件開(kāi)發(fā)語(yǔ)言中,協(xié)程都是熱門的內(nèi)容。對(duì)于協(xié)程的理論以及一些基礎(chǔ)的操作我們都已經(jīng)了解過(guò)了,接下來(lái),我們?cè)倏纯?Swoole 中提供的一些協(xié)程客戶端功能。在協(xié)程之前,異步客戶端是 Swoole 的主流應(yīng)用,但是,現(xiàn)在已經(jīng)不推薦了,所以我們就直接拿協(xié)程來(lái)講這些客戶端相關(guān)的內(nèi)容。

TCP客戶端

TCP 和 UDP 就不分開(kāi)說(shuō)了,無(wú)非就是換些參數(shù)或方法的事,所以我們就以 TCP 為例,來(lái)看一下在 Swoole 中如何實(shí)現(xiàn)一個(gè)協(xié)程版的 TCP 客戶端。

php\Swoole\Coroutineine\run(function () {
   $client = new \Swoole\Coroutine\Client(SWOOLE_SOCK_TCP);
   if (!$client->connect('127.0.0.1'95010.5)) {
       echo "connect failed. Error: {$client->errCode}\n";
   }
   $client->send("hello world\n");

   var_dump($client->isConnected()); // bool(true)

   while (true) {
       $data = $client->recv();
       if (strlen($data) > 0) {
           echo $data;
           $client->send(time() . PHP_EOL);
       } else {
           var_dump($data);
           if ($data === '') {
               // 全等于空 直接關(guān)閉連接
               $client->close();
               var_dump($client->isConnected()); // bool(true)
               break;
           } else {
               if ($data === false) {
                   // 可以自行根據(jù)業(yè)務(wù)邏輯和錯(cuò)誤碼進(jìn)行處理,例如:
                   // 如果超時(shí)時(shí)則不關(guān)閉連接,其他情況直接關(guān)閉連接
                   if ($client->errCode !== SOCKET_ETIMEDOUT) {
                       $client->close();
                       break;
                   }
               } else {
                   $client->close();
                   break;
               }
           }
       }
       \Co::sleep(1);
   }
});
// bool(true)
// 協(xié)程 TCP :hello world
// 協(xié)程 TCP :1640837327
// string(0) ""
// bool(false)

這個(gè)例子其實(shí)就是官網(wǎng)的例子,然后服務(wù)端我們直接使用 【Swoole系列4.1】Swoole協(xié)程系統(tǒng)https://mp.weixin.qq.com/s/6VhloFezWEs5bEdMVRGiLA 上的那個(gè) TCP 服務(wù)端來(lái)進(jìn)行測(cè)試的。

\Swoole\Coroutine\Client 就是一個(gè)協(xié)程客戶端對(duì)象,它的第一個(gè)參數(shù)就可以指定連接的類型,這里我們就是使用 TCP 連接。然后調(diào)用這個(gè)對(duì)象的 connect() 方法建立連接,連接建立成功后,就可以使用 send() 發(fā)送數(shù)據(jù),recv() 接收數(shù)據(jù)。

isConnected() 方法用于判斷連接是否正常,close() 可以關(guān)閉連接,errCode 屬性返回錯(cuò)誤信息。在這里,我們修改了服務(wù)端代碼,進(jìn)行兩次通信之后就關(guān)閉服務(wù)端的連接,所以輸出的效果就是像上面的注釋中那樣的。服務(wù)端的代碼類似下面這樣:

Swoole\Coroutine\run (function () {
    $server = new Swoole\Coroutine\Server('0.0.0.0'9501false);
    $server->handle(function(Swoole\Coroutine\Server\Connection $conn){
        $i = 2;
        while($i){
            $data = $conn->recv();
            echo $data, PHP_EOL;
            $conn->send("協(xié)程 TCP :" . $data);
            sleep(1);
            $i--;
        }
        $conn->close();
    });

    $server->start();
});

HTTP客戶端

HTTP 客戶端的就更好理解了,你其實(shí)就可以把它相像成是一個(gè)高效的、協(xié)程版的、封裝好的 CURL 。

\Swoole\Coroutine\run(function () {
   go(function(){
       $cli = new Swoole\Coroutine\Http\Client('www.baidu.com'80);
       $cli->get('/s?wd=php');
       echo $cli->statusCode, '===', $cli->errCode, PHP_EOL;
       preg_match("/<title>(.*)?<\/title>/i", $cli->body, $match);
       var_dump($match);
       var_dump($cli->getHeaders());
       $cli->close();
   });
//    200===0
//    array(2) {
//            [0]=>
//      string(31) "<title>php_百度搜索</title>"
//            [1]=>
//      string(16) "php_百度搜索"
//    }
//    array(17) {
//            ["bdpagetype"]=>
//      string(1) "3"
//            ["bdqid"]=>
//      string(18) "0xdec6e00b000009a8"
//            ["cache-control"]=>
//      string(7) "private"
//            ["ckpacknum"]=>
//      string(1) "2"
//      string(9) "b000009a8"
//            ["ckrndstr"]=>
//            ["connection"]=>
//      string(10) "keep-alive"
//            ["content-encoding"]=>
//      string(4) "gzip"
//            ["content-type"]=>
//      string(23) "text/html;charset=utf-8"
//            ["date"]=>
//      string(29) "Fri, 31 Dec 2021 00:57:32 GMT"
//            ["p3p"]=>
//      string(34) "CP=" OTI DSP COR IVA OUR IND COM ""
//            ["server"]=>
//      string(7) "BWS/1.1"
//            ["set-cookie"]=>
//      string(115) "H_PS_PSSID=34444_35105_35628_35489_34584_35491_35695_35234_35644_35318_26350_35620_22159; path=/; domain=.baidu.com"
//            ["traceid"]=>
//      string(40) "1640912252031691930616052764259657976232"
//            ["vary"]=>
//      string(15) "Accept-Encoding"
//            ["x-frame-options"]=>
//      string(10) "sameorigin"
//            ["x-ua-compatible"]=>
//      string(16) "IE=Edge,chrome=1"
//            ["transfer-encoding"]=>
//      string(7) "chunked"
//    }

   go(function(){
       $cli = new Swoole\Coroutine\Http\Client('127.0.0.1'9501);
       $cli->setHeaders(['X-Requested-With'=>'xmlhttprequest','Content-type'=>'application/x-www-form-urlencoded']);
       $cli->post('/showname', ['name'=>'Zyblog']);
       echo $cli->statusCode, '===', $cli->errCode, PHP_EOL;
       echo $cli->body, PHP_EOL;
       $cli->close();
   });
//    200===0
//    <h1>Hello Zyblog</h1>

});

第一個(gè)協(xié)程中,我們直接 GET 請(qǐng)求百度的首頁(yè),然后打印了返回頁(yè)面中的 title 標(biāo)簽里面的內(nèi)容以及響應(yīng)的頭部信息。

statusCode 屬性打印出來(lái)的請(qǐng)求狀態(tài)碼,也就是我們常見(jiàn)的 200 、404 這些。errCode 返回的是連接的錯(cuò)誤信息,這個(gè)和 TCP 客戶端是一樣的。

第二個(gè)協(xié)程中,我們使用的是 POST 的方式請(qǐng)求一個(gè)本地的數(shù)據(jù),同樣也是使用之前 【Swoole系列4.1】Swoole協(xié)程系統(tǒng)https://mp.weixin.qq.com/s/6VhloFezWEs5bEdMVRGiLA 中的協(xié)程 HTTP 服務(wù)端來(lái)測(cè)試的,這個(gè)也沒(méi)太多好說(shuō)的。

剩下的其實(shí)還有很多方法,在這里就不一一列舉了,包括設(shè)置請(qǐng)求頭,上傳下載文件之類的,反正常用的功能基本上都有了。

HTTP 客戶端使用的對(duì)象是 Swoole\Coroutine\Http\Client 對(duì)象,比上面的 TCP 客戶端多了一層命名空間。不過(guò)大家也都清楚,本身 HTTP 就是基于 TCP 的封裝。

FastCGI客戶端

FastCGI 是啥?怎么看著這么眼熟?

還沒(méi)想起來(lái)?傳統(tǒng)的 php-fpm 實(shí)現(xiàn)的通信協(xié)議呀!沒(méi)錯(cuò),我們可以直接去請(qǐng)求 php-fpm ,讓 Swoole 變成一個(gè)像 Nginx 一樣的前端多進(jìn)程服務(wù)器。

\Swoole\Coroutine\run(function(){
   echo \Swoole\Coroutine\FastCGI\Client::call(
       '127.0.0.1:9000'// FPM監(jiān)聽(tīng)地址, 也可以是形如 unix:/tmp/php-cgi.sock 的unixsocket地址
       '/home/www/4.Swoole協(xié)程/source/4.71fpm.php'// 想要執(zhí)行的入口文件
       ['name' => 'ZyBlog'// 附帶的POST信息
   );
});
// Hello ZyBlog

// 4.71fpm.php
<?php
echo 'Hello ' . ($_POST['name'] ?? 'no one');

好玩不,有意思不?當(dāng)然,你得在本地把 php-fpm 啟動(dòng)起來(lái)。它可以使用 9000 端口這種形式連接,也可以使用 UnixSocket 的方式。使用的對(duì)象是 \Swoole\Coroutine\FastCGI\Client 。

Socket

最后我們?cè)賮?lái)看一個(gè)今天的重點(diǎn)內(nèi)容,也就是 Socket 服務(wù)。不僅包括客戶端,還包括服務(wù)端。

其實(shí)之前,在 【Swoole系列4.1】Swoole協(xié)程系統(tǒng)https://mp.weixin.qq.com/s/6VhloFezWEs5bEdMVRGiLA 的時(shí)候,我們發(fā)現(xiàn)了 Swoole 并沒(méi)有提供現(xiàn)成的 UDP 協(xié)程服務(wù)端,所以我們就自己實(shí)現(xiàn)了一個(gè)。當(dāng)時(shí)使用的就是 Socket 相關(guān)的組件。

Socket 是更偏底層一些,但也是 TCP/UDP 這一層的,上面所有的其實(shí)最后都是基于 Socket 來(lái)實(shí)現(xiàn)的。包括 TCP/UDP 客戶端或服務(wù)端。如果你之前有過(guò) Socket 編程經(jīng)驗(yàn)的話,這一段就非常簡(jiǎn)單了。但如果你和我一樣,從來(lái)沒(méi)做過(guò) Socket 開(kāi)發(fā)的話,那么咱們還是好好一起看一下吧。具體的原理我就不多說(shuō)了,畢竟我們還是以框架的學(xué)習(xí)為主,而且即使要說(shuō),我也說(shuō)不出個(gè)所以然來(lái),因此,大家可以自己去查閱理詳細(xì)資料進(jìn)行深入的學(xué)習(xí)。

首先,我們起一個(gè) Socket 服務(wù)端,使用的是 Swoole\Coroutine\Socket 對(duì)象,這個(gè)對(duì)象里面包含的方法有些是客戶端的,有些是服務(wù)端的,有些是兩邊都可以使用的。

Swoole\Coroutine\run(function () {
    $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM);
    $socket->bind('0.0.0.0'9501);
    $socket->listen();

    while(1){
        $client = $socket->accept();
        if ($client !== false) {
            go(function () use ($client) {
                while (1) {
                    $data = $client->recv();
                    if($data == 'exit'){
                        echo "checkLiveness:";
                        var_dump($client->checkLiveness());
                        echo "isClosed:";
                        var_dump($client->isClosed());
                        $client->close();

                        echo "斷開(kāi)連接", PHP_EOL;
                        co::sleep(1);

                        echo "checkLiveness:";
                        var_dump($client->checkLiveness());
                        echo "isClosed:";
                        var_dump($client->isClosed());
                        break;
                    }else if ($data) {
                        $client->send("收到了客戶端:[{$client->fd}] 的數(shù)據(jù):" . $data);
                        var_dump($client->getsockname());
                        var_dump($client->getpeername());
                    }
                }
            });
        }
    }
});

和之前我們實(shí)現(xiàn)的 UDP 服務(wù)端差不多,只是把 SOCK_DGRAM 換成了 SOCK_STREAM 。簡(jiǎn)單地理解,SOCK_DGRAM 就是 UDP ,SOCK_STREAM 就是 TCP 。后面我們還會(huì)講到這兩塊的問(wèn)題,主要是解決 TCP數(shù)據(jù)包邊界 問(wèn)題。

實(shí)例化 Swoole\Coroutine\Socket 對(duì)象之后,我們就 bind() 一個(gè)端口,并啟動(dòng) listen() 進(jìn)行監(jiān)聽(tīng)。然后掛起腳本,通過(guò) accept() 對(duì)象獲得一個(gè)客戶端的連接,如果有連接來(lái)了,就啟動(dòng)一個(gè)協(xié)程開(kāi)始處理這個(gè)連接的請(qǐng)求。這幾個(gè)方法都是專門用于服務(wù)端的。

在處理協(xié)程中,我們判斷了一下發(fā)來(lái)的請(qǐng)求,如果是一個(gè) exit 字符串,就關(guān)閉連接,并且結(jié)束這個(gè)掛起協(xié)程。

剩下的就是一些方法的展示了,send() 和 recv() 發(fā)送與接收數(shù)據(jù)。checkLiveness() 檢查連接是否活躍,isClosed() 檢查連接是否關(guān)閉。getsockname() 獲得當(dāng)前端的 Socket 信息,getpeername() 獲得對(duì)端的 Socket 信息。

接下來(lái)就是客戶端。

\Swoole\Coroutine\run(function(){
   $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0);
   $socket->connect('127.0.0.1''9501');
   $socket->send("客戶端發(fā)來(lái)信息啦!");
   $data = $socket->recv();
   echo $data, PHP_EOL;
   var_dump($socket->getsockname());
   var_dump($socket->getpeername());

   co::sleep(2);

   $socket->send("客戶端發(fā)來(lái)第二條信息啦!");
   $data = $socket->recv();
   echo $data, PHP_EOL;

   co::sleep(2);

   var_dump($socket->isClosed());
   var_dump($socket->checkLiveness());

   $socket->send("exit");

   co::sleep(1);

   echo $socket->send("客戶端發(fā)來(lái)第三條信息啦!"), PHP_EOL;
   $data = $socket->recv();
   echo $data, PHP_EOL;

   var_dump($socket->isClosed());
   var_dump($socket->checkLiveness());

});

同樣地使用 Swoole\Coroutine\Socket 對(duì)象,但這里我們只需要使用一個(gè) connect() 來(lái)指定要連接的服務(wù)端就可以了。然后就馬上可以用 send() 和 recv() 進(jìn)行操作。輸出的結(jié)果是這樣的。

// [root@localhost source]# php 4.7協(xié)程TCP、UDP、HTTP客戶端.php
// 收到了客戶端:[6] 的數(shù)據(jù):客戶端發(fā)來(lái)信息啦!
// array(2) {
//   ["address"]=>
//   string(9) "127.0.0.1"
//   ["port"]=>
//   int(41458)
// }
// array(2) {
//   ["address"]=>
//   string(9) "127.0.0.1"
//   ["port"]=>
//   int(9501)
// }
// 收到了客戶端:[6] 的數(shù)據(jù):客戶端發(fā)來(lái)第二條信息啦!
// bool(false)
// bool(true)
// 36
// 
// bool(false)
// bool(false)

第一條發(fā)送并且獲得了響應(yīng),第二條也正常打印出來(lái)了,但第三條,我們先發(fā)送了一個(gè) exit ,然后再發(fā)送第三條數(shù)據(jù)信息。還記得上面我們?cè)诜?wù)端如果接收到了 exit 就會(huì)關(guān)閉連接吧,這時(shí)候連接已經(jīng)被關(guān)閉了,這邊也不會(huì)接收到什么消息內(nèi)容。

但是需要注意的是 isClosed() 在客戶端是沒(méi)什么效果的,一直返回的是 false ,而 checkLiveness() 是有效果的。那么 isClosed() 在服務(wù)端有效果嗎?

// [root@localhost source]# php 4.1Swoole協(xié)程服務(wù).php
// array(2) {
//   ["address"]=>
//   string(9) "127.0.0.1"
//   ["port"]=>
//   int(9501)
// }
// array(2) {
//   ["address"]=>
//   string(9) "127.0.0.1"
//   ["port"]=>
//   int(41458)
// }
// array(2) {
//   ["address"]=>
//   string(9) "127.0.0.1"
//   ["port"]=>
//   int(9501)
// }
// array(2) {
//   ["address"]=>
//   string(9) "127.0.0.1"
//   ["port"]=>
//   int(41458)
// }
// checkLiveness:bool(true)
// isClosed:bool(false)
// 斷開(kāi)連接
// checkLiveness:bool(false)
// isClosed:bool(true)

還好,這兩個(gè)方法在服務(wù)端都是生效的,可以清晰地看到它們的變化。

總結(jié)

今天的內(nèi)容很長(zhǎng),但其實(shí)就是幾段代碼比較長(zhǎng)而已,實(shí)際的內(nèi)容并不是特別多。除了這些客戶端之外,還可以實(shí)現(xiàn) WebSocket、HTTP2 客戶端,大家可以試試。另外還有 MySQL 和 Redis 客戶端,但現(xiàn)在已經(jīng)不推薦使用了,為什么呢?后面馬上就會(huì)講。

對(duì)于協(xié)程最基礎(chǔ)的組件我們就介紹的差不多了,最后下一篇,也是我們協(xié)程篇的最后一篇文章,我們來(lái)簡(jiǎn)單地說(shuō)說(shuō) 一鍵協(xié)程化 的問(wèn)題。

測(cè)試代碼:

https://github.com/zhangyue0503/swoole/blob/main/4.Swoole%E5%8D%8F%E7%A8%8B/source/4.7%E5%8D%8F%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%AE%A2%E6%88%B7%E7%AB%AF.php

參考文檔:

https://wiki./#/coroutine_client/init

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    久久99爱爱视频视频| 微拍一区二区三区福利| 国产精品乱子伦一区二区三区| 欧美一区二区三区五月婷婷| 国产精品成人免费精品自在线观看| 色综合视频一区二区观看| 色哟哟哟在线观看视频| 久久综合九色综合欧美| 免费在线观看欧美喷水黄片| 亚洲中文字幕人妻系列| 激情五月综五月综合网| 麻豆视传媒短视频免费观看| 国产精品一区二区视频大全| 国产剧情欧美日韩中文在线| 黄片免费在线观看日韩| 操白丝女孩在线观看免费高清| 一区二区三区18禁看| 国产免费无遮挡精品视频| 国产毛片对白精品看片| 人妻露脸一区二区三区| 日本加勒比不卡二三四区| 成人亚洲国产精品一区不卡| 国产一区二区三区四区中文| 精品久久久一区二区三| 国产视频在线一区二区| 国产精品亚洲二区三区| 99久久精品国产麻豆| 久久大香蕉精品在线观看 | 精品国产一区二区欧美| 国产成人精品午夜福利av免费| 中文字幕人妻av不卡| 午夜福利92在线观看| 国产又大又黄又粗的黄色| 又色又爽又黄的三级视频| 亚洲欧美一二区日韩高清在线 | 亚洲一区二区精品免费| 国产伦精品一一区二区三区高清版 | 亚洲一区二区三区三区| 久久re6热在线视频| 久久精品国产一区久久久| 高清不卡一卡二卡区在线|