php实现socket推送技术的示例


Posted in PHP onDecember 20, 2017

在socket出现之前已经有ajax定时请求、长轮询等方案,但都不能满足需求,socket就应用而生了。

socket基本函数socket

总结下常用的socket函数

服务端: socket_create 创建socket设置基本参数

socket_bind 绑定ip和端口号

socket_listen 监听

socket_accept 客户端的连接

socket_read 读取客户端的数据

socket_write 给单独客户端发送数据

socket_close 关闭连接

客户端:socket_create 创建socket设置基本参数

socket_connect 连接socket

socket_write 给服务端发送数据

socket_read 读取服务端数据

socket_close 关闭连接

H5websocket不多说了,上链接

OK,开始贴代码~

----------------------------------------------------------分割线

服务端代码:

<?php
class WS {
 var $master;
 var $sockets = array();
 var $debug = false;//true为调试模式,输出log日志
 var $handshake = array();
 function __construct($address, $port){
 $this->master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
 socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
 socket_bind($this->master, $address, $port)  or die("socket_bind() failed");
 socket_listen($this->master,20)  or die("socket_listen() failed");
 
 $this->sockets[] = $this->master;
 $this->say("Server Started : ".date('Y-m-d H:i:s'));
 $this->say("Listening on : ".$address." port ".$port);
 $this->say("Master socket : ".$this->master."\n");
 
 while(true){
 $socketArr = $this->sockets;
 $write = NULL;
 $except = NULL;
 socket_select($socketArr, $write, $except, NULL); //自动选择来消息的socket 如果是握手 自动选择主机
 foreach ($socketArr as $socket){
 if ($socket == $this->master){ //主机
  $client = socket_accept($this->master);
  if ($client < 0){
  $this->log("socket_accept() failed");
  continue;
  } else{
  $this->connect($client);
  }
 } else {
  $bytes = @socket_recv($socket,$buffer,2048,0);
  if ($bytes == 0){
  $this->disConnect($socket);
  }
  else{
  $key = array_search($socket, $this->sockets);
  if (empty($this->handshake) || !isset($this->handshake[$key]) || !$this->handshake[$key]){
  $this->doHandShake($socket, $buffer, $key);
  }
  else{
  $buffer = $this->decode($buffer);
  echo $buffer.PHP_EOL;
  $key = array_search($socket, $this->sockets);
  $arr = $this->sockets;
  array_shift($arr);
  foreach ($arr as $s){
  $this->send($s, $buffer);
  }
  }
  }
 }
 }
 }
 }
 
 function send($client, $msg){
 $msg = $this->frame($msg);
 socket_write($client, $msg, strlen($msg));
 }
 function connect($socket){
 array_push($this->sockets, $socket);
 $this->say("\n" . $socket . " CONNECTED!");
 $this->say(date("Y-n-d H:i:s"));
 }
 function disConnect($socket){
 $index = array_search($socket, $this->sockets);
 socket_close($socket);
 $this->say($socket . " DISCONNECTED!");
 if ($index >= 0){
 echo 'unset index is:'.PHP_EOL;
 unset($this->sockets[$index]);
 }
 }
 function doHandShake($socket, $buffer, $handKey){
 $this->log("\nRequesting handshake...");
 $this->log($buffer);
 list($resource, $host, $origin, $key) = $this->getHeaders($buffer);
 $this->log("Handshaking...");
 $upgrade = "HTTP/1.1 101 Switching Protocol\r\n" .
  "Upgrade: websocket\r\n" .
  "Connection: Upgrade\r\n" .
  "Sec-WebSocket-Accept: " . $this->calcKey($key) . "\r\n\r\n"; //必须以两个回车结尾
 $this->log($upgrade);
 $sent = socket_write($socket, $upgrade, strlen($upgrade));
 $this->handshake[$handKey]=true;
 $this->log("Done handshaking...");
 return true;
 }
 function getHeaders($req){
 $r = $h = $o = $key = null;
 if (preg_match("/GET (.*) HTTP/" ,$req,$match)) { $r = $match[1]; }
 if (preg_match("/Host: (.*)\r\n/" ,$req,$match)) { $h = $match[1]; }
 if (preg_match("/Origin: (.*)\r\n/" ,$req,$match)) { $o = $match[1]; }
 if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)) { $key = $match[1]; }
 return array($r, $h, $o, $key);
 }
 function calcKey($key){
 //基于websocket version 13
 $accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
 return $accept;
 }
 function decode($buffer) {
 $len = $masks = $data = $decoded = null;
 $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 $decoded;
 }
 function frame($s){
 $a = str_split($s, 125);
 if (count($a) == 1){
 return "\x81" . chr(strlen($a[0])) . $a[0];
 }
 $ns = "";
 foreach ($a as $o){
 $ns .= "\x81" . chr(strlen($o)) . $o;
 }
 return $ns;
 }
 
 function say($msg = ""){
 echo $msg . "\n";
 }
 function log($msg = ""){
 if ($this->debug){
 echo $msg . "\n";
 } 
 }
}
 
new WS('localhost', 4000);

客户端代码(H5):

<html>
 <head>
 <title>demo</title>
 <script src="https://cdn.bootcss.com/jquery/1.9.1/jquery.min.js"></script>
 </head>
 <body>
 <input type="text" id="content">
 <input type="button" value="send" id="send">
 <script type="text/javascript">
  var ws = new WebSocket("ws://localhost:4000");
  ws.onopen = function(){
  console.log("握手成功");
  }
  ws.onmessage = function(e){
  console.log("message:" + e.data);
  }
  ws.onerror = function(){
  console.log("error");
  }
  $("#send").click(function(){
  content = $("#content").val();
  console.log(content);
  ws.send(content);
  })
 </script>
 </body>
</html>

然后执行php demo.php 开启socket(从运维那偷学一招,linux下执行nohup php demo.php &可以在后台执行),浏览器打开多个index.html,就能建立通讯了。

代码解析:

1.属性$sockets数组保存每个accept连接(不知道这么描述对不对);

2.属性$handshake数组保存连接是否在连接状态;

以上这篇php实现socket推送技术的示例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
php设计模式 Interpreter(解释器模式)
Jun 26 PHP
php中is_null,empty,isset,unset 的区别详细介绍
Apr 28 PHP
深入密码加salt原理的分析
Jun 06 PHP
使用PHP实现Mysql读写分离
Jun 28 PHP
PHP变量内存分配问题记录整理
Nov 27 PHP
PHP实现的蚂蚁爬杆路径算法代码
Dec 03 PHP
PHP Mysqli 常用代码集合
Nov 12 PHP
基于thinkPHP3.2实现微信接入及查询token值的方法
Apr 18 PHP
PHP异常处理定义与使用方法分析
Jul 25 PHP
ThinkPHP 5.x远程命令执行漏洞复现
Sep 23 PHP
laravel-admin select框默认选中的方法
Oct 03 PHP
详解将数据从Laravel传送到vue的四种方式
Oct 16 PHP
PHP实现模拟http请求的方法分析
Dec 20 #PHP
php封装db类连接sqlite3数据库的方法实例
Dec 19 #PHP
PHP性能分析工具xhprof的安装使用与注意事项
Dec 19 #PHP
PHP实现的最大正向匹配算法示例
Dec 19 #PHP
PHP实现的字符串匹配算法示例【sunday算法】
Dec 19 #PHP
PHP实现的折半查找算法示例
Dec 19 #PHP
php之header的不同用法总结(实例讲解)
Nov 28 #PHP
You might like
yii2.0之GridView自定义按钮和链接用法
2014/12/15 PHP
PHP面向对象程序设计中的self、static、parent关键字用法分析
2019/08/14 PHP
Aster vs Newbee BO5 第一场2.19
2021/03/10 DOTA
ASP.NET jQuery 实例18 通过使用jQuery validation插件校验DropDownList
2012/02/03 Javascript
30分钟就入门的正则表达式基础教程
2013/02/25 Javascript
一个支持任意尺寸的图片上下左右滑动效果
2014/08/24 Javascript
jquery使用slideDown实现模块缓慢拉出效果的方法
2015/03/27 Javascript
javascript性能优化之DOM交互操作实例分析
2015/12/12 Javascript
JS点击某个图标或按钮弹出文件选择框的实现代码
2016/09/27 Javascript
jQuery animate()实现背景色渐变效果的处理方法【使用jQuery.color.js插件】
2017/03/15 Javascript
详解关于Vue版本不匹配问题(Vue packages version mismatch)
2018/09/17 Javascript
小程序实现人脸识别功能(百度ai)
2018/12/23 Javascript
小白教程|一小时上手最流行的前端框架vue(推荐)
2019/04/10 Javascript
微信小程序+腾讯地图开发实现路径规划绘制
2019/05/22 Javascript
vue+koa2实现session、token登陆状态验证的示例
2019/08/30 Javascript
vue项目中openlayers绘制行政区划
2020/12/24 Vue.js
pycharm 使用心得(一)安装和首次使用
2014/06/05 Python
Python 的 with 语句详解
2014/06/13 Python
Python中集合类型(set)学习小结
2015/01/28 Python
Python3实现将文件树中所有文件和子目录归档到tar压缩文件的方法
2015/05/22 Python
Python+Pika+RabbitMQ环境部署及实现工作队列的实例教程
2016/06/29 Python
virtualenv实现多个版本Python共存
2017/08/21 Python
Python基础语言学习笔记总结(精华)
2017/11/14 Python
python生成密码字典的方法
2018/07/06 Python
Python过滤txt文件内重复内容的方法
2018/10/21 Python
python opencv判断图像是否为空的实例
2019/01/26 Python
keras的siamese(孪生网络)实现案例
2020/06/12 Python
Foreo国际站:Foreo International
2018/10/29 全球购物
学校岗位设置方案
2014/01/16 职场文书
会计电算化专业自荐信
2014/03/15 职场文书
贯彻学习两会心得体会范文
2014/03/17 职场文书
好听的队名和口号
2014/06/09 职场文书
读后感作文评语
2014/12/25 职场文书
隐形的翅膀观后感
2015/06/10 职场文书
运动会运动员赞词
2015/07/22 职场文书
解决golang在import自己的包报错的问题
2021/04/29 Golang