TCP 异步风格服务器
异步风格服务器通过监听事件的方式来编写程序。当对应的事件发生时底层会主动回调指定的函数。
由于默认开启协程化,在回调函数内部会自动创建协程,遇到 IO 会产生协程调度,异步风格服务器无法保证调度顺序,所以在遇到并发时无法保证事件执行顺序。
# server.php
// 创建 TCP 服务器对象,监听 0.0.0.0:9501端口
$serv = new Swoole\Server("0.0.0.0", 9501);
// 设置服务器运行参数
$serv->set(array(
'daemonize' => 1, // 作为守护进程运行,需同时设置log_file
'log_file' => '/www/logs/swoole.log', // 指定标准输出和错误日志写入的文件
));
// 监听连接进入事件
// $fd 为客户端连接的唯一标识符
$serv->on('Connect', function ($serv, $fd) {
echo "Client: Connect: {$fd} .\n";
});
// 监听数据接收事件
$serv->on('Receive', function ($serv, $fd, $from_id, $data) {
// 向指定的客户端连接发送数据
$serv->send($fd, "Server: " . $data);
});
// 监听连接关闭事件
$serv->on('Close', function ($serv, $fd) {
echo "Client: Close: {$fd} .\n";
});
// 监听服务器正常关闭事件
// 使用 kill -15 发送 SIGTREM 信号到主进程正常关闭时触发
// kill -9 或 Ctrl+C 不会触发该事件
$serv->on('Shutdown', function ($serv) {
echo "服务器正常关闭.\n";
});
// 启动服务器
$serv->start();
运行并测试 TCP 异步风格服务器
# 如果程序已经运行,先结束进程
kill -9 11590
# 在 cli 命令行环境运行服务端
php server.php
# 查看服务器监听的端口
netstat -an | grep 9501
# 使用Telnet测试连接服务端
telnet 127.0.0.1 9501
# 发送数据
hello
# 接收数据
Server: hello
TCP 协程风格服务器
协程风格服务器处理连接的过程是完全同步的,程序可以顺序处理 Connect
、Receive
、Close
事件,可以保证事件执行顺序。
# server.php
// 在进程池中创建两个进程
$pool = new Swoole\Process\Pool(2);
// 让每个 OnWorkerStart 回调都自动创建一个协程
$pool->set(['enable_coroutine' => true]);
// 进程绑定工作进程启动事件
$pool->on('workerStart', function ($pool, $id) {
// 每个进程都监听9501端口,不使用ssl,开启端口重用
$server = new Swoole\Coroutine\Server('0.0.0.0', '9501', false, true);
// 接收 kill -15 信号关闭服务器
Swoole\Process::signal(SIGTERM, function () use ($server) {
echo "服务器正常关闭.\n";
$server->shutdown();
});
// 设置连接处理函数,接收到新的连接请求并自动创建一个协程并执行回调函数
// 回调函数会在协程空间中执行
$server->handle(function(Swoole\Coroutine\Server\Connection $conn) {
echo "Client: Connect.\n";
// 循环接收和返回数据
while (true) {
// 接收数据
$data = $conn->recv();
if (empty($data)) {
// 关闭连接
$conn->close();
break;
}
//发送数据
$conn->send("Server: " . $data);
\Co::sleep(1);
}
});
// 启动服务器
$server->start();
});
$pool->start();
TCP 同步阻塞客户端
// 同步阻塞客户端可以用于 PHP-FPM 环境下
$client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
if (!$client->connect('127.0.0.1', 9501, -1)) {
exit("connect failed. Error: {$client->errCode}\n");
}
$client->send("hello world\n");
// 同步阻塞等待服务端返回内容
echo $client->recv();
$client->close();
TCP 协程客户端
// 设置要 Hook 的函数的范围
Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]);
// 协程客户端,底层自动使用协程调度实现异步IO,用于代替异步客户端
Co\run(function(){
$client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP);
$client->set(array(
'timeout' => 1.5, //总超时,包括连接、发送、接收所有超时
'connect_timeout' => 1.0, //连接超时,会覆盖第一个总的 timeout
'write_timeout' => 2.0, //发送超时,会覆盖第一个总的 timeout
'read_timeout' => 0.5, //接收超时,会覆盖第一个总的 timeout
));
if (!$client->connect('127.0.0.1', 9501, 0.5)) {
exit("connect failed. Error: {$client->errCode}\n");
}
$client->send("hello world.\n");
echo $client->recv();
$client->close();
});