当前位置:  编程语言>php

php通过pack和unpack函数实现对二进制数据封装及解析

 
    发布时间:2013-7-18  


    本文导语: 1. pack和unpack格式字符串解析:a一个填充空的字节串 A一个填充空格的字节串 b一个位串,在每个字节里位的顺序都是升序 B一个位串,在每个字节里位的顺序都是降序 c一个有符号char(8位整数)值 C一个无符号char(8位整数)值;...

1. packunpack格式字符串解析:

a一个填充空的字节串

A一个填充空格的字节串

b一个位串,在每个字节里位的顺序都是升序

B一个位串,在每个字节里位的顺序都是降序

c一个有符号char(8位整数)值

C一个无符号char(8位整数)值;关于Unicode参阅U

d本机格式的双精度浮点数

f本机格式的单精度浮点数

h一个十六进制串,低四位在

H一个十六进制串,高四位在前

i一个有符号整数值,本机格式

I一个无符号整数值,本机格式

l一个有符号长整形,总是32位

L一个无符号长整形,总是32位

n一个16位短整形,“网络”字节序(大头在前)

N一个32位短整形,“网络”字节序(大头在前)

p一个指向空结尾的字串的指针

P一个指向定长字串的指针

q一个有符号四倍(64位整数)值

Q一个无符号四倍(64位整数)值

s一个有符号短整数值,总是16位

S一个无符号短整数值,总是16位,字节序跟机器芯片有关

u一个无编码的字串

U一个Unicode字符数字

v一个“VAX”字节序(小头在前)的16位短整数

V一个“VAX”字节序(小头在前)的32位短整数

w一个BER压缩的整数

x一个空字节(向前忽略一个字节)

X备份一个字节

Z一个空结束的(和空填充的)字节串

规则:

(1).每个字母后面都可以跟着一个数字,表示count计数,如果count是一个*表示剩下的所有东西。

(2)如果你提供的参数比$format要求的少,pack假设缺的都是空值。如果你提供的参数比$format要求的多,那么多余的参数被忽略。

2.php通过socketjava server通信

  php跟java进行socket通讯的时候,php发送一段数据给java,(协议自定,这里假定类型10表示获取游戏邮件列表,10000表示获取的id)  

socket_write($sock,pack('CN',10,10000),5);

 java接受到后,会返回一段数据,从中获得你所需要的,比如java先告诉你返回内容规则如下:1 byte,2 int

 php可以通过如下方式获得:

$arr=unpack('Csuccess/Nid/Ncount',$data);

这样就完成一次解析过程.

 这里我们都没有提到字符串的发送,我们知道字符串在字节流里的存储方式是前2个字节表示字符串的长度,后面表示字符串的具体内容(学过java的应该都了解),2个字节也就限制了发送长度最大为65536,因而我们要发送字符串需要如下(以下举例都在utf8下完成):

function pack_str($str){
    //如果是gbk,要转成utf8
    // $str = iconv('gbk','utf-8',$str);
  $utflen = strlen($str);
  if ($utflen > 65535) die('too long');
  $in .= pack('C2',$utflen>>8,$utflen>>0);
  return $in.$str;
}

比如我们要向游戏服务器内发送一个公告:各位,服务器在1小时内重起!假设java要求这样的格式:协议号:int,标题,内容。我们就可以如下发送:

$in=pack('N',1000);
$in.=pack_utf8('公告');
$in.=pack_utf8('各位,服务器在1小时内重起!');

这样就完成一次发送.同样如果我们需要读取游戏服务器的数据,比如用户资料,也会返回字符串,原理同上,先读2个字节获取长度,再根据长度来获取具体的内容,代码如下:

$crt_str  =unpack("C{$crt_str_len}str",$data);
      for($ii=1;$ii<=$crt_str_len;$ii++){
       $str .= chr($crt_str['str'.$ii]);
  }

$str就是我们要获取的中文,但是这样极其烦琐,如果有多个字符串的话,中间又包含了其他数据,比如返回为int,string,int,byte,string这样处理起来相当不便,于是提供下面的函数供大家参考:

<?php
/*
由于我的程序经常跟java通信,所以此函数所使用的参数是用java里面的类型来填充的,并且只替换了经常用到的3个类型
C-->b(byte)
n-->s(short)
i-->N(int)
如不习惯或或觉得参数过少,请自行修改
*/                                                                                                                                  
  function rs_unpack($parse,$data)
  {
    $parselen        = strlen($parse);
    $parsepos        = 0;
    $datapos        = 0;
    $argc                = 1;
    $ret                = array();
                                                                                                                                                                             
    while($parsepos<$parselen){
            $dostr                = false;
            $type        = substr($parse,$parsepos++,1);
            switch($type){
              case 'b':
                      $size        = 1;
                      $argv        .= 'C';
                      break;
              case 's':
                      $size        = 2;
                      $argv        .= 'n';
                      break;
              case 'i':
                      $size        = 4;
                      $argv        .= 'N';
                      break;
              case 'Z':
                      $dostr        = true;
                      /*处理字符传之前的数据*/
                      $arr        = unpack($argv,substr($data,$datapos,$argvlen));
                      $datapos        += $argvlen;
                      $argvlen        = 0;
                      $argv                = '';
                      $ret        = array_merge($ret,$arr);
                      /*获取要解析的字符串的个数,并移动指针*/
                      if($parsepos<$parselen)        $argc        = intval(substr($parse,$parsepos));
                      if($argc==0)        $argc        = 1;
                      while($parsepos<$parselen){
                              $type        = substr($parse,$parsepos,1);
                              if($type>='0'&&$type<='9'){
                                      $parsepos++;
                              }else{
                                      break;
                              }
                      }
                      /*获取字符串的命名*/
                      $namepos= $parsepos;
                      $type   = '';
                      while($parsepos<$parselen){
                              $type= substr($parse,$parsepos,1);
                              $parsepos++;
                              $namelen++;
                              if($type=='/')        break;
                      }
                      $strname = substr($parse,$namepos,$parsepos-$namepos-($type=='/'?1:0));
                                                                                                                                                                                               
                      /*处理各个字符串*/
                      for($i=0;$i<$argc;$i++){
                          $str= '';
                          $crt_len_arr = unpack('nstr_len',substr($data,$datapos,2));
                          $datapos+= 2;
                          $crt_str_len = $crt_len_arr['str_len'];
                          $crt_str = unpack("C{$crt_str_len}str",substr($data,$datapos,$crt_str_len));
                          for($ii=1;$ii<=$crt_str_len;$ii++){
                                  $str.= chr($crt_str['str'.$ii]);
                          }
                                                                                                                                                                                           
                          $ret= array_merge($ret,array($strname.($argc>1?($i+1):'')=>$str));
                          $datapos+= $crt_str_len;
                      }
                      break;
              default:
                      die('parse error');
            }
            if($dostr)        continue;
            /*获取数据长度*/
            if($parsepos<$parselen)     
               $argc  = intval(substr($parse,$parsepos));
            if($argc==0){
                    $argc = 1;
            }else{
                    /*unpack代码限制了只能200*/
                    if($argc>200)     
                      $argc= 200;
            }
            $argvlen+= $argc*$size;
            /*移动解析参数指针*/
            while($parsepos<$parselen){
                    $type = substr($parse,$parsepos,1);
                    $argv .= $type;
                    $parsepos++;
                    if($type=='/') break;
            }
    }
    if(!empty($argv)){
            $ret = array_merge($ret,unpack($argv,substr($data,$datapos)));
    }
    return $ret;
  }
  function pack_str($str){
  //        $str        = iconv('gbk','utf-8',$str);
          $utflen = strlen($str);
          if ($utflen > 65535)     
             die('too long');
          $in  .= pack('C2',$utflen>>8,$utflen>>0);
          return $in.$str;
  }
  $in  .= pack('C',10);
  $in  .= pack_str("标题");
  $in  .= pack('C',10);
  $in  .= pack_str("内容");
  print_r(rs_unpack('bbyte/Zstr/be/Zstrw',$in));
  /*比如java发送int,string,string,分别表示协议号,标题,内容
  这里用php模拟发送的数据
  */
  $in        = pack('N',1000);
  $in        .= pack_str("公告");
  $in        .= pack_str("服务器在10分钟内重启!");
  print_r(rs_unpack('i/Ztitle/Zcontent',$in));
  print_r(rs_unpack('i/Z2str',$in));
?>

需要注意的是:

(1)很多服务器都会用utf8编码的格式,所以我们的php文件也必须使用同样的编码,否则会出乱码,或其他问题

(2)该函数我只处理了4种类行,并且参数用java的类型代替了unpack原来的参数类型,如需处理其他类型,请自行修改

2.其它举例:

例子1

<?php
$data = "PHP";
print_r(unpack("C*",$data));
?>

输出

Array
(
[1] => 80
[2] => 72
[3] => 80
)

例子 2

<?php
$data = "PHP";
print_r(unpack("C*myint",$data));
?>

输出:

Array
(
[myint1] => 80
[myint2] => 72
[myint3] => 80
)

例子 3

<?php
$bin = pack("c2n2",0x1234,0x5678,65,66);
print_r(unpack("c2chars/n2int",$bin));
?>

输出:

Array
(
[chars1] => 52
[chars2] => 120
[int1] => 65
[int2] => 66
)


相关文章推荐:
  • PHP的substr() 函数用法
  • php构造函数与析构函数
  • php中检测变量是否是一个对象的is_object函数介绍及用法举例
  • php构造函数与析构函数初探
  • php session_id()函数介绍及代码实例
  • PHP中的Pack()函数,Java有哪个函数与之对应???
  • php中session_id()函数详细介绍,会话id生成过程及session id长度
  • php构造函数与析构函数 php内存管理函数
  • php将html特殊字符转换成html字符串的函数:htmlspecialchars()介绍及代码举例
  • php数组函数之array_combine() 数组合并函数
  • php使用socket_bind()函数绑定IP地址
  • php构造函数和析构函数学习
  • php将unix时间戳转换成字符串时间函数(date)
  • php trim函数执行过程解析
  • php时间格式化函数date介绍及用法参考
  • php递归函数小例子
  • php sprintf函数用法 php浮点数格式
  • php递归示例 php递归函数代码
  • php构造函数与析构函数实例分析
  • 在php中如何用exec()函数运行mv命令
  • php递归使用示例(php递归函数)


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3