上文说明了socket的原理和使用,衔接上文介绍一下stream_socket_xxx函数,且与socket_xxx函数的区别
目录
1.3 php实现stream_socket_xxx函数的RPC远程方法调用
二、socket_xxx函数与stream_socket_xxx函数的区别
一、stream_socket_xxx函数介绍
1.1 Streams的含义
流(Streams)它用于统一文件、网络、数据压缩等类文件操作方式,并为这些类文件操作提供一组通用的函数接口。在最简单的定义中,一个stream就是一个具有流式行为的资源对象。也就是说,它可以线性方式对stream进行读取或写入,并且可以用fseek()跳转到stream内的任意位置
流有点类似数据库抽象层,在数据库抽象层方面,不管使用何种数据库,在抽象层之上都使用相同的方式操作数据,
而流是对数据的抽象,它不管是本地文件还是远程文件还是压缩文件等等,只要来的是流式数据(是一组顺序、大量、快速、连续到达的数据序列,像流水一样来一点数据,处理一点,流式数据被封装成了byte流(其实也是二进制的)如果是全部收到数据以后再处理,那么延迟会很大,而且在很多场合会消耗大量内存),那么操作方式就是一样的
有了流这个概念就引申出了包装器wrapper这个概念
每一种流都实现了一个包装器(wrapper)类【即Stream 可以通过 <scheme>://<target> 方式来引用。其中<scheme>是包装类的名字,<target>中的内容是由包装类的语法指定,不同的包装类的语法会有所不同。】,包装器类包含一些额外的代码用来处理特殊的协议和编码。PHP提供了一些内置的包装器类,我们也可以很轻松的创建和注册自定义的包装器类。我们甚至容可以使用上下文(contexts)和过滤器来改变和增强包装器
PHP默认的包装类是file://,也就是说我们在访问文件系统的时候,其实就是在使用一个stream。我们可以通过下面两种方式来读取文件中的内容,readfile('/path/to/somefile.txt')或者readfile('file:///path/to/somefile.txt'),这两种方式是等效的。如果你是使用readfile('http://google.com/'),那么PHP会选取HTTP stream包装类来进行操作。
1.2 PHP中stream_socket_xxx的函数介绍
stream_socket_xxx常用函数:
stream_socket_client() - 打开Internet或Unix域套接字连接
stream_socket_server() -创建Internet或Unix域服务器套接字
stream_socket_accept — 接受由 stream_socket_server() 创建的套接字连接
stream_set_blocking() - 为资源流设置阻塞或者阻塞模式
stream_set_timeout() - 设置流的超时时间
stream_select() - 在tv_sec和tv_usec指定的超时时间内,对给定的流数组运行等效的select()系统调用
fgets() - 从文件指针中读取一行
fgetss() - 从文件指针中读取一行并过滤掉 HTML 标记
fwrite() - 写入文件(可安全用于二进制文件)
fread() - 读取文件(可安全用于二进制文件)
fclose() - 关闭一个已打开的文件指针
feof() - 测试文件指针是否到了文件结束的位置
1.3 php实现stream_socket_xxx函数的RPC远程方法调用
创建服务端代码 rpc_server.php
<?php
/*
* 服务端
*/
class RpcServer{
private $port = 0; // 监听端口号
private $host = ''; // IP
public function __construct($host, $port){
$this->host = $host;
$this->port = $port;
}
/**
* 运行, 监听端口并处理
*/
public function run(){
// 创建socket
$server = stream_socket_server("tcp://{$this->host}:{$this->port}");
if(empty($server)) throw new Exception('创建套接字失败');
// 监听
while (true){
$client = stream_socket_accept($server);
if(empty($client)) continue;
// 处理请求
$this->disposeClient($client);
fclose($client);
}
}
private function disposeClient($client){
$buf = fread($client, 4096);
$array = json_decode($buf, true);
// 创建对象并调用方法
$class = $array['class'] ?? '';
$method = $array['method'] ?? '';
$params = $array['params'] ?? [];
$instance = new $class();
$result = $instance->$method(...$params);
fwrite($client, json_encode($result));
}
}
// 测试调用类
class Test{
public function returnFunction(){
return 'connect_success';
}
public function add($a, $b){
return $a + $b;
}
}
(new RpcServer('127.0.0.1', 8888))
->run();
创建客户端代码 rpc_client.php
<?php
/*
* 客户端
*/
class RpcClient{
private $urlInfo = null;
private $className = '';
private function __construct($url, $className){
$this->urlInfo = parse_url($url);
$this->className = $className;
}
public static function getInstance($className){
return new RpcClient('127.0.0.1:8888', $className);
}
public function __call($name, $arguments){
// 创建客户端
$client = stream_socket_client("tcp://{$this->urlInfo['host']}:{$this->urlInfo['port']}");
if(empty($client)) return null;
// 发送数据
fwrite($client, json_encode([
'class' => $this->className,
'method' => $name,
'params' => $arguments,
]));
// 接收返回
$data = fread($client, 4096);
// 关闭客户端
fclose($client);
return json_decode($data, true);
}
}
$test = RpcClient::getInstance('Test');
echo $test->returnFunction();
echo PHP_EOL;
echo $test->add(2, 6);
echo PHP_EOL;
执行结果:
二、socket_xxx函数与stream_socket_xxx函数的区别
通过对比上篇文章socket_xxx函数的使用和stream_socket_xxx函数可以发现,stream_socket_xxx函数建立客户端或者服务的只需要一个函数即可,而socket_xxx函数需要执行几次函数,相对来说stream_socket_xxx函数是php自带的接口,所以更好用一些,而socket_xxx函数有点类似于C语言,更底层,所以用起来复杂一些。
1、socket_xxx函数需要安装扩展,stream_socket_xxx函数不需要安装扩展
2、socket_xxx函数可以比stream_socket_xxx函数设置的更精确