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 相关文章推荐
如何过滤高亮显示非法字符
Oct 09 PHP
一个典型的PHP分页实例代码分享
Jul 28 PHP
php 中英文语言转换类代码
Aug 11 PHP
PHP fgetcsv 定义和用法(附windows与linux下兼容问题)
May 29 PHP
thinkphp3.2中Lite文件替换框架入口文件或应用入口文件的方法
May 21 PHP
PHP编译安装时常见错误解决办法
May 28 PHP
PHP执行SQL文件并将SQL文件导入到数据库
Sep 17 PHP
ThinkPHP整合datatables实现服务端分页的示例代码
Feb 10 PHP
php文件操作之文件写入字符串、数组的方法分析
Apr 15 PHP
PHP实现基于状态的责任链审批模式详解
May 31 PHP
laravel框架中视图的基本使用方法分析
Nov 23 PHP
php 使用expat方式解析xml文件操作示例
Nov 26 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
php读取本地文件常用函数(fopen与file_get_contents)
2013/09/09 PHP
浅谈PHP中单引号和双引号到底有啥区别呢?
2015/03/04 PHP
php判断一个数组是否为有序的方法
2015/03/27 PHP
PHP实现对数组分页处理实例详解
2017/02/07 PHP
php实现生成PDF文件的方法示例【基于FPDF类库】
2018/07/21 PHP
Laravel5.1框架路由分组用法实例分析
2020/01/04 PHP
js 变量类型转换常用函数与代码[比较全]
2009/12/01 Javascript
Javascript学习笔记4 Eval函数
2010/01/11 Javascript
详细介绍8款超实用JavaScript框架
2013/10/25 Javascript
jQuery对Select的操作大集合(收藏)
2013/12/28 Javascript
浅谈jQuery中的事件
2015/03/23 Javascript
Node.js与Sails ~项目结构与Mvc实现及日志机制
2015/10/14 Javascript
javascript中checkbox使用方法简单实例演示
2015/11/17 Javascript
JavaScript焦点事件、鼠标事件和滚轮事件使用详解
2016/01/15 Javascript
全面解析多种Bootstrap图片轮播效果
2016/05/27 Javascript
利用jquery正则表达式在页面验证url网址输入是否正确
2017/04/04 jQuery
Vue-cli3项目配置Vue.config.js实战记录
2018/07/29 Javascript
基于JS实现简单滑块拼图游戏
2019/10/12 Javascript
[01:57]2018年度DOTA2最具潜力解说-完美盛典
2018/12/16 DOTA
详细探究Python中的字典容器
2015/04/14 Python
使用C#配合ArcGIS Engine进行地理信息系统开发
2016/02/19 Python
python基础教程之Filter使用方法
2017/01/17 Python
Python实现嵌套列表及字典并按某一元素去重复功能示例
2017/11/30 Python
python构建深度神经网络(续)
2018/03/10 Python
python实现校园网自动登录的示例讲解
2018/04/22 Python
Python多线程threading模块用法实例分析
2019/05/22 Python
关于Python 常用获取元素 Driver 总结
2019/11/24 Python
详解python tkinter模块安装过程
2020/01/06 Python
python3.6.5基于kerberos认证的hive和hdfs连接调用方式
2020/06/06 Python
PyInstaller运行原理及常用操作详解
2020/06/13 Python
浅谈keras中的keras.utils.to_categorical用法
2020/07/02 Python
CSS3盒子模型详解
2013/04/24 HTML / CSS
美国真皮手袋品牌:GiGi New York
2017/03/10 全球购物
爱国卫生月实施方案
2014/02/21 职场文书
3分钟英语演讲稿
2014/04/29 职场文书
广播节目策划方案
2014/05/23 职场文书