swoole學習之: 協程初探(三) TCP測試

昨天我們編寫了協程的數據庫測試代碼, 幾天繼續擼 TCP.

我們先看一下php中packunpack這兩個函數:

pack 將數據打包成二進制字符串

pack( string $format[, mixed $args[, mixed $...]] ) : string

unpack 從二進制字符串對數據進行解包

unpack( string $format, string $data[, int $offset = 0] ) : array

下面的例子中, 會使用'n'作爲參數 format, 表示無符號短整型(16位,大端字節序)

$data = 'aben';
$packed = pack('n', strlen($data)) . $data;
var_dump($packed);
$unpacked = unpack('n', $packed);
var_dump($unpacked);

執行結果:

好了, 正式開始tcp操作.

use Swoole\Runtime;
use Swoole\Coroutine;
use function Swoole\Coroutine\run;

//開啓協程. 自動將文件操作,sleep,mysqli,pdo,streams等都變成異步IO
Runtime::enableCoroutine();

$time_start = microtime(true);

run(function(){
    // region 測試tcp連接12.8k次

    function tcp_pack(string $data) : string
    {
        return pack('n', strlen($data)) . $data; //將數據打包成二進制字符串. 格式'n'表示無符號短整型(16位,大端字節序)
    }
    function tcp_length(string $head) : int
    {
        return unpack('n', $head)[1];//將二進制字符串解包, 取數組下標1, 表示長度
    }

    Coroutine::create(function(){
        //創建並返回一個資源流上下文
       $ctx = stream_context_create([
           'socket'=>[
                    'so_reuseaddr'=>true,//讓端口釋放後立即就可以被再次使用
                    'backlog'=>128//累計未完成的待處理事件
               ]
       ]);
       //創建一個socket服務
       $socket = stream_socket_server(
         'tcp://10.0.2.7:81',
         $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx
       );
       if(!$socket){
           echo $errstr.'('.$errno.')'.PHP_EOL;
       }else{
           $i = 0;
           //接受由 stream_socket_server() 創建的套接字連接
           while($conn = stream_socket_accept($socket, 1)){
               stream_set_timeout($conn, 5);
               for($n = 100; $n--;){//100
                   //接收的數據
                   $data = fread($conn, tcp_length(fread($conn, 2)));
                   echo 'SERVER RECEIVED: '.$data.PHP_EOL;
                   assert($data == 'Hello Swoole Server #'.$n);//如果服務器返回的文字等於xxx, 則中斷執行
                   //給客戶端返回信息, 告知客戶端循環的序號
                   fwrite($conn, tcp_pack('Hello Swoole Client #'.$n));
               }
               if( ++ $i == 128){//128
                   fclose($socket);
                   break;
               }
           }
       }
    });
    for($c = 128; $c--;){
        Coroutine::create(function(){
            //打開一個socket連接
            $fp = stream_socket_client('tcp://10.0.2.7:81', $errno, $errstr, 1);
            if(!$fp){
                echo $errstr.'('.$errno.')'.PHP_EOL;
            }else{
                stream_set_timeout($fp, 5);
                for($n = 100; $n --;){
                    //發送
                    fwrite($fp, tcp_pack('Hello Swoole Server #'.$n));
                    //接收數據
                    $data = fread($fp, tcp_length(fread($fp, 2)));
                    echo 'CLIENT RECEIVED: '.$data.PHP_EOL;
                    assert($data == 'Hello Swoole Client #'.$n);
                }
                fclose($fp);
            }
        });
    }

    //endregion

});

$time_end = microtime(true);
echo 'Time elapsed: '.($time_end - $time_start).' s'.PHP_EOL;

未完待續

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章