ng+php架构下websocket监听实例

系统架构:nginx服务器+应用服务器+数据库

通过websocket监听应用服务器的8090端口;前端js发送websocket请求到ng服务器;ng服务器转发请求到应用服务器的8090端口

1、php后端设置websocket监听

$_ip取应用服务器ip

$_port为监听的端口

启动命令:php /u01/a/b/c/socketServer.php

如果提示没有php命令,要将php添加到环境变量

<?php
class socketServer
{const LISTEN_SOCKET_NUM = 9;const LOG_PATH = "./log/";  //日志private $_ip = ''; //ipprivate $_port = 8090;  //端口 要和前端创建WebSocket连接时的端口号一致private $_socketPool = array(); //socket池,即存放套接字的数组private $_master = null;    //创建的套接字对象public function __construct(){$this->_ip = gethostbyname(gethostname());$this->initSocket();}// 创建WebSocket连接private function initSocket(){try {//创建socket套接字$this->_master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);// 设置IP和端口重用,在重启服务器后能重新使用此端口;socket_set_option($this->_master, SOL_SOCKET, SO_REUSEADDR, 1);//绑定地址与端口socket_bind($this->_master, $this->_ip, $this->_port);//listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接,其中的能存储的请求不明的socket数目。socket_listen($this->_master, self::LISTEN_SOCKET_NUM);} catch (Exception $e) {}//将socket保存到socket池中  (将套接字放入数组)默认把当前用户放在第一个$this->_socketPool[0] = array('resource' => $this->_master);$pid = getmypid();}public function array_column($arr2, $column_key) {$data = [];foreach ($arr2 as $key => $value) {$data[] = $value[$column_key];}return $data;}// 挂起进程遍历套接字数组,对数据进行接收、处理、发送public function run(){// 死循环  直到socket断开while (true) {try {$write = $except = NULL;// 从数组中取出resource列$sockets = $this->array_column($this->_socketPool, 'resource');/* $sockets 是一个存放文件描述符的数组。$write 是监听是否客户端写数据,传入NULL是不关心是否有写变化$except 是$sockets里面要派粗话的元素,传入null是监听全部最后一个参数是超时时间,0立即结束 n>1则最多n秒后结束,如遇某一个连接有新动态,则提前返回  null如遇某一个连接有新动态,则返回*/// 接收套接字数字,监听他们的状态就是有新消息到或有客户端连接/断开时,socket_select函数才会返回,继续往下执行$read_num = socket_select($sockets, $write, $except, NULL);if (false === $read_num) {return;}// 遍历套接字数组foreach ($sockets as $socket) {// 如果有新的连接进来if ($socket == $this->_master) {// 接收一个socket连接$client = socket_accept($this->_master);if ($client === false) {continue;}//连接 并放到socket池中$this->connection($client);} else {//接收已连接的socket数据,返回的是从socket中接收的字节数。// 第一个参数:socket资源,第二个参数:存储接收的数据的变量,第三个参数:接收数据的长度$bytes = @socket_recv($socket, $buffer, 2048, 0);// 如果接收的字节数为0$recv_msg = '';if ($bytes == 0) {// 断开连接$recv_msg = $this->disconnection($socket);} else {$data = json_decode($buffer);// 判断有没有握手,没有握手进行握手,已经握手则进行处理// 当从PMS后端传过来的,不用再握手if ($data->from != 'server' && $this->_socketPool[(int)$socket]['handShake'] == false) {// 握手$this->handShake($socket, $buffer);continue;} else {// 解析客户端传来的数据$recv_msg = gettype($data) == 'object' ? (array)$data : $this->parse($buffer);}}// 业务处理,组装返回客户端的数据格式$msg = $this->doEvents($socket, $recv_msg);// 把服务端返回的数据写入套接字$this->broadcast($msg);}}} catch (Exception $e) {}}}/*** 数据广播* @param $data*/private function broadcast($data){foreach ($this->_socketPool as $socket) {if ($socket['resource'] == $this->_master) {continue;}// 写入套接字socket_write($socket['resource'], $data, strlen($data));}}/*** 业务处理,在这可以对数据库进行操作,并返回客户端数据;根据不同类型,组装不同格式的数据* @param $socket* @param $recv_msg 客户端传来的数据* @return string*/private function doEvents($socket, $recv_msg){$type = $recv_msg['type'];$response = [];switch ($type) {// case 'login'://     $response['type'] = 'login';//     break;// case 'logout'://     $response['type'] = 'logout';//     break;case 'send':$response['info'] = $recv_msg['info'];break;}return $this->frame(json_encode($response));}/*** socket握手* @param $socket* @param $buffer  客户端传来的数据接收的数据* @return bool*/public function handShake($socket, $buffer){$acceptKey = $this->encry($buffer);$upgrade = "HTTP/1.1 101 Switching Protocols\r\n" ."Upgrade: websocket\r\n" ."Connection: Upgrade\r\n" ."Sec-WebSocket-Accept: " . $acceptKey . "\r\n\r\n";// 将socket写入缓冲区socket_write($socket, $upgrade, strlen($upgrade));// 标记握手已经成功,下次接受数据采用数据帧格式$this->_socketPool[(int)$socket]['handShake'] = true;//发送消息通知客户端握手成功$msg = array('type' => 'handShake', 'msg' => 'success');$msg = $this->frame(json_encode($msg));socket_write($socket, $msg, strlen($msg));return true;}/*** 帧数据封装* @param $msg* @return string*/private function frame($msg){$frame = [];$frame[0] = '81';$len = strlen($msg);if ($len < 126) {$frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len);} else if ($len < 65025) {$s = dechex($len);$frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s;} else {$s = dechex($len);$frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s;}$data = '';$l = strlen($msg);for ($i = 0; $i < $l; $i++) {$data .= dechex(ord($msg{$i}));}$frame[2] = $data;$data = implode('', $frame);return pack("H*", $data);}/*** 解析客户端的数据* @param $buffer* @return mixed*/private function parse($buffer){$decoded = '';$len = ord($buffer[1]) & 127;if ($len === 126) {$masks = substr($buffer, 4, 4);$data = substr($buffer, 8);} else if ($len === 127) {$masks = substr($buffer, 10, 4);$data = substr($buffer, 14);} else {$masks = substr($buffer, 2, 4);$data = substr($buffer, 6);}for ($index = 0; $index < strlen($data); $index++) {$decoded .= $data[$index] ^ $masks[$index % 4];}return json_decode($decoded, true);}//提取 Sec-WebSocket-Key 信息并加密private function encry($req){$key = null;if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $req, $match)) {$key = $match[1];}// 加密return base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));}/*** 连接socket* @param $client*/public function connection($client){socket_getpeername($client, $address, $port);$info = array('resource' => $client,'userInfo' => '','handShake' => false,'ip' => $address,'port' => $port,);$this->_socketPool[(int)$client] = $info;}/*** 断开连接* @param $socket* @return array*/public function disconnection($socket){$recv_msg = array('type' => 'logout','msg' => @$this->_socketPool[(int)$socket]['userInfo']['username'],);unset($this->_socketPool[(int)$socket]);return $recv_msg;}
}// 类外实例化
$sk = new socketServer();
// 运行
$sk ->run();

2、js前端代码

如果是https请求的,将【ws://】改成【wss://】

$(function(){var wsObj = new WebSocket("ws://【ng服务器ip】:【ng服务器端口】/ws");wsObj.onopen = function(ev){console.log("监听中...");};wsObj.onmessage = function(ev){// 业务代码};wsObj.onclose = function(ev){console.log("监听结束");wsObj.close();};wsObj.onerror = function(ev){console.log("监听报错", ev);};
});

3、nginx配置

如果是https请求的,将【/ws】改成【/wss】

upstream pms.uat {server  10.73.17.8:80;
}
upstream pms.uat2 {server  10.73.17.8:8090;
}server {listen        80;server_name   localhost;client_max_body_size 50M;location / {root /usr/local/httpd/htdocs;proxy_pass http://pms.uat/trunk/www/;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}# 转发websocketlocation /ws{ root /usr/local/httpd/htdocs;proxy_pass http://pms.uat2;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection 'upgrade';}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1163.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

51单片机学习--独立按键控制LED

功能&#xff1a;按下K1时D1亮&#xff0c;松开时D1灭&#xff0c;P3_1对应K1 , P2_0对应D1 #include <REGX52.H>void main() {while(1) {if(P3_1 0) //按下K1{P2_0 0;}else{P2_0 1;}}} 按下按钮和松开按钮时会有抖动&#xff0c;所以需要用延时函数来避免抖动造成的…

短视频抖音账号矩阵系统源码开发分享

引用&#xff1a;MySQL数据库&#xff0c;NGINX&#xff0c;PHP7.4&#xff0c;MySQL5.7&#xff0c;redis 媒体组件 组件 描述 image 图片 图片。支持 JPG、PNG、SVG、WEBP、GIF 等格式。 video 视频 视频组件。相关 API 请参考 tt.createVideoContext。 开发背景&…

手机快充协议

高通:QC2.0、QC3.0、QC3.5、QC4.0、QC5.0、 FCP、SCP、AFC、SFCP、 MTKPE1.1/PE2.0/PE3.0、TYPEC、PD2.0、PD3.0/3.1、VOOC 支持 PD3.0/PD2.0 支持 QC3.0/QC2.0 支持 AFC 支持 FCP 支持 PE2.0/PE1.1 联发科的PE&#xff08;Pump Express&#xff09;/PE 支持 SFCP 在PP…

计算机网络——三次握⼿、四次挥手

TCP 三次握手 1、第⼀个SYN报⽂&#xff1a; 客户端随机初始化序列号client_isn&#xff0c;放进TCP⾸部序列号段&#xff0c; 然后把SYN置1。把SYN报⽂发送给服务端&#xff0c;表⽰发起连接&#xff0c; 之后客户端处于SYN-SENT状态。 2、第⼆个报⽂SYNACK报⽂&#xff1a; …

MQTT协议在物联网环境中的应用及代码实现解析(一)

MQTT协议全称是Message Queuing Telemetry Transport&#xff0c;翻译过来就是消息队列遥测传输协议&#xff0c;它是物联网常用的应用层协议&#xff0c;运行在TCP/IP中的应用层中&#xff0c;依赖TCP协议&#xff0c;因此它具有非常高的可靠性&#xff0c;同时它是基于TCP协议…

TCP/IP协议是什么?

78. TCP/IP协议是什么&#xff1f; TCP/IP协议是一组用于互联网通信的网络协议&#xff0c;它定义了数据在网络中的传输方式和规则。作为前端工程师&#xff0c;了解TCP/IP协议对于理解网络通信原理和调试网络问题非常重要。本篇文章将介绍TCP/IP协议的概念、主要组成部分和工…

深度学习 / 数据处理:如何处理偏态数据

1 前言 当我们使用一个线性回归模型时&#xff0c;通常这个模型是在很大假设的前提下才有一个很好的结果&#xff1a; 1、假设预测因子和预测目标之间的关系是线性的2、数据不存在外在噪声&#xff1a;不存在一些极端的数据3、非共线性&#xff08; collinearity&#xff09;…

利用弱监督学习实现从单张图像到图像集的准确3D人脸重建:PyTorch和Python的深度实践

在这篇文章中&#xff0c;我将带你走进3D人脸重建的世界&#xff0c;并介绍如何使用弱监督学习从单张图像或图像集中准确重建3D人脸。我们将使用Python和PyTorch&#xff0c;一种广泛用于深度学习的开源框架&#xff0c;来实现这一目标。 一、概述 3D人脸重建是计算机视觉领域…

新增进程管理、SSH会话管理功能,1Panel开源面板v1.4.0发布

2023年7月17日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布v1.4.0版本。 在这个版本中&#xff0c;1Panel新增了进程管理和SSH会话管理功能&#xff1b;支持容器编辑和升级&#xff0c;数据库兼容MySQL 5.6&#xff1b;备份账号可以添加微软OneDrive&…

github简单使用

留个链接等着吃灰 链接github的使用 举例 先选择需要上传的文件夹 比如db -main文件夹 gitbash 打开shell 进行如下的操作 一般都得写的东西 git init git remote add origin 想要上传的地址 git pull 将远程库拉过来 如果远程没有文件的话 那么shell就显示没有文件 git …

【Vue】 在 vue 中使用 iframe 挂载 html 文件

文章目录 首先需要将要挂载的html文件放到public 文件夹中 路径的引用形式 <iframe ref"sheet" src"/luckysheet.html" width"100%" height"100%"></iframe>通过绑定 ref 获取到 iframe&#xff0c;data为我需要传递的数…

Vscode自定义注释模板

首先安装插件Doxygen Documentation Generator&#xff0c;安装完成之后点击Doxygen插件的设置&#xff0c;按照下面的步骤打开settings.json进行编辑&#xff1a; 在settings.json中追加如下代码&#xff1a; "doxdocgen.file.copyrightTag": ["Copyright (C),…

Python一行命令搭建HTTP服务器并外网访问+-+内网穿透

文章目录 1.前言2.本地http服务器搭建2.1.Python的安装和设置2.2.Python服务器设置和测试 3.cpolar的安装和注册3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 转载自远程内网穿透的文章&#xff1a;【Python】快速简单搭建HTTP服务器并公网访问「cpolar内网穿透…

select 语法和 select 死锁问题

select 语法和 select 死锁问题 引言 本文介绍了 Go 语言中的 select 语法以及与之相关的 select 死锁问题。我们将详细讨论 select 语句的语法&#xff0c;解答常见的疑问&#xff0c;并提供示例代码进行说明。 select 语法 select 用于处理多个通道操作&#xff0c;实现非…

FlinkCDC第四部分-同步mysql到mysql,ctrl就完事~(flink版本1.17.1)

本文介绍了不同源单表-单表同步&#xff0c;不同源多表-单表同步。 注&#xff1a;此版本支持火焰图 Flink版本&#xff1a;1.17.1 环境&#xff1a;Linux CentOS 7.0、jdk1.8 基础文件&#xff1a; flink-1.17.1-bin-scala_2.12.tgz、 flink-connector-jdbc-3.0.0-1.16.…

人工智能与Chat GPT

一本书全面掌握ChatGPT&#xff0c;既有向ChatGPT提问的技巧&#xff0c; 也有构建自己的ChatGPT模型的方法&#xff0c;涵盖开发背景、关联技术、使用方法、应用形式、实用案例等 人工智能是我们这个时代最热门的话题&#xff0c;人们既希望它能代替我们做一些工作&#xff0c…

云原生——Docker容器化实战

❄️作者介绍&#xff1a;奇妙的大歪❄️ &#x1f380;个人名言&#xff1a;但行前路&#xff0c;不负韶华&#xff01;&#x1f380; &#x1f43d;个人简介&#xff1a;云计算网络运维专业人员&#x1f43d; 前言 "Docker"一词指代了多个概念&#xff0c;包括开源…

uniapp调接口出现跨域问题。

今天在写uniapp项目的时候&#xff0c;使用在线模拟接口的时候&#xff0c;出现跨域问题。 【问题描述】&#xff1a; ①在内嵌浏览器运行&#xff0c;不会出现跨域问题&#xff0c;好像是内嵌浏览器自动去掉了跨域问题。 ②在外部浏览器调用的时候会出现跨域问题。&#xf…

IDEA 搭建Android 开发环境

项目实战 废话不多说开始创建先第一个 Android 项目 步骤一 FILE → New → Project 步骤二-选择 Android 项目模板 选那个安卓机器人,如果没有这个选项,需要升级IDEA版本或者安装安卓插件 选择*Basic Activity* Next-下一步 步骤三-项目初始化 名称、包名、安装位置自行调整…

Cadence PCB 仿真激励专题

&#x1f3e1;《总目录》   &#x1f3e1;《宝典目录》 目录 1&#xff0c;内容概述2&#xff0c;内容目录 1&#xff0c;内容概述 本专题详细介绍Cadence PCB仿真的激励源。 2&#xff0c;内容目录 Cadence PCB仿真 使用 Allegro PCB SI 激励信号源参数Simulation配置方法图…