Coroutine\Client 提供了 TCP、UDP、unixSocket 传输协议的 Socket 客户端封装代码,使用时仅需 new Swoole\Coroutine\Client 即可。
实现原理
Coroutine\Client 的所有涉及网络请求的方法,Swoole 都会进行协程调度,业务层无需感知
使用方法和 Client 同步模式方法完全一致
connect 超时设置同时作用于 Connect、Recv 和 Send 超时
继承关系
Coroutine\Client 与 Client 并不是继承关系,但 Client 提供的方法均可在 Coroutine\Client 中使用。请参考 Swoole\Client,在此不再赘述 。
在 Coroutine\Client 中可以使用 set 方法设置配置选项,使用方法和与 Client->set 完全一致,对于使用有区别的函数,在 set() 函数小节会单独说明
使用示例
use Swoole\Coroutine\Client;
use function Swoole\Coroutine\run;
run(function () {
$client = new Client(SWOOLE_SOCK_TCP);
if (!$client->connect('127.0.0.1', 9501, 0.5))
{
echo "connect failed. Error: {$client->errCode}\n";
}
$client->send("hello world\n");
echo $client->recv();
$client->close();
});协议处理
协程客户端也支持长度和 EOF 协议处理,设置方法与 Swoole\Client 完全一致。
$client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); $client->set(array( 'open_length_check' => true, 'package_length_type' => 'N', 'package_length_offset' => 0, //第N个字节是包长度的值 'package_body_offset' => 4, //第几个字节开始计算长度 'package_max_length' => 2000000, //协议最大长度 ));
连接到远程服务器。
Swoole\Coroutine\Client->connect(string $host, int $port, float $timeout = 0.5): bool
参数
功能:网络 IO 的超时时间;包括 connect/send/recv,超时发生时,连接会被自动 close,参考客户端超时规则
值单位:秒【支持浮点型,如 1.5 表示 1s+500ms】
默认值:0.5s
其它值:无
功能:远程服务器端口
默认值:无
其它值:无
功能:远程服务器的地址【底层会自动进行协程切换解析域名为 IP 地址】
默认值:无
其它值:无
string $host
int $port
float $timeout
提示
如果连接失败,会返回 false
超时后返回,检查 $cli->errCode 为 110
失败重试
connect 连接失败后,不可直接进行重连。必须使用 close 关闭已有 socket,然后再进行 connect 重试。
//连接失败
if ($cli->connect('127.0.0.1', 9501) == false) {
//关闭已有socket
$cli->close();
//重试
$cli->connect('127.0.0.1', 9501);
}示例
if ($cli->connect('127.0.0.1', 9501)) {
$cli->send('data');
} else {
echo 'connect failed.';
}
if ($cli->connect('/tmp/rpc.sock')) {
$cli->send('data');
} else {
echo 'connect failed.';
}返回 Client 的连接状态
Swoole\Coroutine\Client->isConnected(): bool
返回值
isConnected 方法返回的是应用层状态,只表示 Client 执行了 connect 并成功连接到了 Server,并且没有执行 close 关闭连接。Client 可以执行 send、recv、close 等操作,但不能再次执行 connect 。
这不代表连接一定是可用的,当执行 send 或 recv 时仍然有可能返回错误,因为应用层无法获得底层 TCP 连接的状态,执行 send 或 recv 时应用层与内核发生交互,才能得到真实的连接可用状态。
返回 false,表示当前未连接到服务器
返回 true,表示当前已连接到服务器
发送数据。
Swoole\Coroutine\Client->send(string $data): int|bool
参数
功能:为发送的数据,必须为字符串类型,支持二进制数据
默认值:无
其它值:无
string $data
发送成功返回写入 Socket 缓存区的字节数,底层会尽可能地将所有数据发出。如果返回的字节数与传入的 $data 长度不同,可能是 Socket 已被对端关闭,再下一次调用 send 或 recv 时将返回对应的错误码。
发送失败返回 false,可以使用 $client->errCode 获取错误原因。
recv 方法用于从服务器端接收数据。
Swoole\Coroutine\Client->recv(float $timeout = 0): string|bool
参数
设置超时,优先使用指定的参数,其次使用 set 方法中传入的 timeout 配置。发生超时的错误码为 ETIMEDOUT
功能:设置超时时间
值单位:秒【支持浮点型,如 1.5 表示 1s+500ms】
默认值:参考客户端超时规则
其它值:无
float $timeout
返回值
设置了通信协议,recv 会返回完整的数据,长度受限于 package_max_length
未设置通信协议,recv 最大返回 64K 数据
未设置通信协议返回原始的数据,需要 PHP 代码中自行实现网络协议的处理
recv 返回空字符串表示服务端主动关闭连接,需要 close
recv 失败,返回 false,检测 $client->errCode 获取错误原因,处理方式可参考下文的完整示例
关闭连接。
close 不存在阻塞,会立即返回。关闭操作没有协程切换。
Swoole\Coroutine\Client->close(): bool
窥视数据。
peek 方法直接操作 socket,因此不会引起协程调度。
Swoole\Coroutine\Client->peek(int $length = 65535): string
提示
peek 方法仅用于窥视内核 socket 缓存区中的数据,不进行偏移。使用 peek 之后,再调用 recv 仍然可以读取到这部分数据
peek 方法是非阻塞的,它会立即返回。当 socket 缓存区中有数据时,会返回数据内容。缓存区为空时返回 false,并设置 $client->errCode
连接已被关闭 peek 会返回空字符串
设置客户端参数。
Swoole\Coroutine\Client->set(array $settings): bool
配置参数
请参考 Swoole\Client 。
和 Swoole\Client 的差异
协程客户端提供了更细粒度的超时控制。可以设置:
timeout:总超时,包括连接、发送、接收所有超时
connect_timeout:连接超时
read_timeout:接收超时
write_timeout:发送超时
参考客户端超时规则
示例
use Swoole\Coroutine\Client;
use function Swoole\Coroutine\run;
run(function () {
$client = new Client(SWOOLE_SOCK_TCP);
$client->set(array(
'timeout' => 0.5,
'connect_timeout' => 1.0,
'write_timeout' => 10.0,
'read_timeout' => 0.5,
));
if (!$client->connect('127.0.0.1', 9501, 0.5))
{
echo "connect failed. Error: {$client->errCode}\n";
}
$client->send("hello world\n");
echo $client->recv();
$client->close();
});use Swoole\Coroutine\Client;
use function Swoole\Coroutine\run;
run(function () {
$client = new Client(SWOOLE_SOCK_TCP);
if (!$client->connect('127.0.0.1', 9501, 0.5)) {
echo "connect failed. Error: {$client->errCode}\n";
}
$client->send("hello world\n");
while (true) {
$data = $client->recv();
if (strlen($data) > 0) {
echo $data;
$client->send(time() . PHP_EOL);
} else {
if ($data === '') {
// 全等于空 直接关闭连接
$client->close();
break;
} else {
if ($data === false) {
// 可以自行根据业务逻辑和错误码进行处理,例如:
// 如果超时时则不关闭连接,其他情况直接关闭连接
if ($client->errCode !== SOCKET_ETIMEDOUT) {
$client->close();
break;
}
} else {
$client->close();
break;
}
}
}
\Co::sleep(1);
}
});