💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## workerman 网络分析 **author: xiak** **last update: 2021-12-20 10:11:11** ---- [TOC=3,8] ---- 网络连接分析: send close destroy baseWrite baseRead pipe pauseRecv server Workerman\Connection\TcpConnection client Workerman\Connection\AsyncTcpConnection ---- ### 二进制操作 ```php <?php // https://blog.csdn.net/sinat_28631741/article/details/80961033 // 用 是十六进制 表示 ASCII 字符 echo "\x41" . PHP_EOL; // 65 0x41 A // 转换字符串第一个字节为 0-255 之间的值(十进制) echo ord("A") . PHP_EOL; // 65 // 返回指定的 ASCII 字符 echo chr(65) . PHP_EOL; // A //echo sprintf("%02X", "\xFF"); // 十六进制 字符转换为 十进制 echo hexdec('0x41') . PHP_EOL; // 65 echo 0x41 . PHP_EOL; // 65 // 1. 在 php 中直接定义 十六进制 0x 形式,输出时 会转换为 十进制输出 // 2. 在 php 中通过 十六进制 \x 字符串形式 表示 ASCII 字符 // 二进制转换为十进制 echo bindec('00000000') . PHP_EOL; // 0 // 十进制转换十六进制 echo sprintf("%02X", 15) . PHP_EOL; // 0F // 十进制转二进制 echo decbin(3) . PHP_EOL; // 11 // 十进制转二进制 echo sprintf("%08b", 1) . PHP_EOL; // 00000001 ``` ---- ~~~ 0010 0001 0x21 这并不是 21,而是 十六进制的 21 ,对应的十进制 是 33 0010 0x2 0001 0x1 探寻为什么 十六进制 到 二进制 的转换 尽然可以这样,直接每一位就能单独对应上,结果就是拼接在一起的。 ~~~ [IEEE 754 双精度浮点数内部表示法 · 开发者工具箱](https://devtool.tech/double-type) ---- ### WebSocket 协议 ~~~ $_t = Workerman\Protocols\Ws::BINARY_TYPE_BLOB; // $_t = $conn->websocketType; // echo $_t; // 0x8 close // 0x9 ping // 0xa ping // "\x8a" 138 10001010 // $conn->websocketType = "\x8a"; // $conn->websocketType = "\x8a"; ~~~ [使用Websocket实现消息推送(心跳)_ttdevs-CSDN博客_websocket心跳](https://blog.csdn.net/ttdevs/article/details/62887058) [(websocket)协议中Ping Pong,Socket通讯ping pong(长连接),心跳包_ShareUs的专栏-CSDN博客_ping pong 协议](https://blog.csdn.net/ShareUs/article/details/85246287?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link&utm_relevant_index=2) [WebSocket协议深入探究 - 知乎](https://zhuanlan.zhihu.com/p/32731137) > ping 应该是%x9 作者写错了一下 [定时器最大可以设置成多久执行一次?-workerman社区](https://www.workerman.net/q/1270) ---- 记录一个无法平滑停止的问题 ~~~ mqtt 库 // unset($this->s->conn); unset($this->s); $this->s = null; 对象释放,但是成员属性的指向的对象本身并没有释放 修复: ~~~ ```php <?php /** * 命令行程序(守护): TestWorkerBusiness * * last update: 2023-09-28 14:25:22 * * author: 夏凯 */ use Workerman\Timer; use Workerman\Worker; // realPath echo 'TestWorkerBusiness __DIR__: ' . __DIR__ . PHP_EOL; class S { public $conn; public static function sEcho($str, $type) { echo $str . PHP_EOL; } public function __construct() { $this->conn = new \Workerman\Connection\AsyncTcpConnection("ws://screen.api.test.yf5g.cn/websocket"); // 有这个的话, onClose 中必须释放,否则会出现无法释放 conn $this->conn->onConnect = function () { self::sEcho('initInnerTextConn: onConnect: ', 'debug'); // $auth = json_encode(['pwd' => \Env::get('yf_iot_gatewayworker_app_rrpc_pwd')]); // $conn->send($auth); // $conn->keepAliveTimerd = Timer::add(60, function () use ($conn) { // if (isset($conn->keepAliveTimerd) && $conn->getStatus() !== \Workerman\Connection\TcpConnection::STATUS_ESTABLISHED) { // Timer::del($conn->keepAliveTimerd); // return; // } // $conn->send('{"type": "ping"}'); // }); // self::$innerTextConn = $conn; }; // $conn->onError = function ($conn, $code, $msg) { // self::sEcho('initInnerTextConn: onError: ' . $msg, 'error'); // if (isset($conn->keepAliveTimerd)) { // Timer::del($conn->keepAliveTimerd); // $conn->keepAliveTimerd = null; // } // // https://www.workerman.net/doc/workerman/worker/on-error.html // $conn->onMessage = $conn->onClose = $conn->onError = $conn->onBufferFull = $conn->onBufferDrain = null; // // self::$innerTextConn = null; // }; $this->conn->onClose = function ($conn) { self::sEcho('initInnerTextConn: onClose', 'error'); if (isset($conn->keepAliveTimerd)) { Timer::del($conn->keepAliveTimerd); $conn->keepAliveTimerd = null; } // 必须要释放掉,否则会出现对象属性无法释放 $conn->onClose = $conn->onConnect = null; // self::$innerTextConn = null; }; $this->conn->connect(); // $this->conn = $conn; } } class TestWorkerBusiness extends BaseWorkerBusiness { public static $innerTextConn; public $s; public function __construct($worker) { parent::__construct($worker); // 单独设置 tp 日志目录 config('log.path', LOG_PATH . "daemon/smartpark/{$worker->name}/"); // 还可以定义只写入的日志级别 config('log.level', ['error', 'debug', 'info']); \think\Log::init(config('log')); $this->sEcho(__METHOD__); $this->sEcho('log.path: ' . config('log.path')); } // 进程启动后 public function onWorkerStart($worker) { $this->sEcho(__METHOD__); $this->masterTask(); // stop -g Timer::add(5, function () { if ($this->worker->stopping) { foreach (\Workerman\Connection\TcpConnection::$connections as $conn) { $conn->close(); // unset($conn); } unset($this->s); // \Workerman\Connection\TcpConnection::$connections = null; // $this->s->conn->__destruct(); // unset($this->s->conn); // $this->s = null; // self::$innerTextConn->close(); // exit(0); } }, [], true); } public function masterTask() { $this->s = new S(); } } ``` ```php <?php class A { public $b; public function __construct() { $b = new B(); $this->b = $b; // 加了这个 $a = null; A B 就不会析构 $this->b->x = function () { }; } function __destruct() { echo '__destruct A'; } } class B { function __destruct() { echo '__destruct B'; } } $a = new A(); // $a->b = null; $a = null; while (true) { sleep(2); } ``` ```php <?php class A { public $b; public function __construct() { // 成员属性为匿名函数,就不会析构 $this->b = function () { }; } function __destruct() { echo '__destruct A'; } } $a = new A(); // $a->b = null; $a = null; while (true) { sleep(2); } ```