任何一款擁有socket操作能力的語言都有一個專門用于組包的函數,php也不例外!          

用了很久php了卻很少有機會用php進行一些二進制操作。 最近用php寫一個socket客戶端連接一個用C++語言開發(fā)的游戲服務端。 服務器端開發(fā)人員使用了二進制的形式來定義協議的格式。協議格式如下:

包頭(2bytes)+加密(1byte)+命令碼(2bytes)+幀內容

1.包頭的內容是記錄幀內容的長度;
2. 加密:0表示不加密,1表示加密;
3. 命令碼為服務端命令識別符號;

一開始不了解php原來有pack可以來組裝二進制包, 走了彎路,讓服務端開發(fā)人員用C語言幫忙開發(fā)了的幾個內存操作函數,按照協議規(guī)則返回二進制包,然后我將這幾個方法編譯成一組擴展函數供php使用。

話歸正題,本文是介紹如何使用pack和unpack這兩個方法的。php官方手冊舉例太少,不能很容易理解,特別是那些格式化參數的使用。

轉摘的參數中文說明:

pack/unpack 的摸板字符字符 含義
a 一個填充空的字節(jié)串
A 一個填充空格的字節(jié)串
b 一個位串,在每個字節(jié)里位的順序都是升序
B 一個位串,在每個字節(jié)里位的順序都是降序
c 一個有符號 char(8位整數)值
C 一個無符號 char(8位整數)值;關于 Unicode 參閱 U
d 本機格式的雙精度浮點數
f 本機格式的單精度浮點數
h 一個十六進制串,低四位在前
H 一個十六進制串,高四位在前
i 一個有符號整數值,本機格式
I 一個無符號整數值,本機格式
l 一個有符號長整形,總是 32 位
L 一個無符號長整形,總是 32 位
n 一個 16位短整形,“網絡”字節(jié)序(大頭在前)
N 一個 32 位短整形,“網絡”字節(jié)序(大頭在前)
p 一個指向空結尾的字串的指針
P 一個指向定長字串的指針
q 一個有符號四倍(64位整數)值
Q 一個無符號四倍(64位整數)值
s 一個有符號短整數值,總是 16 位
S 一個無符號短整數值,總是 16 位,字節(jié)序跟機器芯片有關
u 一個無編碼的字串
U 一個 Unicode 字符數字
v 一個“VAX”字節(jié)序(小頭在前)的 16 位短整數
V 一個“VAX”字節(jié)序(小頭在前)的 32 位短整數
w 一個 BER 壓縮的整數
x 一個空字節(jié)(向前忽略一個字節(jié))
X 備份一個字節(jié)
Z 一個空結束的(和空填充的)字節(jié)串
@ 用空字節(jié)填充絕對位置


string pack ( string $format [, mixed $args [, mixed $...]] )

一些規(guī)則:
1.每個字母后面都可以跟著一個數字,表示 count(計數),如果 count 是一個 * 表示剩下的所有東西。
2.如果你提供的參數比 $format 要求的少,pack 假設缺的都是空值。如果你提供的參數比 $format 要求的多,那么多余的參數被忽略。

下面還是用例子來說明用法會容易理解一點:

關于Pack:

下面的第一部分把數字值包裝成字節(jié):
$out = pack("CCCC", 65, 66, 67, 68);      # $out 等于"ABCD"
$out = pack("C4", 65, 66, 67, 68);         # 一樣的東西

下面的對 Unicode 的循環(huán)字母做同樣的事情:
$foo = pack("U4", 0x24b6, 0x24b7, 0x24b8, 0x24b9);

下面的做類似的事情,增加了一些空:
$out = pack("CCxxCC", 65, 66, 67, 68);      # $out 等于 "AB\0\0CD"

打包你的短整數并不意味著你就可移植了:
$out = pack("s2", 1, 2);        
# 在小頭在前的機器上是 "\1\0\2\0"
# 在大頭在前的機器上是 "\0\1\0\2"

在二進制和十六進制包裝上,count 指的是位或者半字節(jié)的數量,而不是生成的字節(jié)數量:
$out = pack("B32", "...");
$out = pack("H8", "5065726c");         # 都生成“Perl”

a 域里的長度只應用于一個字串:
$out = pack("a4", "abcd", "x", "y", "z");      # "abcd"

要繞開這個限制,使用多倍聲明:
$out = pack("aaaa",    "abcd", "x", "y", "z");   # "axyz"
$out = pack("a" x 4,   "abcd", "x", "y", "z");   # "axyz"

a 格式做空填充:
$out = pack("a14", "abcdefg");         # " abcdefg\0\0\0\0\0\0"


關于unpack:

array unpack ( string $format, string $data )

$data = "010000020007";
unpack("Sint1/Cchar1/Sint2/Cchar2",$data);

## array('int1'=>1, 'char1'=>'0','int2'=>2,'char2'=>7);

最后本文開頭講到的協議使用pack/unpack 舉例程序代碼為 :

$lastact   = pack('SCSa32a32',0x0040, 0x00, 0x0006, $username, $passwd );

unpack('Sint1/Cchar1/Sint2/Cchar2/',$lastmessage);