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

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要求的多,那么多余的参数被忽略。

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

$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

if($argc==0) $argc = 1;

while($parsepos

$type = substr($parse,$parsepos,1);

if($type>='0'&&$type<='9'){

$parsepos++;

}else{

break;

}

}

/*获取字符串的命名*/

$namepos= $parsepos;

$type = '';

while($parsepos

$type= substr($parse,$parsepos,1);

$parsepos++;

$namelen++;

if($type=='/') break;

}

$strname = substr($parse,$namepos,$parsepos-$namepos-($type=='/'?1:0));

/*处理各个字符串*/

for($i=0;$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

$argc = intval(substr($parse,$parsepos));

if($argc==0){

$argc = 1;

}else{

/*unpack代码限制了只能200*/

if($argc>200)

$argc= 200;

}

$argvlen+= $argc*$size;

/*移动解析参数指针*/

while($parsepos

$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

)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值