swool 分为异步和协程两种风格。
异步风格:实例化服务器对象,设置参数,绑定事件,最后 start() 启动服务开始监听。
Swoole\Server: 所有服务器的基类
Swoole\Server::__construct(string $host = '0.0.0.0', int $port = 0, int $mode = SWOOLE_BASE, int $sockType = SWOOLE_SOCK_TCP)
$mode: 分为 SWOOLE_BASE:基本模式, SWOOLE_PROCESS 多进程模式
$sockType: 连接类型,主要有 SWOOLE_TCP, SWOOLE_UDP, SWOOLE_TCP6, SWOOLE_UDP6, SWOOLE_UNIX_DGRAM, SWOOLE_UNIX_STREAM
Swoole\Http\Swoole\Server: HTTP 服务器,继承 Swoole\Server
Swoole\Http\Swoole\Server::__construct(string $host = '0.0.0.0', int $port = 0, int $mode = SWOOLE_BASE, int $sockType = SWOOLE_SOCK_TCP)
Swoole\WebSocket\Server : WebSocket服务器,继承 Swoole\Http\Swoole\Server
Swoole\WebSocket\Server::__construct(string $host = '0.0.0.0', int $port = 0, int $mode = SWOOLE_BASE, int $sockType = SWOOLE_SOCK_TCP)
它们构造方法都是一样的,只是有些限定了有些参数可选择性,比如 Swoole\Http\Swoole\Server 的 $sockType 不能使用 SWOOLE_UDP, 另外就是能够绑定的事件有所不同。
Server 服务器:
$server = new Swoole\Server("127.0.0.1", 9501);
$server->on('connect', function ($server, $fd){
echo "Client:Connect.\n";
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
$server->send($fd, 'Swoole: '.$data);
$server->close($fd);
});
$server->on('close', function ($server, $fd) {
echo "Client: Close.\n";
});
$server->start();
HTTP 服务器: 同 Swoole\Server 相比, 不接受 onConnect/onReceive 回调设置
$http = new Swoole\Http\Server("127.0.0.1", 9501);
$http->on('request', function ($request, $response) {
$response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>");
});
$http->start();
WebSocket 服务器:
$ws = new Swoole\WebSocket\Server('0.0.0.0', 9502);
$ws->on('Open', function ($ws, $request) {
$ws->push($request->fd, "hello, welcome\n");
});
$ws->on('Message', function ($ws, $frame) {
echo "Message: {$frame->data}\n";
$ws->push($frame->fd, "server: {$frame->data}");
});
$ws->on('Close', function ($ws, $fd) {
echo "client-{$fd} is closed\n";
});
$ws->start();
总结: 可见这种风格基本上非常好理解, 就像写js 绑定事件的一样。
协程风格
直接手写协程,大概如下:
// 短命名风格,swoole 配置 swoole.use_shortname='On' 时,有效。 先创建协程容器,然后在容器内部创建子协程
Co\run(function(){
go(function(){ }); //创建子协程
go(function(){ });//创建子协程
});
// 正常写法
Swoole\Coroutine\run(function(){
Swoole\Coroutine::create(function(){ });
Swoole\Coroutine::create(function(){ });
});
创建协程风格服务器:大概就是代码写在协程容器中。
构造方法:
Swoole\Coroutine\Server::__construct(string $host, int $port = 0, bool $ssl = false, bool $reuse_port = false)
$reuse_port 是否可以重复启动监听同一个端口。
Swoole\Coroutine\Http\Server::__construct($host, $port = null, $ssl = null, $reuse_port = null)
创建协程风格 HTTP 服务器
Swoole\Coroutine\run(function () {
$server = new \Swoole\Coroutine\Http\Server('127.0.0.1', 9502, false);
$server->handle('/', function ($request, $response) {
$response->end("<h1>Index</h1>");
});
$server->handle('/test', function ($request, $response) {
$response->end("<h1>Test</h1>");
});
$server->handle('/stop', function ($request, $response) use ($server) {
$response->end("<h1>Stop</h1>");
$server->shutdown();
});
$server->start();
});
创建协程风格 HTTP 服务器,带有 websocket 功能
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\WebSocket\CloseFrame;
use function Swoole\Coroutine\run;
run(function () {
$server = new \Swoole\Coroutine\Http\Server('127.0.0.1', 9502, false);
$server->handle('/', function (Request $request, Response $response) {
$response->end(
<<<HTML
<h1>Swoole WebSocket Server</h1>
<script>
var wsServer = 'ws://127.0.0.1:9502/websocket';
var websocket = new WebSocket(wsServer);
websocket.onopen = function (evt) {
console.log("Connected to WebSocket server.");
websocket.send('hello');
};
websocket.onclose = function (evt) {
console.log("Disconnected");
};
websocket.onmessage = function (evt) {
console.log('Retrieved data from server: ' + evt.data);
};
websocket.onerror = function (evt, e) {
console.log('Error occured: ' + evt.data);
};
</script>
HTML
);
});
$server->handle('/websocket', function (Request $request, Response $response) {
$response->upgrade(); // 发送握手成功信息
while (true) {
$frame = $response->recv($timeout = 10); // 接收 WebSocket 消息, 返回: Swoole\WebSocket\Frame | false | string
if ($frame === '') {
$response->close();
break;
} else if ($frame === false) { // recv(10) 设置了10秒过期所以10秒没收到消息,走到这里
echo 'errorCode: ' . swoole_last_error() . "\n";
$response->close();
break;
}else if ($frame->data == 'close' || get_class($frame) === CloseFrame::class) {
$response->close();
break;
}
$response->push("Hello {$frame->data}!");
$response->push("How are you, {$frame->data}?");
}
});
$server->start();
});
测试效果:
创建协程风格多进程的 Server 服务器
<?php
use Swoole\Process;
use Swoole\Coroutine;
use Swoole\Coroutine\Server\Connection;
$pool = new Process\Pool(2); //多进程管理模块, 启动两个进程
$pool->set(['enable_coroutine' => true]); //启用携程, 每个进程的 OnWorkerStart 回调都自动创建一个协程
$pool->on('workerStart', function ($pool, $id) {
$server = new \Swoole\Coroutine\Server('127.0.0.1', 9501, false, true);//每个进程都监听9501端口
//收到15信号关闭服务, 这里得向父进程发送信号才能停止,而子进程终止又会被重建
Process::signal(SIGTERM, function () use ($server) {
$server->shutdown();
});
//设置连接处理函数. 根据配置, 每当接收到新的连接自动动创建一个协程
$server->handle(function (Connection $conn) {
while (true) {
$data = $conn->recv($timeout = 5); // 超过5秒,没收到消息,就断开连接
if ($data === '' || $data === false) {
$errCode = swoole_last_error();
$errMsg = socket_strerror($errCode);
echo "errCode: {$errCode}, errMsg: {$errMsg}\n";
$conn->close();
break;
}
//发送数据
$conn->send('hello ' . $data);
Coroutine::sleep(1);
}
});
//开始监听端口
$server->start();
});
$pool->start();