系统相关 API
的协程封装。此模块在 v4.4.6
正式版本后可用。大部分 API
基于 AIO
线程池实现。
v4.4.6
以前的版本,请使用 Co
短名或 Swoole\Coroutine
调用,如: Co::sleep
或 Swoole\Coroutine::sleep
v4.4.6
及以后版本官方推荐使用Co\System::sleep
或 Swoole\Coroutine\System::sleep
此修改旨在规范命名空间,但同时也保证向下兼容 (也就是说 v4.4.6
版本以前的写法也是可以的,无需修改)
获取文件系统信息。
Swoole 版本 >= v4.2.5 可用
Swoole\Coroutine\System::statvfs(string $path): array|false
参数
功能:文件系统挂载的目录【如 /
,可以使用 df 和 mount -l
命令获取】
默认值:无
其它值:无
string $path
使用示例
Swoole\Coroutine\run(function () { var_dump(Swoole\Coroutine\System::statvfs('/')); }); // array(11) { // ["bsize"]=> // int(4096) // ["frsize"]=> // int(4096) // ["blocks"]=> // int(61068098) // ["bfree"]=> // int(45753580) // ["bavail"]=> // int(42645728) // ["files"]=> // int(15523840) // ["ffree"]=> // int(14909927) // ["favail"]=> // int(14909927) // ["fsid"]=> // int(1002377915335522995) // ["flag"]=> // int(4096) // ["namemax"]=> // int(255) // }
协程方式读取文件。
Swoole\Coroutine\System::fread(resource $handle, int $length = 0): string|false
v4.0.4
以下版本 fread
方法不支持非文件类型的 stream
,如 STDIN
、Socket
,请勿使用 fread
操作此类资源。v4.0.4
以上版本 fread
方法支持了非文件类型的 stream
资源,底层会自动根据 stream
类型选择使用 AIO
线程池或 EventLoop 实现。
参数
功能:读取的长度【默认为 0
,表示读取文件的全部内容】
默认值:0
其它值:无
功能:文件句柄【必须是 fopen
打开的文件类型 stream
资源】
默认值:无
其它值:无
resource $handle
int $length
返回值
读取成功返回字符串内容,读取失败返回 false
使用示例
$fp = fopen(__FILE__, "r"); Swoole\Coroutine\run(function () use ($fp) { $r = Swoole\Coroutine\System::fread($fp); var_dump($r); });
协程方式向文件写入数据。
Swoole\Coroutine\System::fwrite(resource $handle, string $data, int $length = 0): int|false
v4.0.4
以下版本 fwrite
方法不支持非文件类型的 stream
,如 STDIN
、Socket
,请勿使用 fwrite
操作此类资源。v4.0.4
以上版本 fwrite
方法支持了非文件类型的 stream
资源,底层会自动根据 stream
类型选择使用 AIO
线程池或 EventLoop 实现。
参数
功能:读取的长度【默认为 0
,表示写入 $data
的全部内容,$length
必须小于 $data
的长度】
默认值:0
其它值:无
功能:要写入的数据内容【可以是文本或二进制数据】
默认值:无
其它值:无
功能:文件句柄【必须是 fopen
打开的文件类型 stream
资源】
默认值:无
其它值:无
resource $handle
string $data
int $length
返回值
写入成功返回数据长度,读取失败返回 false
使用示例
$fp = fopen(__DIR__ . "/test.data", "a+"); Swoole\Coroutine\run(function () use ($fp) { $r = Swoole\Coroutine\System::fwrite($fp, "hello world\n", 5); var_dump($r); });
协程方式按行读取文件内容。
底层使用了 php_stream
缓存区,默认大小为 8192
字节,可使用 stream_set_chunk_size
设置缓存区尺寸。
Swoole\Coroutine\System::fgets(resource $handle): string|false
fgets
函数仅可用于文件类型的 stream
资源,Swoole 版本 >= v4.4.4
可用
参数
功能:文件句柄【必须是 fopen
打开的文件类型 stream
资源】
默认值:无
其它值:无
resource $handle
返回值
读取到 EOL
(\r
或 \n
)将返回一行数据,包括 EOL
未读取到 EOL
,但内容长度超过 php_stream
缓存区 8192
字节,将返回 8192
字节的数据,不包含 EOL
达到文件末尾 EOF
时,返回空字符串,可用 feof
判断文件是否已读完
读取失败返回 false
,使用 swoole_last_error 函数获取错误码
使用示例
$fp = fopen(__DIR__ . "/defer_client.php", "r"); Swoole\Coroutine\run(function () use ($fp) { $r = Swoole\Coroutine\System::fgets($fp); var_dump($r); });
协程方式读取文件。
Swoole\Coroutine\System::readFile(string $filename): string|false
参数
功能:文件名
默认值:无
其它值:无
string $filename
返回值
读取成功返回字符串内容,读取失败返回 false
,可使用 swoole_last_error 获取错误信息
readFile
方法没有尺寸限制,读取的内容会存放在内存中,因此读取超大文件时可能会占用过多内存
使用示例
$filename = __DIR__ . "/defer_client.php"; Swoole\Coroutine\run(function () use ($filename) { $r = Swoole\Coroutine\System::readFile($filename); var_dump($r); });
协程方式写入文件。
Swoole\Coroutine\System::writeFile(string $filename, string $fileContent, int $flags): bool
参数
功能:写入的选项【默认会清空当前文件内容,可以使用 FILE_APPEND
表示追加到文件末尾】
默认值:无
其它值:无
功能:写入到文件的内容【最大可写入 4M
】
默认值:无
其它值:无
功能:文件名【必须有可写权限,文件不存在会自动创建。打开文件失败会立即返回 false
】
默认值:无
其它值:无
string $filename
string $fileContent
int $flags
返回值
写入成功返回 true
写入失败返回 false
使用示例
$filename = __DIR__ . "/defer_client.php"; Swoole\Coroutine\run(function () use ($filename) { $w = Swoole\Coroutine\System::writeFile($filename, "hello swoole!"); var_dump($w); });
进入等待状态。
相当于 PHP
的 sleep
函数,不同的是 Coroutine::sleep
是协程调度器实现的,底层会 yield
当前协程,让出时间片,并添加一个异步定时器,当超时时间到达时重新 resume
当前协程,恢复运行。
使用 sleep
接口可以方便地实现超时等待功能。
Swoole\Coroutine\System::sleep(float $seconds): void
参数
功能:睡眠的时间【必须大于 0
,最大不得超过一天时间(86400
秒)】
值单位:秒,最小精度为毫秒(0.001
秒)
默认值:无
其它值:无
float $seconds
使用示例
$server = new Swoole\Http\Server("127.0.0.1", 9502); $server->on('Request', function($request, $response) { //等待200ms后向浏览器发送响应 Swoole\Coroutine\System::sleep(0.2); $response->end("<h1>Hello Swoole!</h1>"); }); $server->start();
Swoole\Coroutine\System::exec(string $cmd): array
参数
功能:要执行的 shell
指令
默认值:无
其它值:无
string $cmd
返回值
执行失败返回 false
,执行成功返回数组,包含了进程退出的状态码、信号、输出内容。
array( 'code' => 0, // 进程退出的状态码 'signal' => 0, // 信号 'output' => '', // 输出内容 );
使用示例
Swoole\Coroutine\run(function() { $ret = Swoole\Coroutine\System::exec("md5sum ".__FILE__); });
将域名解析为 IP。基于同步的线程池模拟实现,底层自动进行协程调度。
Swoole\Coroutine\System::gethostbyname(string $domain, int $family = AF_INET, float $timeout = -1): string|false
参数
功能:超时时间
值单位:秒,最小精度为毫秒(0.001
秒)
默认值:-1
其它值:无
功能:域族【AF_INET
表示返回 IPv4
地址,使用 AF_INET6
时返回 IPv6
地址】
默认值:AF_INET
其它值:AF_INET6
功能:域名
默认值:无
其它值:无
string $domain
int $family
float $timeout
返回值
成功返回域名对应的 IP
地址,失败返回 false
,可使用 swoole_last_error 获取错误信息
array( 'code' => 0, // 进程退出的状态码 'signal' => 0, // 信号 'output' => '', // 输出内容 );
扩展
超时控制
$timeout
参数可以控制协程等待的超时时间,在规定的时间内未返回结果,协程会立即返回 false
并继续向下执行。底层实现中会将该异步任务标记为 cancel
,gethostbyname
还是会在 AIO
线程池中继续执行。
可修改 /etc/resolv.conf
设置 gethostbyname
和 getaddrinfo
底层 C
函数的超时时间。具体请参考 设置 DNS 解析超时和重试
使用示例
Swoole\Coroutine\run(function () { $ip = Swoole\Coroutine\System::gethostbyname("www.baidu.com", AF_INET, 0.5); echo $ip; });
进行 DNS 解析,查询域名对应的 IP
地址。
与 gethostbyname
不同,getaddrinfo
支持更多参数设置,而且会返回多个 IP
结果。
Swoole\Coroutine\System::getaddrinfo(string $domain, int $family = AF_INET, int $socktype = SOCK_STREAM, int $protocol = STREAM_IPPROTO_TCP, string $service = null, float $timeout = -1): array|false
参数
功能:超时时间
值单位:秒,最小精度为毫秒(0.001
秒)
默认值:-1
其它值:无
功能:
默认值:无
其它值:无
功能:协议
默认值:STREAM_IPPROTO_TCP
其它值:STREAM_IPPROTO_UDP
、STREAM_IPPROTO_STCP
、STREAM_IPPROTO_TIPC
、0
功能:协议类型
默认值:SOCK_STREAM
其它值:SOCK_DGRAM
、SOCK_RAW
功能:域族【AF_INET
表示返回 IPv4
地址,使用 AF_INET6
时返回 IPv6
地址】
默认值:无
其它值:无
功能:域名
默认值:无
其它值:无
string $domain
int $family
其他参数设置请参考 man getaddrinfo
文档
int $socktype
int $protocol
string $service
float $timeout
返回值
成功返回多个 IP
地址组成的数组,失败返回 false
使用示例
Swoole\Coroutine\run(function () { $ips = Swoole\Coroutine\System::getaddrinfo("www.baidu.com"); var_dump($ips); });
域名地址查询。
与 Coroutine\System::gethostbyname
不同,Coroutine\System::dnsLookup
是直接基于 UDP
客户端网络通信实现的,而不是使用 libc
提供的 gethostbyname
函数。
Swoole 版本 >= v4.4.3
可用,底层会读取 /etc/resolve.conf
获取 DNS
服务器地址,目前仅支持 AF_INET(IPv4)
域名解析。Swoole 版本 >= v4.7
时可使用第三个参数来支持 AF_INET6(IPv6)
Swoole\Coroutine\System::dnsLookup(string $domain, float $timeout = 5, int $type = AF_INET): string|false
参数
$type
参数在 Swoole 版本 >= v4.7
可用。
值单位:秒,最小精度为毫秒(0.001
秒)
默认值:AF_INET
其它值:AF_INET6
功能:超时时间
值单位:秒,最小精度为毫秒(0.001
秒)
默认值:5
其它值:无
功能:域名
默认值:无
其它值:无
string $domain
float $timeout
int $type
返回值
解析成功返回对应的 IP 地址
失败返回 false
,可以使用 swoole_last_error 获取错误信息
常见错误
SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED
:此域名无法解析,查询失败
SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT
:解析超时,DNS 服务器可能存在故障,无法在规定的时间内返回结果
使用示例
Swoole\Coroutine\run(function () { $ip = Swoole\Coroutine\System::dnsLookup("www.baidu.com"); echo $ip; });
对应原有的 Process::wait,不同的是此 API 是协程版本,会造成协程挂起,可替换 Swoole\Process::wait
和 pcntl_wait
函数。
Swoole 版本 >= v4.5.0
可用
Swoole\Coroutine\System::wait(float $timeout = -1): array|false
参数
功能:超时时间,负数表示永不超时
值单位:秒,最小精度为毫秒(0.001
秒)
默认值:-1
其它值:无
float $timeout
返回值
操作成功会返回一个数组包含子进程的 PID
、退出状态码、被哪种信号 KILL
失败返回 false
每个子进程启动后,父进程必须都要派遣一个协程调用 wait()
(或 waitPid()
) 进行回收,否则子进程会变成僵尸进程,会浪费操作系统的进程资源。
如果使用了协程,必须是先创建进程,进程里面再开协程。而不是反过来,否则就是带着协程 fork 情况会非常复杂,导致底层很难处理。
示例
use Swoole\Coroutine; use Swoole\Coroutine\System; use Swoole\Process; $process = new Process(function () { echo 'Hello Swoole'; }); $process->start(); Coroutine\run(function () use ($process) { $status = System::wait(); assert($status['pid'] === $process->pid); var_dump($status); });
和上述 wait 方法基本一致,不同的是此 API 可以指定等待特定的进程
Swoole 版本 >= v4.5.0
可用
Swoole\Coroutine\System::waitPid(int $pid, float $timeout = -1): array|false
参数
功能:超时时间,负数表示永不超时
值单位:秒,最小精度为毫秒(0.001
秒)
默认值:-1
其它值:无
功能:进程 id
默认值:-1
(表示任意进程,此时等价于 wait 方法)
其它值:任意自然数
int $pid
float $timeout
返回值
操作成功会返回一个数组包含子进程的 PID
、退出状态码、被哪种信号 KILL
失败返回 false
每个子进程启动后,父进程必须都要派遣一个协程调用 wait()
(或 waitPid()
) 进行回收,否则子进程会变成僵尸进程,会浪费操作系统的进程资源。
示例
use Swoole\Coroutine; use Swoole\Coroutine\System; use Swoole\Process; $process = new Process(function () { echo 'Hello Swoole'; }); $process->start(); Coroutine\run(function () use ($process) { $status = System::waitPid($process->pid); var_dump($status); });
协程版本的信号监听器,会阻塞当前协程直到信号触发,可替换 Swoole\Process::signal
和 pcntl_signal
函数。
Swoole 版本 >= v4.5.0
可用
Swoole\Coroutine\System::waitSignal(int $signo, float $timeout = -1): bool
参数
功能:超时时间,负数表示永不超时
值单位:秒,最小精度为毫秒(0.001
秒)
默认值:-1
其它值:无
功能:信号类型
默认值:无
其它值:SIG 系列常量,如 SIGTERM
, SIGKILL
等
int $signo
float $timeout
返回值
收到信号返回 true
超时未收到信号返回 false
示例
use Swoole\Coroutine; use Swoole\Coroutine\System; use Swoole\Process; $process = new Process(function () { Coroutine\run(function () { $bool = System::waitSignal(SIGUSR1); var_dump($bool); }); }); $process->start(); sleep(1); $process::kill($process->pid, SIGUSR1);
协程版本的信号监听器,会阻塞当前协程直到信号触发。等待 IO 事件,可替换 swoole_event
相关函数。
Swoole 版本 >= v4.5
可用
Swoole\Coroutine\System::waitEvent(mixed $socket, int $events = SWOOLE_EVENT_READ, float $timeout = -1): int | false
参数
功能:超时时间,负数表示永不超时
值单位:秒,最小精度为毫秒(0.001
秒)
默认值:-1
其它值:无
功能:事件类型
默认值:SWOOLE_EVENT_READ
其它值:SWOOLE_EVENT_WRITE
或 SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE
功能:文件描述符 (任何可以转化为 fd 的类型,如 Socket 对象,资源等等)
默认值:无
其它值:无
mixed $socket
int $events
float $timeout
返回值
返回触发的事件类型的和 (可能是多个位), 和参数 $events
传入的值有关
失败返回 false
,可以使用 swoole_last_error 获取错误信息
示例
同步阻塞的代码通过该 API 即可变为协程非阻塞
use Swoole\Coroutine; Coroutine\run(function () { $client = stream_socket_client('tcp://www.qq.com:80', $errno, $errstr, 30); $events = Coroutine::waitEvent($client, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); assert($events === SWOOLE_EVENT_WRITE); fwrite($client, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); $events = Coroutine::waitEvent($client, SWOOLE_EVENT_READ); assert($events === SWOOLE_EVENT_READ); $response = fread($client, 8192); echo $response; });