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 反向排序和随机排序代码
Jun 30 PHP
解析在PHP中使用全局变量的几种方法
Jun 24 PHP
CodeIgniter上传图片成功的全部过程分享
Aug 12 PHP
PHP利用REFERER根居访问来地址进行页面跳转
Sep 28 PHP
PHP文件读写操作相关函数总结
Nov 18 PHP
php正则匹配文章中的远程图片地址并下载图片至本地
Sep 29 PHP
PHP常见错误提示含义解释(实用!值得收藏)
Apr 25 PHP
PHP获取不了React Native Fecth参数的解决办法
Aug 26 PHP
thinkphp分页实现效果
Oct 13 PHP
PHP实现蛇形矩阵,回环矩阵及数字螺旋矩阵的方法分析
May 29 PHP
Laravel给生产环境添加监听事件(SQL日志监听)
Jun 19 PHP
php设计模式之观察者模式定义与用法经典示例
Sep 19 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实现的二分查找算法实例分析
Dec 19 #PHP
You might like
《心理测量者3》剧场版动画预告
2020/03/02 日漫
第十一节--重载
2006/11/16 PHP
关于mysql字符集设置了character_set_client=binary 在gbk情况下会出现表描述是乱码的情况
2013/01/06 PHP
PHP分页详细讲解(有实例)
2013/10/30 PHP
php+js实现百度地图多点标注的方法
2016/11/30 PHP
php 生成加密公钥加密私钥实例详解
2017/06/16 PHP
laravel 框架配置404等异常页面
2019/01/07 PHP
jQuery实现下拉框左右选择的简单实例
2014/02/22 Javascript
5种处理js跨域问题方法汇总
2014/12/04 Javascript
javascript实现微信分享
2014/12/23 Javascript
jQuery创建自定义的选择器用以选择高度大于100的超链接实例
2015/03/18 Javascript
js创建数组的简单方法
2016/07/27 Javascript
Javascript 引擎工作机制详解
2016/11/30 Javascript
javascript实现鼠标点击页面 移动DIV
2016/12/02 Javascript
js 性能优化之算法和流程控制
2017/02/15 Javascript
使用store来优化React组件的方法
2017/10/23 Javascript
vue二级路由设置方法
2018/02/09 Javascript
Vue头像处理方案小结
2018/07/26 Javascript
angularJS实现不同视图同步刷新详解
2018/10/09 Javascript
微信小程序云开发如何实现数据库自动备份实现
2019/08/16 Javascript
[04:16]DOTA2全国高校联赛16强抽签
2018/05/02 DOTA
Python 深入理解yield
2008/09/06 Python
python共享引用(多个变量引用)示例代码
2013/12/04 Python
由Python运算π的值深入Python中科学计算的实现
2015/04/17 Python
pip安装时ReadTimeoutError的解决方法
2018/06/12 Python
python画图把时间作为横坐标的方法
2019/07/07 Python
python3图片文件批量重命名处理
2019/10/31 Python
python-地图可视化组件folium的操作
2020/12/14 Python
印度婴儿用品在线商店:Firstcry.com
2016/12/05 全球购物
阿迪达斯法国官方网站:adidas法国
2018/03/20 全球购物
住房租房协议书
2014/08/20 职场文书
2014年电信员工工作总结
2014/12/19 职场文书
2015年社区中秋节活动总结
2015/03/23 职场文书
新课程改革心得体会
2016/01/22 职场文书
react antd实现动态增减表单
2021/06/03 Javascript
css实现左上角飘带效果的完整代码
2022/03/18 HTML / CSS