昨天我們編寫了協程的數據庫測試代碼, 幾天繼續擼 TCP.
我們先看一下php中pack
和unpack
這兩個函數:
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;
未完待續