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中对2个数组相加的函数
Jun 24 PHP
PHP详解ASCII码对照表与字符转换
Dec 05 PHP
使用PHP生成二维码的两种方法(带logo图像)
Mar 14 PHP
利用php下载xls文件(自己动手写的)
Apr 18 PHP
PHP中使用循环实现的金字塔图形
Nov 08 PHP
CentOS 安装 PHP5.5+Redis+XDebug+Nginx+MySQL全纪录
Mar 25 PHP
Laravel 5框架学习之子视图和表单复用
Apr 09 PHP
PHP 数组基本操作方法详解
Jun 17 PHP
Thinkphp3.2.3整合phpqrcode生成带logo的二维码
Jul 21 PHP
PHP使用imagick扩展实现合并图像的方法
Apr 25 PHP
Yii框架ACF(accessController)简单权限控制操作示例
Apr 26 PHP
laravel框架中控制器的创建和使用方法分析
Nov 23 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
php中filter函数验证、过滤用户输入的数据
2014/01/13 PHP
php使用cookie保存用户登录的用户名实例
2015/01/26 PHP
PHP使用FFmpeg获取视频播放总时长与码率等信息
2016/09/13 PHP
xml分页+ajax请求数据源+dom取结果实例代码
2008/10/31 Javascript
Javascript中的变量使用说明
2010/05/18 Javascript
基于jquery的lazy loader插件实现图片的延迟加载[简单使用]
2011/05/07 Javascript
深入理解JavaScript中的传值与传引用
2013/12/09 Javascript
JS获得选取checkbox整行数据的方法
2015/01/28 Javascript
jquery实现动态改变div宽度和高度
2015/05/08 Javascript
常常会用到的截取字符串substr()、substring()、slice()方法详解
2015/12/16 Javascript
JS 循环li添加点击事件 (闭包的应用)
2016/12/10 Javascript
如何用js判断dom是否有存在某class的值
2017/02/13 Javascript
Vue2.0 vue-source jsonp 跨域请求
2017/08/04 Javascript
微信小程序实现顶部普通选项卡效果(非swiper)
2020/06/19 Javascript
jQuery中each方法的使用详解
2018/03/18 jQuery
Rollup处理并打包JS文件项目实例代码
2018/05/31 Javascript
vue自动路由-单页面项目(非build时构建)
2019/04/30 Javascript
Jquery异步上传文件代码实例
2019/11/13 jQuery
JavaScript中变量提升机制示例详解
2019/12/27 Javascript
在Python程序和Flask框架中使用SQLAlchemy的教程
2016/06/06 Python
Linux下python3.6.1环境配置教程
2018/09/26 Python
python 解决动态的定义变量名,并给其赋值的方法(大数据处理)
2018/11/10 Python
解决新django中的path不能使用正则表达式的问题
2018/12/18 Python
Python获取数据库数据并保存在excel表格中的方法
2019/06/12 Python
python中@property的作用和getter setter的解释
2020/12/22 Python
儿科主治医生个人求职信
2013/09/23 职场文书
银行实习鉴定
2013/12/13 职场文书
工作室成员个人发展规划范文
2014/01/24 职场文书
青年志愿者事迹材料
2014/02/07 职场文书
情况说明书格式范文
2014/05/06 职场文书
高考标语大全
2014/06/05 职场文书
小学综治宣传月活动总结
2014/07/02 职场文书
2014最新党员违纪检讨书
2014/10/12 职场文书
开学第一周总结
2015/07/16 职场文书
解决Navicat for MySQL 连接 MySQL 报2005错误的问题
2021/05/29 MySQL
Win11跳过联网界面创建本地管理账户的3种方法
2022/04/20 数码科技