资讯专栏INFORMATION COLUMN

Swoole 多协议 多端口 的应用

wenshi11019 / 2201人阅读

摘要:概述这是关于学习的第五篇文章多协议多端口的应用。网络通信协议设计多端口监听的使用希望通过我提供的,能够对文档有更加深刻的理解。支持了种类型的自定义网络通信协议结束符协议固定包头包体协议。

概述

这是关于 Swoole 学习的第五篇文章:Swoole 多协议 多端口 的应用。

第四篇:Swoole HTTP 的应用

第三篇:Swoole WebSocket 的应用

第二篇:Swoole Task 的应用

第一篇:Swoole Timer 的应用

主要参考官方的这两篇文章,进行实现的 Demo。

网络通信协议设计:https://wiki.swoole.com/wiki/...

多端口监听的使用:https://wiki.swoole.com/wiki/...

希望通过我提供的 Demo,能够对文档有更加深刻的理解。

网络通信协议设计

为什么需要通信协议?

官方:TCP协议在底层机制上解决了UDP协议的顺序和丢包重传问题。但相比UDP又带来了新的问题,TCP协议是流式的,数据包没有边界。应用程序使用TCP通信就会面临这些难题。因为TCP通信是流式的,在接收1个大数据包时,可能会被拆分成多个数据包发送。多次Send底层也可能会合并成一次进行发送。这里就需要2个操作来解决:分包 和 合包,所以TCP网络通信时需要设定通信协议。

Swoole 支持了2种类型的自定义网络通信协议 :EOF结束符协议、固定包头+包体协议。

EOF结束符协议

先看下,未设置协议的效果:

发送的每条数据长度都是 23,但在 onReceive 接收数据的时候每次接收的长度不一样,并没有按照想象的方式进行分包。

再看下,设置了EOF结束符协议的效果:

发送的每条数据长度都是 23,在 onReceive 接收数据的时候每次接收的也是 23 ,完美。

主要设置项如下:

"package_max_length" => "8192",
"open_eof_split"     => true,
"package_eof"        => "
"

不做解释,官方文档已经写的很清楚。

示例代码如下:

server.php

serv = new swoole_server("0.0.0.0", 9501);
        $this->serv->set([
            "worker_num"      => 2, //开启2个worker进程
            "max_request"     => 4, //每个worker进程 max_request设置为4次
            "dispatch_mode"   => 2, //数据包分发策略 - 固定模式

            //EOF结束符协议
            "package_max_length" => "8192",
            "open_eof_split"     => true,
            "package_eof"        => "
"
        ]);

        $this->serv->on("Start", [$this, "onStart"]);
        $this->serv->on("Connect", [$this, "onConnect"]);
        $this->serv->on("Receive", [$this, "onReceive"]);
        $this->serv->on("Close", [$this, "onClose"]);

        $this->serv->start();
    }

    public function onStart($serv) {
        echo "#### onStart ####".PHP_EOL;
        echo "SWOOLE ".SWOOLE_VERSION . " 服务已启动".PHP_EOL;
        echo "swoole_cpu_num:".swoole_cpu_num().PHP_EOL;
        echo "master_pid: {$serv->master_pid}".PHP_EOL;
        echo "manager_pid: {$serv->manager_pid}".PHP_EOL;
        echo "########".PHP_EOL.PHP_EOL;
    }

    public function onConnect($serv, $fd) {
        echo "#### onConnect ####".PHP_EOL;
        echo "客户端:".$fd." 已连接".PHP_EOL;
        echo "########".PHP_EOL.PHP_EOL;
    }

    public function onReceive($serv, $fd, $from_id, $data) {
        echo "#### onReceive ####".PHP_EOL;
        var_dump($data);
    }

    public function onClose($serv, $fd) {
        echo "Client Close.".PHP_EOL;
    }
}

$server = new Server();

client.php

client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);

        $this->client->on("Connect", [$this, "onConnect"]);
        $this->client->on("Close", [$this, "onClose"]);
        $this->client->on("Error", [$this, "onError"]);
    }

    public function connect() {
        if(!$fp = $this->client->connect("127.0.0.1", 9501)) {
            echo "Error: {$fp->errMsg}[{$fp->errCode}]".PHP_EOL;
            return;
        }
    }

    public function onConnect() {

        fwrite(STDOUT, "发送测试数据(Y or N):");
        swoole_event_add(STDIN, function() {
            $msg = trim(fgets(STDIN));
            if ($msg == "y") {
                $this->send();
            }
            fwrite(STDOUT, "发送测试数据(Y or N):");
        });
    }

    public function send() {
        $msg_info =  "客户端发信息...
";

        $i = 0;
        while ($i < 50) {
            var_dump($msg_info);
            $this->client->send($msg_info);
            $i++;
        }
    }

    public function onClose() {
        echo "Client close connection".PHP_EOL;
    }

    public function onError() {

    }
}

$client = new Client();
$client->connect();
固定包头+包体协议

先看下,未设置协议的效果:

很明显,在 onReceive 接收到的数据,是少的。

再看下,设置协议的效果:

主要设置项如下:

"open_length_check"     => true,
"package_max_length"    => "8192",
"package_length_type"   => "N",
"package_length_offset" => "0",
"package_body_offset"   => "4",

不做解释,官方文档已经写的很清楚。

示例代码如下:

server.php

serv = new swoole_server("0.0.0.0", 9501);
        $this->serv->set([
            "worker_num"      => 2, //开启2个worker进程
            "max_request"     => 4, //每个worker进程 max_request设置为4次
            "dispatch_mode"   => 2, //数据包分发策略 - 固定模式

            //固定包头+包体协议
            "open_length_check"     => true,
            "package_max_length"    => "8192",
            "package_length_type"   => "N",
            "package_length_offset" => "0",
            "package_body_offset"   => "4",
        ]);

        $this->serv->on("Start", [$this, "onStart"]);
        $this->serv->on("Connect", [$this, "onConnect"]);
        $this->serv->on("Receive", [$this, "onReceive"]);
        $this->serv->on("Close", [$this, "onClose"]);

        $this->serv->start();
    }

    public function onStart($serv) {
        echo "#### onStart ####".PHP_EOL;
        echo "swoole_cpu_num:".swoole_cpu_num().PHP_EOL;
        echo "SWOOLE ".SWOOLE_VERSION . " 服务已启动".PHP_EOL;
        echo "master_pid: {$serv->master_pid}".PHP_EOL;
        echo "manager_pid: {$serv->manager_pid}".PHP_EOL;
        echo "########".PHP_EOL.PHP_EOL;
    }

    public function onConnect($serv, $fd) {
        echo "#### onConnect ####".PHP_EOL;
        echo "客户端:".$fd." 已连接".PHP_EOL;
        echo "########".PHP_EOL.PHP_EOL;
    }

    public function onReceive($serv, $fd, $from_id, $data) {
        echo "#### onReceive ####".PHP_EOL;
        $length = unpack("N", $data)[1];
        echo "Length:".$length.PHP_EOL;
        $msg = substr($data, -$length);
        echo "Msg:".$msg.PHP_EOL;
    }

    public function onClose($serv, $fd) {
        echo "Client Close.".PHP_EOL;
    }
}

$server = new Server();

client.php

client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);

        $this->client->on("Connect", [$this, "onConnect"]);
        $this->client->on("Close", [$this, "onClose"]);
        $this->client->on("Error", [$this, "onError"]);
    }

    public function connect() {
        if(!$fp = $this->client->connect("127.0.0.1", 9501, 1)) {
            echo "Error: {$fp->errMsg}[{$fp->errCode}]".PHP_EOL;
            return;
        }
    }

    public function onConnect() {

        fwrite(STDOUT, "发送测试数据(Y or N):");
        swoole_event_add(STDIN, function() {
            $msg = trim(fgets(STDIN));
            if ($msg == "y") {
                $this->send();
            }
            fwrite(STDOUT, "发送测试数据(Y or N):");
        });
    }

    public function send() {
        $msg = "客户端发的信息...";
        $msg_info = pack("N", strlen($msg)).$msg;

        $i = 0;
        while ($i < 50) {
            var_dump($msg_info);
            $this->client->send($msg_info);
            $i++;
        }
    }

    public function onClose() {
        echo "Client close connection".PHP_EOL;
    }

    public function onError() {

    }
}

$client = new Client();
$client->connect();
多端口监听的使用

上图,是示例代码中的端口监听:

9501 onMessage 处理 WebSocket。

9501 onRequest 处理 HTTP。

9502 onReceive 处理 TCP。

9503 onPacket 处理 UDP。

不多说,看下效果图:

示例代码如下:

server.php

serv = new swoole_websocket_server("0.0.0.0", 9501);
        $this->serv->set([
            "worker_num"      => 2, //开启2个worker进程
            "max_request"     => 4, //每个worker进程 max_request设置为4次
            "task_worker_num" => 4, //开启4个task进程
            "dispatch_mode"   => 4, //数据包分发策略 - IP分配
            "daemonize"       => false, //守护进程(true/false)
        ]);

        $this->serv->on("Start", [$this, "onStart"]);
        $this->serv->on("Open", [$this, "onOpen"]);
        $this->serv->on("Message", [$this, "onMessage"]);
        $this->serv->on("Request", [$this, "onRequest"]);
        $this->serv->on("Close", [$this, "onClose"]);
        $this->serv->on("Task", [$this, "onTask"]);
        $this->serv->on("Finish", [$this, "onFinish"]);

        //监听 9502 端口
        $tcp = $this->serv->listen("0.0.0.0", 9502, SWOOLE_SOCK_TCP);
        $tcp->set([
            "worker_num"      => 2, //开启2个worker进程
            "max_request"     => 4, //每个worker进程 max_request设置为4次
            "dispatch_mode"   => 2, //数据包分发策略 - 固定模式

            //固定包头+包体协议
            "open_length_check"     => true,
            "package_max_length"    => "8192",
            "package_length_type"   => "N",
            "package_length_offset" => "0",
            "package_body_offset"   => "4",
        ]);
        $tcp->on("Receive", [$this, "onReceive"]);

        //监听 9503 端口
        $udp = $this->serv->listen("0.0.0.0", 9503, SWOOLE_SOCK_UDP);
        $udp->set([
            "worker_num"      => 2, //开启2个worker进程
            "max_request"     => 4, //每个worker进程 max_request设置为4次
            "dispatch_mode"   => 2, //数据包分发策略 - 固定模式
        ]);
        $udp->on("Packet", [$this, "onPacket"]);

        $this->serv->start();
    }

    public function onStart($serv) {
        echo "#### onStart ####".PHP_EOL;
        echo "SWOOLE ".SWOOLE_VERSION . " 服务已启动".PHP_EOL;
        echo "master_pid: {$serv->master_pid}".PHP_EOL;
        echo "manager_pid: {$serv->manager_pid}".PHP_EOL;
        echo "########".PHP_EOL.PHP_EOL;
    }

    public function onOpen($serv, $request) {
        echo "#### onOpen ####".PHP_EOL;
        echo "server: handshake success with fd{$request->fd}".PHP_EOL;
        $serv->task([
            "type" => "login"
        ]);
        echo "########".PHP_EOL.PHP_EOL;
    }

    public function onTask($serv, $task_id, $from_id, $data) {
        echo "#### onTask ####".PHP_EOL;
        echo "#{$serv->worker_id} onTask: [PID={$serv->worker_pid}]: task_id={$task_id}".PHP_EOL;
        $msg = "";
        switch ($data["type"]) {
            case "login":
                $msg = "我来了...";
                break;
            case "speak":
                $msg = $data["msg"];
                break;
        }
        foreach ($serv->connections as $fd) {
            $connectionInfo = $serv->connection_info($fd);
            if (isset($connectionInfo["websocket_status"]) && $connectionInfo["websocket_status"] == 3) {
                $serv->push($fd, $msg); //长度最大不得超过2M
            }
        }
        $serv->finish($data);
        echo "########".PHP_EOL.PHP_EOL;
    }

    public function onFinish($serv,$task_id, $data) {
        echo "#### onFinish ####".PHP_EOL;
        echo "Task {$task_id} 已完成".PHP_EOL;
        echo "########".PHP_EOL.PHP_EOL;
    }

    public function onClose($serv, $fd) {
        echo "#### onClose ####".PHP_EOL;
        echo "client {$fd} closed".PHP_EOL;
        echo "########".PHP_EOL.PHP_EOL;
    }

    public function onMessage($serv, $frame) {
        echo "#### onMessage ####".PHP_EOL;
        echo "receive from fd{$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}".PHP_EOL;
        $serv->task(["type" => "speak", "msg" => $frame->data]);
        echo "########".PHP_EOL.PHP_EOL;
    }

    public function onRequest($request, $response) {
        echo "#### onRequest ####".PHP_EOL;
        $response->header("Content-Type", "text/html; charset=utf-8");
        $server = $request->server;
        $path_info    = $server["path_info"];
        $request_uri  = $server["request_uri"];

        echo "PATH_INFO:".$path_info.PHP_EOL;

        if ($path_info == "/favicon.ico" || $request_uri == "/favicon.ico") {
            return $response->end();
        }

        $html = "

你好 Swoole.

"; $response->end($html); } public function onReceive($serv, $fd, $from_id, $data) { echo "#### onReceive ####".PHP_EOL; $length = unpack("N", $data)[1]; echo "Length:".$length.PHP_EOL; $msg = substr($data, -$length); echo "Msg:".$msg.PHP_EOL; } public function onPacket($serv, $data, $clientInfo) { echo "#### onPacket ####".PHP_EOL; $serv->sendto($clientInfo["address"], $clientInfo["port"], "Server ".$data); var_dump($clientInfo); } } $server = new Server();

4 个客户端连接的代码分别是:

1、9501 onMessage 处理 WebSocket。可以参考原来文章 Swoole WebSocket 的应用 中的代码即可。

2、9501 onRequest 处理 HTTP。可以参考原来文章 Swoole HTTP 的应用 中的代码即可。

3、9502 onReceive 处理 TCP。可以参考原来文章 Swoole Task 的应用 中的代码即可。

4、9503 onPacket 处理 UDP。

示例代码:

netcat -u 10.211.55.4 9503
小结

一、多端口的应用场景是什么?

比如,开发一个直播网站,直播用一个端口,IM聊天用一个端口。

比如,开发一个RPC服务,数据通讯用一个端口,统计界面用一个端口。

推荐阅读

系统的讲解 - SSO 单点登录

系统的讲解 - PHP WEB 安全防御

系统的讲解 - PHP 缓存技术

系统的讲解 - PHP 接口签名验证

系统的讲解 - PHP 浮点数高精度运算

本文欢迎转发,转发请注明作者和出处,谢谢!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/31480.html

相关文章

  • swoolefy-基于swoole扩展实现高性能常驻内存型API和Web应用服务框架

    摘要:是一个基于扩展实现的轻量级高性能的常驻内存型的和应用服务框架高度封装了,,服务器,以及基于实现可扩展的服务,同时支持包方式安装部署项目。基于实用,抽象事件处理类,实现与底层的回调的解耦,支持同步异步调用,内置等常用组件等。 swoolefy swoolefy是一个基于swoole扩展实现的轻量级高性能的常驻内存型的API和Web应用服务框架,高度封装了http,websocket,ud...

    lewinlee 评论0 收藏0
  • Swoole源码研究】浅析swoole中server实现

    摘要:的部分是基于以及协议的。例如父进程向中写入子进程从中读取子进程向中写入父进程从中读取。默认使用对进程进行分配交给对应的线程进行监听线程收到某个进程的数据后会进行处理值得注意的是这个线程可能并不是发送请求的那个线程。 作者:施洪宝 一. 基础知识 1.1 swoole swoole是面向生产环境的php异步网络通信引擎, php开发人员可以利用swoole开发出高性能的server服务。...

    rainyang 评论0 收藏0
  • Swoole MySQL 连接池实现

    摘要:概述这是关于入门学习的第八篇文章连接池的实现。开始今天的文章,这篇文章实现了连接池,代码是在的实现文章的基础上进行开发的。 概述 这是关于 Swoole 入门学习的第八篇文章:Swoole MySQL 连接池的实现。 第七篇:Swoole RPC 的实现 第六篇:Swoole 整合成一个小框架 第五篇:Swoole 多协议 多端口 的应用 第四篇:Swoole HTTP 的应用 第三...

    13651657101 评论0 收藏0
  • Swoole RPC 实现

    摘要:概述这是关于学习的第七篇文章的实现。还有一些大佬加了微信,可能是出于对晚辈的提携吧,偷偷告诉你,从大佬的朋友圈能学到很多东西。就到这了,上面的需要源码的,加我微信。 概述 这是关于 Swoole 学习的第七篇文章:Swoole RPC 的实现。 第六篇:Swoole 整合成一个小框架 第五篇:Swoole 多协议 多端口 的应用 第四篇:Swoole HTTP 的应用 第三篇:Swo...

    FWHeart 评论0 收藏0
  • 压测 swoole_websocket_server 性能

    摘要:概述这是关于入门学习的第十篇文章压测性能。测试机上安装的虚拟机系统内存数量核数数量大小代码压测脚本并发量请求量压测结果第次第次第次以上是压测结果,供参考。小结通过这个压测结果,表明的执行效率是杠杠的当然还有一些参数是可以调优的,比如等。 概述 这是关于 Swoole 入门学习的第十篇文章:压测 swoole_websocket_server 性能。 第九篇:Swoole Redis ...

    余学文 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<