0%

Swoole

胜,不妄喜;败,不遑馁;胸有激雷而面如平湖者,可拜上将军。 —— 春秋战国·孙武 《孙子兵法》

一、基础

      Swoole就是披着PHP外衣的C程序,类似于Java等语言里面的高阶功能,如持久连接、异步通信、Websocket、多线程等。

      Swoole是由C语言开发的PHP扩展类,由于有着C语言的优势,Swoole在内存管理、数据结构、通信协议解析等方面优势明显。它是一个面向生产环境的PHP异步网络通信引擎,使PHP开发人员可以编写高性能的异步并发TCP、UDP、Unix Socket、HTTP、WebSocket等服务。Swoole可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。

二、实战

  1. 安装

    • 下载源码包/usr/local/src目录下
    • 解压 sudo tar -zxvf swoole-4.4.3.tgz
    • 切换目录 cd swoole-4.4.3
    • 运行sudo /usr/local/php/7.3/bin/phpize命令,生成configure可执行文件
    • 运行配置sudo ./configure --with-php-config=/usr/local/php/7.3/bin/php-config
    • 安装扩展sudo make && sudo make install
      • 报错:configure: error: C compiler cannot create executables,解决sudo xcodebuild -license accept
    • 修改php.ini文件,增加extension=/path/swoole.so
    • 重启php-fpm
    • 查看是否安装成功php7.3 -m,显示swoole则成功
  2. 方法列表/your_path/swoole-4.4.3/ext-src/stubs/php_swoole_server.stub.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php
namespace Swoole {
final class Server {
public function __construct(string $host = '0.0.0.0', int $port = 0, int $mode = SWOOLE_BASE, int $sock_type = SWOOLE_SOCK_TCP) {}
public function __destruct() {}
public function set(array $settings): bool {}
public function on(string $event_name, callable $callback): bool {}
public function getCallback(string $event_name): \Closure|string|null|array {}
public function listen(string $host, int $port, int $sock_type): false|Server\Port {}
public function sendMessage(mixed $message, int $dst_worker_id): bool {}
public function addProcess(\Swoole\Process $process): int {}
public function addCommand(string $name, int $accepted_process_types, callable $callback): bool {}
public function start(): bool {}
public function stop(int $workerId = -1, bool $waitEvent = false): bool {}
public function send(int|string $fd, string $send_data, int $serverSocket = -1): bool {}
public function sendfile(int $conn_fd, string $filename, int $offset = 0, int $length = 0): bool {}
public function stats(): array {}
public function bind(int $fd, int $uid): bool {}
public function sendto(string $ip, int $port, string $send_data, int $server_socket = -1): bool {}
public function sendwait(int $conn_fd, string $send_data): bool {}
public function exists(int $fd): bool {}
public function protect(int $fd, bool $is_protected = true): bool {}
public function close(int $fd, bool $reset = false): bool {}
public function pause(int $fd): bool {}
public function resume(int $fd): bool {}
public function task(mixed $data, int $taskWorkerIndex = -1, ?callable $finishCallback = null): int|false {}
public function taskwait(mixed $data, float $timeout = 0.5, int $taskWorkerIndex = -1): mixed {}
public function taskWaitMulti(array $tasks, float $timeout = 0.5): false|array {}
public function taskCo(array $tasks, float $timeout = 0.5): array|false {}
public function finish(mixed $data): bool {}
public function reload(bool $only_reload_taskworker = false): bool {}
public function shutdown(): bool {}
public function heartbeat(bool $ifCloseConnection = true): false|array {}
public function command(string $name, int $process_id, int $process_type, mixed $data, bool $json_decode = true): false|string {}
public function getClientList(int $start_fd = 0, int $find_count = 10): false|array {}
public function getClientInfo(int $fd, int $reactor_id = -1, bool $ignoreError = false): false|array {}
public function getWorkerId(): int|false {}
public function getWorkerPid(int $worker_id = -1): int|false {}
public function getWorkerStatus(int $worker_id = -1): int|false {}
public function getManagerPid(): int {}
public function getMasterPid(): int {}
#ifdef SWOOLE_SOCKETS_SUPPORT
public function getSocket(int $port = 0): false|\Socket {}
#endif
public function getLastError(): int {}
}
}

namespace Swoole\Connection {
final class Iterator {
public function __construct() {}
public function __destruct() {}
public function rewind(): void {}
public function next(): void {}
public function current(): mixed {}
public function key(): mixed {}
public function valid(): bool {}
public function count(): int {}
public function offsetExists(mixed $fd): bool {}
public function offsetGet(mixed $fd): mixed {}
public function offsetSet(mixed $fd, mixed $value): void {}
public function offsetUnset(mixed $fd): void {}
}
}

namespace Swoole\Server {
final class Task {
public function finish(mixed $data): bool {}
public static function pack(mixed $data): string|false {}
public static function unpack(string $data): mixed {}
}
}
  1. 实战一:简易聊天室(面向过程)

    • server.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <?php   
    use Swoole\WebSocket\Server;

    // old version, unsupported
    // $ws_server = new swoole_websocket_server('192.168.1.169', 9502);
    $ws_server = new Server("0.0.0.0", 9501, SWOOLE_BASE);

    $ws_server->set([
    'daemonize' => true
    ]);

    $ws_server->on('open', function ($ws, $request) {
    // file_put_contents( __DIR__ .'/log.txt' , $request->fd);
    $ws->push($request->fd, "From Server!\n");
    });

    $ws_server->on('message', function ($ws, $frame) {
    pushMessage($ws, $frame);
    });

    $ws_server->on('close', function ($ws, $fd) {
    echo "client-{$fd} is closed\n";
    });

    $ws_server->start();

    function pushMessage($ws, $frame){
    // $data = $frame->data;
    // $msg = file_get_contents( __DIR__ .'/log.txt');
    // for ($i=1; $i<= $msg ; $i++) {
    // $ws->push($fd, $frame->fd . ':' . $data);
    // }
    foreach ($ws->connections as $fd) {
    $ws->push($fd, $frame->fd . ':' . $frame->data);
    }
    }
    • client.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <html>
    <head>
    <title></title>
    <meta charset="UTF-8">
    <script type="text/javascript">
    if(window.WebSocket){
    var webSocket = new WebSocket("ws://localhost:9501");
    webSocket.onopen = function (event) {
    webSocket.send("From Client!");
    };
    webSocket.onmessage = function (event) {
    var content = document.getElementById('content');
    content.innerHTML = content.innerHTML.concat(
    '<p style="margin-left:20px;height:20px;line-height:20px;">uid:'+event.data+'</p>'
    );
    }
    var sendMessage = function(){
    var data = document.getElementById('message').value;
    webSocket.send(data);
    }
    }else{
    console.log("您的浏览器不支持WebSocket");
    }
    </script>
    </head>
    <body>
    <div style="width:600px;margin:0 auto;border:1px solid #ccc;">
    <div id="content" style="overflow-y:auto;height:300px;"></div>
    <hr/>
    <div style="height:40px">
    <input type="text" id="message" style="margin-left:10px;height:25px;width:450px;">
    <button onclick="sendMessage()" style="height:28px;width:75px;">发送</button>
    </div>
    </div>
    </body>
    </html>
  2. 实战二:简易聊天室(面向对象)

    • server.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    <?php
    use Swoole\WebSocket\Server;

    class Chat
    {
    const HOST = '0.0.0.0';
    const PART = 9052;
    private $server = null;

    public function __construct() {
    // old version, unsupported
    // $this->server = new swoole_websocket_server(self::HOST, self::PART);
    $this->server = new Server("0.0.0.0", 9502, SWOOLE_BASE);

    // $ref = new ReflectionClass($this->server);
    // $data =$ref->getMethods();
    // file_put_contents("a.json", json_encode($data));
    // $data = $ref->getProperties();
    // file_put_contents("b.json", json_encode($data));

    $this->server->on('open', [$this, 'onOpen']);
    $this->server->on('message', [$this, 'onMessage']);
    $this->server->on('close', [$this, 'onClose']);
    $this->server->start();
    }

    public function onOpen($server, $request) {
    print_r($server->connection_info($request->fd));
    echo $request->fd . '连接了' . PHP_EOL;//打印到我们终端
    }

    public function onMessage($server, $frame) {
    echo $frame->fd . '来了,说:' . $frame->data . PHP_EOL;
    foreach ($server->connections as $fd) {
    $server->push($fd, json_encode(['no' => $frame->fd, 'msg' => $frame->data]));
    }
    echo "当前服务器共有".count($server->connections). "个连接\n";

    }

    public function onClose($server, $fd) {
    echo $fd . '走了' . PHP_EOL;//打印到我们终端
    }
    }

    $obj = new Chat();
    • client.html
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>聊天室</title>
    <script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
    </head>

    <body>

    <textarea class="log" style="width: 300px; height: 300px;">
    ===聊天室===
    </textarea><br/>
    <input type="button" value="连接" onClick="connect()">
    <input type="button" value="断开" onClick="disconnect()">
    <input type="input" id="text">
    <input type="button" value="发送" onClick="send()">

    <script>
    function connect() {
    var url = 'ws://localhost:9502';
    socket = new WebSocket(url);
    socket.onopen = function () {
    log1('连接成功')
    }
    socket.onmessage = function (msg) {
    log(msg.data);
    }
    socket.onclose = function () {
    log1('断开连接')
    }
    }

    function disconnect() {
    socket.close();
    socket = null;
    }

    function log1(var1) {
    $('.log').append(var1 + '\r\n');
    }
    function log(var1) {
    var v = $.parseJSON(var1)
    $('.log').append('uid:'+v['no']+',msg:'+v['msg']+ '\r\n');
    }

    function send() {
    // var json = $('#text').val();
    var json = JSON.stringify({
    'type': 'php',
    'msg': $('#text').val()
    })
    console.log(json)
    socket.send(json);
    }
    </script>
    </body>
    </html>
  3. 常见问题

三、参考

  1. 参考一
  2. 参考二
  3. 参考三