详解PHP数据压缩、加解密(pack, unpack)

2018-09-07 22:16

阅读:343

  网络通信、文件存储中经常需要交换数据,为了减少网络通信流量、文件存储大小以及加密通信规则,经常需要对数据进行双向加解密以保证数据的安全。

  PHP中实现此功能主要需要使用的函数主要是pack及unpack函数

  pack

  压缩资料到位字符串之中。

  语法: string pack(string format, mixed [args]...);

  返回值: 字符串

  本函数用来将资料压缩打包到位的字符串之中。

  a - NUL- 字符串填满[padded string] 将字符串空白以 NULL 字符填满

  A - SPACE- 字符串填满[padded string]

  h – 十六进制字符串,低“四位元”[low nibble first] (低位在前)

  H - 十六进制字符串,高“四位元”[high nibble first](高位在前)

  c – 带有符号的字符

  C – 不带有符号的字符

  s – 带有符号的短模式[short](通常是16位,按机器字节顺序)

  S – 不带有符号的短模式[short](通常是16位,按机器字节排序)

  n -不带有符号的短模式[short](通常是16位,按大endian字节排序)

  v -不带有符号的短模式[short](通常是16位,按小endian字节排序)

  i – 带有符号的整数(由大小和字节顺序决定)

  I – 不带有符号的整数(由大小和字节顺序决定)

  l– 带有符号的长模式[long](通常是32位,按机器字节顺序)

  L – 不带有符号的长模式[long](通常是32位,按机器字节顺序)

  N – 不带有符号的长模式[long](通常是32位,按大edian字节顺序)

  V– 不带有符号的长模式[long](通常是32位,按小edian字节顺序)

  f –浮点(由大小和字节顺序决定)

  d – 双精度(由大小和字节顺序决定)

  x – 空字节[NUL byte]

  X- 后面一个字节[Back up one byte](倒回一位)

  unpack

  解压缩位字符串资料。

  语法: string pack(string format, mixed [args]...);

  返回值: 数组

  本函数用来将位的字符串的资料解压缩。本函数和 Perl 的同名函数功能用法完全相同。

  案例一、pack实现缩减文件数据存储大小

   <?php //存储整数1234567890 file_put_contents(test.txt, 1234567890);

  此时test.txt的文件大小是10byte。注意此时文件大小是10字节,实际占用空间大小是1KB。

  上面存储的整数实际是以字符串形式存储于文件test.txt中。

  但如果以整数的二进制字符串存jy储,将会缩减至4byte。

   <?php print_r(unpack(i, file_get_contents(test.txt)));

  案例二、数据加密

  以字符串形式存储一段有意义数据,7-110-abcdefg-117。

  字符-分割后,第一位表示字符串长度,第二位表示存储位置,第三位表示实际存储的字符串,第四位表示结尾位置。

   <?php file_put_contents(test.txt, 7-110-abcdefg-117);

  上述方法缺点:

  一、数据存储大小

  二、数据以明文方式存储,如果是任何敏感信息,都可能造成不安全访问。

  三、文件存储大小,以不规则方式递增。

  加密:

   <?php file_put_contents(test.txt, pack(i2a7i1, 7, 110, abcdefg, 117));

  存储一段数据,加密格式为:整数2位长度字符串10位长度整数1位长度。

  优点:

  一、数据大小最优化

  二、在不知道i2a7i1这样的压缩格式时,即使拿到文件,也无法正确读出二进制文件转化为明文。

  三、数据增加时,文件存储大小是等量递增。每次都是以19byte递增。

  案例三、key-value型文件存储

  存储生成的文件为两个:索引文件,数据文件

  文件中数据存储的格式如下图:

  代码实现:

   <?php error_reporting(E_ALL); class fileCacheException extends Exception{ } //Key-Value型文件存储 class fileCache{ private $_file_header_size = 14; private $_file_index_name; private $_file_data_name; private $_file_index;//索引文件句柄 private $_file_data;//数据文件句柄 private $_node_struct;//索引结点结构体 private $_inx_node_size = 36;//索引结点大小 public function __construct($file_index=filecache_index.dat, $file_data=filecache_data.dat){ $this->_node_struct = array( next=>array(1, V), prev=>array(1, V), data_offset=>array(1,V),//数据存储起始位置 data_size=>array(1,V),//数据长度 ref_count=>array(1,V),//引用此处,模仿PHP的引用计数销毁模式 key=>array(16,H*),//存储KEY ); $this->_file_index_name = $file_index; $this->_file_data_name = $file_data; if(!file_exists($this->_file_index_name)){ $this->_create_index(); }else{ $this->_file_index = fopen($this->_file_index_name, rb+); } if(!file_exists($this->_file_data_name)){ $this->_create_data(); }else{ $this->_file_data = fopen($this->_file_data_name, rb+);//二进制存储需要使用b } } //创建索引文件 private function _create_index(){ $this->_file_index = fopen($this->_file_index_name, wb+);//二进制存储需要使用b if(!$this->_file_index) throw new fileCacheException(Couldt open index file:.$this->_file_index_name); $this->_index_puts(0, <.?php exit()?.>);//定位文件流至起始位置0, 放置php标记防止下载 $this->_index_puts($this->_file_header_size, pack(V1, 0)); } //创建存储文件 private function _create_data(){ $this->_file_data = fopen($this->_file_data_name, wb+);//二进制存储需要使用b if(!$this->_file_index) throw new fileCacheException(Couldt open index file:.$this->_file_data_name); $this->_data_puts(0, <.?php exit()?.>);//定位文件流至起始位置0, 放置php标记防止下载 } private function _index_puts($offset, $data, $length=false){ fseek($this->_file_index, $offset); if($length) fputs($this->_file_index, $data, $length); else fputs($this->_file_index, $data); } private function _data_puts($offset, $data, $length=false){ fseek($this->_file_data, $offset); if($length) fputs($this->_file_data, $data, $length); else fputs($this->_file_data, $data); } /** * 文件锁 * @param $is_block 是否独占、阻塞锁 */ private function _lock($file_res, $is_block=true){ flock($file_res, $is_block ? LOCK_EX : LOCK_EXLOCK_NB); } private function _unlock($file_res){ flock($file_res, LOCK_UN); } public function add($key, $value){ $key = md5($key); $value = serialize($value); $this->_lock($this->_file_index, true); $this->_lock($this->_file_data, true); fseek($this->_file_index, $this->_file_header_size); list(, $index_count) = unpack(V1, fread($this->_file_index, 4)); $data_size = filesize($this->_file_data_name); fseek($this->_file_data, $data_size); $value_size = strlen($value); $this->_data_puts(filesize($this->_file_data_name), $value); $node_data = pack(V1V1V1V1V1H32, ($index_count==0) ? 0 : $index_count*$this->_inx_node_size, 0, filesize($this->_file_data_name), strlen($value), 0, $key); $index_count++; $this->_index_puts($this->_file_header_size, $index_count, 4); $this->_index_puts($this->get_new_node_pos($index_count), $node_data); $this->_unlock($this->_file_data); $this->_unlock($this->_file_index); } public function get_new_node_pos($index_count){ return $this->_file_header_size + 4 + $this->_inx_node_size * ($index_count-1); } public function get_node($key){ $key = md5($key); fseek($this->_file_index, $this->_file_header_size); $index_count = fread($this->_file_index, 4); if($index_count>0) { for ($i=0; $i < $index_count ; $i++) { fseek($this->_file_index, $this->_file_header_size + 4 + $this->_inx_node_size * $i); $data = fread($this->_file_index, $this->_inx_node_size); $node = unpack(V1next/V1prev/V1data_offset/V1data_size/V1ref_count/H32key, $data); if($key == $node[key]){ return $node; } } }else{ return null; } } public function get_data($offset, $length){ fseek($this->_file_data, $offset); return unserialize(fread($this->_file_data, $length)); } } //使用方法 $cache = new fileCache(); $cache->add(abcdefg , testabc); $data = $cache->get_node(abcdefg); print_r($data); echo $cache->get_data($data[data_offset], $data[data_size]);

  案例四、socket通信加密

  通信双方都定义好加密格式:

  例如:

   $LOGIN = array( COMMAND=>array(a30, LOGIN), DATA=>array(a30, HELLO) ); $LOGOUT = array( COMMAND=>array(a30, LOGOUT), DATA=>array(a30, GOOD BYE) ); $LOGIN_SUCCESS = array( COMMAND=>array(a30, LOGIN_SUCCESS), DATA=>array(V1, 1) ); $LOGOUT_SUCCESS = array( COMMAND=>array(a30, LOGIN_SUCCESS), DATA=>array(V1, time()) );

  服务器端与客户端根据解析COMMAND格式,找到对应的DATA解码方式,得到正确的数据

  以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。


评论


亲,登录后才可以留言!