昨天我们编写了协程的数据库测试代码, 几天继续撸 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;
未完待续