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 相关文章推荐
Mysql的常用命令
Oct 09 PHP
vs中通过剪切板循环来循环粘贴不同内容
Apr 30 PHP
php启用zlib压缩文件的配置方法
Jun 12 PHP
ThinkPHP3.1.3版本新特性概述
Jun 19 PHP
PHP中对各种加密算法、Hash算法的速度测试对比代码
Jul 08 PHP
yiic命令时提示“php.exe”不是内部或外部命令的解决方法
Dec 18 PHP
PHP 二级子目录(后台目录)设置二级域名
Mar 02 PHP
thinkphp框架page类与bootstrap分页(美化)
Jun 25 PHP
php封装的pdo数据库操作工具类与用法示例
May 08 PHP
PHP 观察者模式深入理解与应用分析
Sep 25 PHP
tp5框架无刷新分页实现方法分析
Sep 26 PHP
laravel config文件配置全局变量的例子
Oct 13 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
蝙蝠侠:侠影之谜
2020/03/04 欧美动漫
php中隐形字符65279(utf-8的BOM头)问题
2014/08/16 PHP
PHP提高编程效率的20个要点
2015/09/23 PHP
PHP使用PDO操作数据库的乱码问题解决方法
2016/04/08 PHP
Laravel下生成验证码的类
2017/11/15 PHP
PHP实现的微信APP支付功能示例【基于TP5框架】
2019/09/16 PHP
JavaScript更改class和id的方法
2008/10/10 Javascript
关于js内存泄露的一个好例子
2013/12/09 Javascript
jQuery.holdReady()使用方法
2014/05/20 Javascript
js实现延时加载Flash的方法
2015/11/26 Javascript
javascript中eval和with用法实例总结
2015/11/30 Javascript
EditPlus中的正则表达式 实战(4)
2016/12/15 Javascript
js图片轮播手动切换特效
2017/01/12 Javascript
完美实现js焦点轮播效果(一)
2017/03/07 Javascript
jQuery实现对网页节点的增删改查功能示例
2017/09/18 jQuery
vue router+vuex实现首页登录验证判断逻辑
2018/05/17 Javascript
AngularJS 前台分页实现的示例代码
2018/06/07 Javascript
详解Vue iview IE浏览器不兼容报错(Iview Bable polyfill)
2019/01/07 Javascript
小白教程|一小时上手最流行的前端框架vue(推荐)
2019/04/10 Javascript
vue+element UI实现树形表格带复选框的示例代码
2019/04/16 Javascript
优雅的处理vue项目异常实战记录
2019/06/05 Javascript
Vue+Node实现的商城用户管理功能示例
2019/12/23 Javascript
Vue通过getAction的finally来最大程度避免影响主数据呈现问题
2020/04/24 Javascript
js实现html滑动图片拼图验证
2020/06/24 Javascript
Vue 实现可视化拖拽页面编辑器
2021/02/01 Vue.js
python简单线程和协程学习心得(分享)
2017/06/14 Python
如何在django里上传csv文件并进行入库处理的方法
2019/01/02 Python
python自制包并用pip免提交到pypi仅安装到本机【推荐】
2019/06/03 Python
python处理excel绘制雷达图
2019/10/18 Python
logging level级别介绍
2020/02/21 Python
CSS3中颜色线性渐变实战
2015/07/18 HTML / CSS
英格兰橄榄球商店:England Rugby Store
2016/12/17 全球购物
英文简历中的自我评价
2013/10/06 职场文书
初中生操行评语大全
2014/04/24 职场文书
2015年宣传工作总结
2015/04/08 职场文书
Java基础之this关键字的使用
2021/06/30 Java/Android