1. 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要求的多,那么多余的参数被忽略。
2.php通过socket和java server通信
php跟java进行socket通讯的时候,php发送一段数据给java,(协议自定,这里假定类型10表示获取游戏邮件列表,10000表示获取的id)
1 | socket_write( $sock ,pack( 'CN' ,10,10000),5); |
java接受到后,会返回一段数据,从中获得你所需要的,比如java先告诉你返回内容规则如下:1 byte,2 int
php可以通过如下方式获得:
1 | $arr =unpack( 'Csuccess/Nid/Ncount' , $data ); |
这样就完成一次解析过程.
这里我们都没有提到字符串的发送,我们知道字符串在字节流里的存储方式是前2个字节表示字符串的长度,后面表示字符串的具体内容(学过java的应该都了解),2个字节也就限制了发送长度最大为65536,因而我们要发送字符串需要如下(以下举例都在utf8下完成):
1 2 3 4 5 6 7 8 | 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,标题,内容。我们就可以如下发送:
1 2 3 | $in =pack( 'N' ,1000); $in .=pack_utf8( '公告' ); $in .=pack_utf8( '各位,服务器在1小时内重起!' ); |
这样就完成一次发送.同样如果我们需要读取游戏服务器的数据,比如用户资料,也会返回字符串,原理同上,先读2个字节获取长度,再根据长度来获取具体的内容,代码如下:
1 2 3 4 | $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这样处理起来相当不便,于是提供下面的函数供大家参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | <?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
1 2 3 4 | <?php $data = "PHP" ; print_r(unpack( "C*" , $data )); ?> |
输出:
1 2 3 4 5 6 | Array ( [1] => 80 [2] => 72 [3] => 80 ) |
例子 2
1 2 3 4 | <?php $data = "PHP" ; print_r(unpack( "C*myint" , $data )); ?> |
输出:
1 2 3 4 5 6 | Array ( [myint1] => 80 [myint2] => 72 [myint3] => 80 ) |
例子 3
1 2 3 4 | <?php $bin = pack( "c2n2" ,0x1234,0x5678,65,66); print_r(unpack( "c2chars/n2int" , $bin )); ?> |
输出:
1 2 3 4 5 6 7 | Array ( [chars1] => 52 [chars2] => 120 [int1] => 65 [int2] => 66 ) |
联系客服