目录
【了解Swoole】
【PHP中使用Swoole案例演示】
安装Swoole扩展
Swoole实现TCP请求
Swoole实现UDP请求
Swoole实现HTTP请求
Swoole实现WebSocket聊天室功能
Swoole执行异步任务 (Task)
Swoole实现Redis服务器
PHPStorm中添加swoole智能提示
为什么要学习使用swoole,首先说说PHP存在的缺陷:
Swoole官网: Swoole - PHP 协程框架 是这么说明的:
Swoole 使 PHP 开发人员可以编写高性能高并发的 TCP、UDP、Unix Socket、HTTP、 WebSocket 等服务,让 PHP 不再局限于 Web 领域。Swoole4 协程的成熟将 PHP 带入了前所未有的时期, 为性能的提升提供了独一无二的可能性。Swoole 可以广泛应用于互联网、移动通信、云计算、 网络游戏、物联网(IOT)、车联网、智能家居等领域。使用 PHP + Swoole 可以使企业 IT 研发团队的效率大大提升,更加专注于开发创新产品。
Swoole的特性:
Swoole的优点:
下载swoole,进入swoole目录,编译安装:
phpize
./configure
make && make install
然后给php.ini加入swoole.so
查看是否编译成功:php -m, 或者 php --ri swoole
查看phpinfo()效果如下:
Swoole源码包里面 examples/server 目录下有个 eho.php,运行:php echo.php
使用 netstat -anp | grep 9501 查看端口情况(Mac不能带参数p,或者用 lsof -i:9501)
官网资料:https://wiki.swoole.com/#/start/start_tcp_server
server = new Swoole\Server("127.0.0.1", 9501);
$this->server->set([
'worker_num' => 4,
'max_request' => 50,
]);
$this->server->on('Connect', [$this, "onConnect"]);
$this->server->on('Receive', [$this, "onReceive"]);
$this->server->on('Close', [$this, "onClose"]);
//启动服务器
$this->server->start();
}
public function onConnect($server, $fd) {
echo "客户端id: {$fd} 链接.\n";
}
public function onReceive($server, $fd, $from_id, $data) {
$server->send($fd, "发送的数据:" . $data);
}
public function onClose($server, $fd) {
echo "客户端id: {$fd}关闭.\n";
}
}
new TCP();
- 服务器可以同时被成千上万个客户端连接,$fd 就是客户端连接的唯一标识符。
- 调用 $server->send() 方法向客户端连接发送数据,参数就是 $fd 客户端标识符。
- 调用 $server->close() 方法可以强制关闭某个客户端连接。
- 客户端可能会主动断开连接,此时会触发 onClose 事件回调。
官网资料:https://wiki.swoole.com/#/start/start_udp_server
server = new Swoole\Server('127.0.0.1', 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP);
$this->server->set([
'worker_num' => 4,
'max_request' => 50,
]);
$this->server->on('Packet', [$this, "onPacket"]);
//启动服务器
$this->server->start();
}
public function onPacket($server, $data, $clientInfo) {
var_dump($clientInfo);
$server->sendto($clientInfo['address'], $clientInfo['port'], "Server:{$data}");
}
}
new UDP();
UDP 服务器与 TCP 服务器不同,UDP 没有连接的概念。启动 Server 后,客户端无需 Connect,直接可以向 Server 监听的 9502 端口发送数据包。对应的事件为 onPacket。
- $clientInfo 是客户端的相关信息,是一个数组,有客户端的 IP 和端口等内容。
- 调用 $server->sendto 方法向客户端发送数据。
如何理解TCP和UDP:TCP 是面向连接的、可靠的、只支持点对点通信;UDP 是无连接的、不可靠的、支持一对一、一对多、多对一、多对多的通信模式。
TCP就像是两个人打电话,你必须听清楚对方讲的什么才能知道回复什么;而UDP就像是马路上的广播,它不会在乎你有没有听到,错过就是错过了。
官网资料:https://wiki.swoole.com/#/start/start_http_server
http = new Swoole\Http\Server('0.0.0.0', 9503);
$this->http->set([
'enable_static_handler' => true,
'document_root' => "./static",
]);
$this->http->on('Request', [$this, "onRequest"]);
//启动服务器
$this->http->start();
}
public function onRequest($request, $response) {
var_dump($request->get, $request->post);
$response->header('Content-Type', 'text/html; charset=utf-8');
$response->end('Hello Swoole.' . json_encode($request->get));
}
}
new HTTP();
通过浏览器访问http根目录,并且指定参数:
通过浏览器直接访问静态资源html:
HTTP 服务器只需要关注请求响应即可,所以只需要监听一个 onRequest 事件。当有新的 HTTP 请求进入就会触发此事件。事件回调函数有 2 个参数,一个是 $request 对象,包含了请求的相关信息,如 GET/POST 请求的数据。另外一个是 response 对象,对 request 的响应可以通过操作 response 对象来完成。$response->end() 方法表示输出一段 HTML 内容,并结束此请求。
- 0.0.0.0 表示监听所有 IP 地址,一台服务器可能同时有多个 IP,如 127.0.0.1 本地回环 IP、192.168.1.100 局域网 IP、210.127.20.2 外网 IP,这里也可以单独指定监听一个 IP;
- 9501 监听的端口,如果被占用程序会抛出致命错误,中断执行。
官网资料:https://wiki.swoole.com/#/start/start_ws_server
http = new Swoole\WebSocket\Server('0.0.0.0', 9502, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
$this->http = new Swoole\WebSocket\Server('0.0.0.0', 9502);
$this->http->set([
//下面的部分也是用来配置https的ssl证书的
'ssl_cert_file' => "",
'ssl_key_file' => "",
'enable_static_handler' => true,
'document_root' => "./static",
]);
$this->http->on('Open', [$this, "onOpen"]);
$this->http->on('Message', [$this, "onMessage"]);
$this->http->on('Close', [$this, "onClose"]);
//启动服务器
$this->http->start();
}
public function onOpen($ws, $request) {
$ws->push($request->fd, "hello,welcome\n");
}
public function onMessage($ws, $frame) {
echo "Message: {$frame->data}\n";
foreach ($ws->connections as $fd) {
if ($fd == $frame->fd) {
$ws->push($fd, "我: {$frame -> data}");
} else {
$ws->push($fd, "对方:{$frame -> data}");
}
}
}
public function onClose($ws, $fd) {
echo "client:{$fd} is closed\n";
}
}
new WS();
客户端JS代码:
var wsServer = 'ws://127.0.0.1:9502';
var websocket = new WebSocket(wsServer);
websocket.onopen = function (res) {
$("#welcome").append(
"连接成功!欢迎
"
);
};
websocket.onclose = function (res) {
$("#message").append(
"" + res.data + "
"
);
};
websocket.onmessage = function (res) {
$("#message").append(
"" + res.data + "
"
);
};
websocket.onerror = function (res, e) {
$("#message").append(
"" + res + "
"
);
};
function send() {
websocket.send($("#input").val());
}
如何理解WebSocket:是一种基于 TCP 的轻量级网络通信协议,在地位上是与 HTTP“平级”的。HTTP 难以应用在动态页面、即时消息、网络游戏等要求“实时通信”的领域;WebSocket 客户端和服务器都可以随时向对方发送数据。
- 客户端向服务器端发送信息时,服务器端触发 onMessage 事件回调。
- 服务器端可以调用 $server->push() 向某个客户端(使用 $fd 标识符)发送消息。
官网资料:https://wiki.swoole.com/#/start/start_task
set([
// 'worker_num' => 1,
//如果要使用 Task ,需要先设置 task_worker_num ,它代表的是开启的 Task 进程数量。
'task_worker_num' => 4,
]);
$http->on('Request', function ($request, $response) use ($http) {
echo "接收到了请求", PHP_EOL;
$response->header('Content-Type', 'text/html; charset=utf-8');
$http->task("发送邮件");
$http->task("发送广播");
$http->task("执行队列");
$response->end('Hello Swoole. #' . rand(1000, 9999) . '
');
});
//处理异步任务(此回调函数在task进程中执行)
//Task 事件是用于处理任务的,可以根据传递过来的 $data 内容进行处理。
$http->on('Task', function ($serv, $task_id, $reactor_id, $data) {
$sec = rand(1, 5);
echo "New AsyncTask[id={$task_id}] sleep sec: {$sec}" . PHP_EOL;
sleep($sec);
//返回任务执行的结果
$serv->finish("{$data} -> OK");
});
//处理异步任务的结果(此回调函数在worker进程中执行)
//Finish 事件是监听任务结束,当执行的任务结束后,就会调用这个事件回调,可以进行后续的处理。如果你的任务没有后续的处理,那么我们也可以不去监听这个事件。
$http->on('Finish', function ($serv, $task_id, $data) {
echo "AsyncTask[{$task_id}] Finish: {$data}" . PHP_EOL;
});
echo "服务启动", PHP_EOL;
$http->start();
官网资料:https://wiki.swoole.com/#/redis_server
//使用 setHandler() 方法来监听 Reids 命令,在这里我们看到了熟悉的 get、set 等命令的定义。
$server->setHandler('GET', function ($fd, $data) use ($server) {
if (count($data) == 0) {
return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command"));
}
$key = $data[0];
//指定了 $server->data ,可以将它看成是一个数据源,直接使用的就是一个文件,
//直接在当前测试环境目录下创建一个叫做 db 的空文件就可以了。
if (empty($server->data[$key])) {
//使用 send() 方法来返回响应的命令信息,并通过 format() 方法格式化返回的响应数据。
return $server->send($fd, Server::format(Server::NIL));
} else {
return $server->send($fd, Server::format(Server::STRING, $server->data[$key]));
}
});
以上内容完整代码参考: https://gitee.com/rxbook/thinkphp-demo-2023/tree/master/swoole_test1
备注,默认情况下PHPStorm中是不提示Swoole相关函数信息的,比如下面这样:
如果要给PHPStorm中添加swoole智能提示,方法如下:
下载函数库 git clone https://github.com/eaglewu/swoole-ide-helper.git
加载方式1: 右键External Libraries,选择Configure PHP Include Path, 选择下载好的swoole-ide-helper目录,点击确定, 只提供给本项目使用。
加载方式2: 将代码包含到PhpStorm的Settings->Languages & Frameworks->PHP->Include path里面, 提供给本机使用。