swoole 学习大纲

发布时间 2023-07-16 22:12:12作者: 心随所遇


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();