Thinkphp6实现websocket

article/2025/6/19 3:09:44

项目需要连接一台自动售货机,售货机要求两边用websocket连接,监听9997端口。本文实现了一个基于PHP的WebSocket服务器,用于连接自动售货机,支持start/stop/restart命令操作

1.新建文件

新建文件 /command/socket.php

<?php
namespace app\command;use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;class TestSocket extends Command
{public $server;protected static $pidFile = '/tmp/test_socket.pid';protected static $running = true;protected function configure(){$this->setName('test:socket')->setDescription('WebSocket server for JieLe')->addArgument('action', Argument::OPTIONAL, 'start|stop|restart', 'start');}protected function execute(Input $input, Output $output){$action = $input->getArgument('action');switch ($action) {case 'start':$this->startServer($output);break;case 'stop':$this->stopServer($output);break;case 'restart':$this->stopServer($output);sleep(1); // 等待1秒确保服务停止$this->startServer($output);break;default:$output->writeln("Invalid action. Use start|stop|restart");break;}}protected function startServer(Output $output){// 检查是否已运行if (file_exists(self::$pidFile)) {$pid = file_get_contents(self::$pidFile);if (posix_getpgid($pid)) {$output->writeln("Server is already running (PID: {$pid})");return;}}// 创建TCP Socket服务器$this->server = stream_socket_server("tcp://0.0.0.0:9997", $errno, $errstr);if (!$this->server) {$output->error("Failed to start server: $errstr ($errno)");return;}// 保存PIDfile_put_contents(self::$pidFile, getmypid());// 注册信号处理器pcntl_signal(SIGTERM, function() {self::$running = false;});pcntl_signal(SIGINT, function() {self::$running = false;});$output->info("WebSocket server running on ws://0.0.0.0:9997");$clients = [];while (self::$running) {// 处理信号pcntl_signal_dispatch();$read = array_merge([$this->server], $clients);$write = $except = null;// 使用stream_select监听活动连接if (stream_select($read, $write, $except, 5) > 0) {// 处理新连接if (in_array($this->server, $read)) {$client = stream_socket_accept($this->server);$clients[] = $client;}// 处理客户端消息foreach ($read as $socket) {if ($socket === $this->server) continue;$data = fread($socket, 1024);if ($data === false || $data === '') {// 客户端断开$key = array_search($socket, $clients);unset($clients[$key]);fclose($socket);continue;}// WebSocket握手处理if (strpos($data, 'Upgrade: websocket') !== false) {$this->handshake($socket, $data);continue;}// 处理WebSocket帧$decoded = $this->decodeFrame($data);//$decoded = $this->main($decoded); 实际处理业务的函数// 发送回复$response = $decoded;$frame = $this->encodeFrame($response);fwrite($socket, $frame);}}}// 清理工作foreach ($clients as $client) {fclose($client);}fclose($this->server);unlink(self::$pidFile);$output->writeln("Server stopped");}protected function stopServer(Output $output){if (!file_exists(self::$pidFile)) {$output->writeln("Server is not running");return;}$pid = file_get_contents(self::$pidFile);if (posix_getpgid($pid)) {posix_kill($pid, SIGTERM);$output->writeln("Stopping server (PID: {$pid})...");// 等待进程结束$timeout = 10; // 10秒超时while ($timeout-- > 0 && posix_getpgid($pid)) {sleep(1);}if (posix_getpgid($pid)) {posix_kill($pid, SIGKILL); // 强制杀死}}if (file_exists(self::$pidFile)) {unlink(self::$pidFile);}$output->writeln("Server stopped");}/************************************************** websocket转码相关函数  *******************************************************/// WebSocket帧解码public function decodeFrame($data){$len = ord($data[1]) & 127;if ($len === 126) {$masks = substr($data, 4, 4);$data = substr($data, 8);} elseif ($len === 127) {$masks = substr($data, 10, 4);$data = substr($data, 14);} else {$masks = substr($data, 2, 4);$data = substr($data, 6);}$decoded = '';for ($i = 0; $i < strlen($data); $i++) {$decoded .= $data[$i] ^ $masks[$i % 4];}return $decoded;}// WebSocket帧编码public function encodeFrame($data){$frame = [];$frame[0] = 0x81; // FIN + text frame$len = strlen($data);if ($len <= 125) {$frame[1] = $len;} elseif ($len <= 65535) {$frame[1] = 126;$frame[2] = ($len >> 8) & 255;$frame[3] = $len & 255;} else {$frame[1] = 127;for ($i = 0; $i < 8; $i++) {$frame[$i + 2] = ($len >> (8 * (7 - $i))) & 255;}}$frame = array_map('chr', $frame);$frame = implode('', $frame) . $data;return $frame;}// WebSocket握手处理public function handshake($socket, $headers){if (preg_match('/Sec-WebSocket-Key: (.*)\r\n/', $headers, $match)) {$key = base64_encode(sha1($match[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));$response = "HTTP/1.1 101 Switching Protocols\r\n";$response .= "Upgrade: websocket\r\n";$response .= "Connection: Upgrade\r\n";$response .= "Sec-WebSocket-Accept: $key\r\n\r\n";fwrite($socket, $response);}}
}

2.开启服务
docker exec php7.3 php /lnmp/nginx/data/thinkphp6/think test:socket start
docker exec php7.3 php /lnmp/nginx/data/thinkphp6/think test:socket stop
docker exec php7.3 php /lnmp/nginx/data/thinkphp6/think test:socket restart
在这里插入图片描述

3.在nginx配置目录,可通过浏览器访问socket业务

server {listen 80;root 省略;#172.18.0.3是提供php服务的iplocation /rtsp {proxy_pass http://172.18.0.3:9997;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";proxy_set_header Host $host;proxy_read_timeout 600s;}location ~ \.php$ {#省略}}

4.测试


http://www.hkcw.cn/article/HEXaJBbWpn.shtml

相关文章

痉挛性斜颈带来的困扰

当颈部不受控制地扭转歪斜&#xff0c;生活便被打乱了节奏。颈部肌肉异常收缩&#xff0c;导致头部不自觉偏向一侧或后仰&#xff0c;不仅让外观明显异于常人&#xff0c;还会引发持续的酸痛与僵硬感。长时间保持扭曲姿势&#xff0c;肩颈肌肉过度紧绷&#xff0c;甚至会牵连背…

【中国・珠海】2025 物联网与边缘计算国际研讨会(IoTEC2025)盛大来袭!

2025 物联网与边缘计算国际研讨会&#xff08;IoTEC2025&#xff09;盛大来袭&#xff01; 科技浪潮奔涌向前&#xff0c;物联网与边缘计算已成为驱动各行业变革的核心力量。在此背景下&#xff0c;2025 物联网与边缘计算国际研讨会&#xff08;IoTEC2025&#xff09;即将震撼…

一周学会Pandas2之Python数据处理与分析-数据重塑与透视-pivot() - 透视 (长 -> 宽,有限制)

锋哥原创的Pandas2 Python数据处理与分析 视频教程&#xff1a; 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili pivot() 是 pandas 中用于数据重塑的核心方法&#xff0c;它将长格式数据转换为宽格式数据&#xff0c;与 melt() 方…

WordPress通过简码插入bilibili视频

发布于&#xff1a;Eucalyptus-Blog 一、前言 B站是国内非常受欢迎的视频分享平台&#xff0c;上面不仅内容丰富&#xff0c;而且很多视频制作精良、趣味十足。很多人&#xff0c;比如我&#xff0c;就喜欢将B站的视频通过 iframe 嵌入到自己的网页中&#xff0c;但这段代码又…

【Unity博客节选】Timeline 的 Traversal mode参数

注&#xff1a;软件版本Unity 6.0 Timeline 1.8.7 作者&#xff1a;CSDN RingleaderWang 原文&#xff1a;《Unity第25期——Timeline结构及其源码浅析》 文章首发Github&#x1f44d;&#xff1a;《Timeline结构及其源码浅析》 Bilibili 视频版&#x1f44d;&#x1f44d;&a…

Constraints and Triggers

目录 Kinds of Constraints Single-Attribute Keys Multiattribute Key Foreign Keys Expressing Foreign Keys Enforcing Foreign-Key Constraints Actions Taken Attribute-Based Checks Timing of Checks Tuple-Based Checks Assertions Timing of Assertion Ch…

免费且好用的PDF水印添加工具

软件介绍 今天要给大家推荐一款超实用的PDF添加水印工具&#xff0c;它能够满足用户给PDF文件添加水印的需求&#xff0c;而且完全免费。 这款PDF添加水印的软件有着简洁的界面&#xff0c;操作简便&#xff0c;无需安装&#xff0c;解压后即可使用。 在使用前&#xff0c;先…

设计模式——面向对象设计六大原则

摘要 本文详细介绍了设计模式中的六大基本原则&#xff0c;包括单一职责原则、开放封闭原则、里氏替换原则、接口隔离原则、依赖倒置原则和合成复用原则。每个原则都通过定义、理解、示例三个部分进行阐述&#xff0c;旨在帮助开发者提高代码的可维护性和灵活性。通过具体代码…

DO指数GPU版本

大指数下DO指数模型计算优化 DO指数模型概述 DO指数&#xff08;Duranton-Overman Index&#xff09;是由Duranton和Overman于2005年提出的产业空间集聚测度方法&#xff0c;它通过分析企业间的精确地理距离分布来识别产业集聚模式。与传统集聚指标相比&#xff0c;DO指数具有…

工业物联网中的事件驱动采样架构及优化

论文标题 Event-Based Sampling Architecture and Optimization for Industrial Internet of Things 工业物联网中的事件驱动采样架构及优化 作者信息 Tejas Thosani Process Control Systems, Micron Technology Inc., Manassas, USA tthosanimicron.com Andres Prado Esp…

Windows | 总误按Num Lock?修改注册表永久禁用Numlk键使小键盘一直输入数字

先说需修改注册表的位置与键值 路径&#xff1a;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout\ 二进制键&#xff1a;Scancode Map 键值&#xff1a; 00 00 00 00 00 00 00 00 01 00 00 00 00 00 45 00 00 00 00 00 00 00 00 00如下图&#xff1a; …

c#与halcon环境配置,导出算法库,使用halcon环境编程

目录 1. C#配置halcon运行环境 2.导出halcon算法库 3.使用方法 记录下C#配置halcon环境的方法&#xff0c;以及halcon导出库的使用方法。 1. C#配置halcon运行环境 VS版本&#xff1a; vs2019 halcon版本: 20.11 创建c#工程&#xff0c;点击“创建新项目”&#xff0c;…

tomcat yum安装

使用yum安装 yum install -y java-1.7.0-openjdk* tomcat* --disablerepoepel## java-1.7.0-openjdk* 注意&#xff1a;最终安装的是java-1.8.0版本## --disablerepoepel 禁用&#xff1a;EPEL源&#xff0c;防止版本冲突 java -version (2) 启停&#xff1a;Tomcat 7 s…

时间的基本概念与相关技术三

1.5 守时技术 所谓守时&#xff08;time keeping&#xff09;是指一个时频系统&#xff08;包括频标和分频钟&#xff09;对时间信号和时间信息的保持。频率标准&#xff08;简称频标&#xff09;的频率准确度、频率稳定度和守时系统的环境条件是决定守时能力的三个关键因素。…

云原生安全基石:Kubernetes 核心概念与安全实践指南

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. Kubernetes 架构全景 Kubernetes&#xff08;简称 K8s&#xff09;采用主从架构&#xff0c;由控制平面&#xff08;Control Plane&…

【python】uv管理器

uv是一个速度极快的 Python 包和项目管理器&#xff0c;用 Rust 编写。 安装 安装uv之前&#xff0c;确保你的电脑不需要安装了python 在Windows下&#xff0c;可以使用官方的脚本直接安装 powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.…

2021 年 12 月大学英语四级考试真题(第 1 2 3 套)——解析版——篇章题

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;目前中南大学MBA在读&#xff0c;也考取过HCIE Cloud Computing、CCIE Security、PMP、CISP、RHCE、CCNP RS、PEST 3等证书。&#x1f433; &…

【Linux】mmap文件内存映射

&#x1f4dd;前言&#xff1a; 这篇文章我们来讲讲Linux——mmap mmap介绍mmap接口介绍mmap使用示例 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;Linux &#x1f380;CSDN主页 愚润求学 &#x1f304;其他专栏&#xff1a;C学习笔记&a…

深度学习驱动的超高清图修复技术——综述

Deep Learning-Driven Ultra-High-Definition Image Restoration: A Survey Liyan Wang, Weixiang Zhou, Cong Wang, Kin-Man Lam, Zhixun Su, Jinshan Pan Abstract Ultra-high-definition (UHD) image restoration​​ aims to specifically solve the problem of ​​quali…

【Docker系列】Docker 容器内安装`ps`命令

博客目录 一、为什么需要在 Docker 容器中安装ps命令二、不同 Linux 发行版的安装方法1. Alpine Linux 镜像的安装方法2. Debian/Ubuntu 镜像的安装方法3. CentOS/RHEL 镜像的安装方法 三、验证安装与基本使用四、永久解决方案&#xff1a;修改 Dockerfile1. Alpine 基础镜像的…