通过 TCP/IP 协议传输数据经常会用二进制数据包的形式,在 PHP 中可使用 pack() 和 unpack() 函数进行二进制封包和解包,通过 socket 建立 TCP 连接,并将数据包传输出去。
字节序
在不同的计算机体系结构中,对于数据(比特、字节、字)等的存储和传输机制有所不同,因而引发了计算机领域中一个潜在但是又很重要的问题,即通信双方交流的信息单元应该以什么样的顺序进行传送。如果达不成一致的规则,计算机的通信与存储将会无法进行。目前在各种体系的计算机中通常采用的字节存储机制主要有两种:大端(Big-endian)和小端(Little-endian)。这里所说的大端和小端即是字节序。网络字节序是指大端序。TCP/IP都是采用网络字节序的方式
1 |
|
Pack参数说明
Code | Description |
---|---|
a | 将字符串空白以 NULL 字符填满 |
A | 将字符串空白以 SPACE 字符 (空格) 填满 |
h | 16进制字符串,低位在前以半字节为单位 |
H | 16进制字符串,高位在前以半字节为单位 |
c | 有符号字符 |
C | 无符号字符 |
s | 有符号短整数 (16位,主机字节序) |
S | 无符号短整数 (16位,主机字节序) |
n | 无符号短整数 (16位, 大端字节序) |
v | 无符号短整数 (16位, 小端字节序) |
i | 有符号整数 (依赖机器大小及字节序) |
I | 无符号整数 (依赖机器大小及字节序) |
l | 有符号长整数 (32位,主机字节序) |
L | 无符号长整数 (32位,主机字节序) |
N | 无符号长整数 (32位, 大端字节序) |
V | 无符号长整数 (32位, 小端字节序) |
f | 单精度浮点数 (依计算机的范围) |
d | 双精度浮点数 (依计算机的范围) |
x | 空字节 |
X | 倒回一位 |
@ | 填入 NULL 字符到绝对位置 |
使用例子
比如现在要通过PHP发送数据包到服务器来登录。在仅需要提供用户名(最多30个字节)和密码(md5之后固定为32字节)的情况下,可以构造如下数据包(当然这事先需要跟服务器协商好数据包的规范,本例以网络字节序通信)
包结构
字段 | 字节数 | 说明 |
---|---|---|
包头 | 定长 | 每一个通信消息必须包含的内容 |
包体 | 不定长 | 根据每个通信消息的不同产生变化 |
包头详细内容
字段 | 字节数 | 类型 | 说明 |
---|---|---|---|
pkg_len | 2 | ushort | 整个包的长度,不超过4K |
version | 1 | uchar | 通讯协议版本号 |
command_id | 2 | ushort | 消息命令ID |
result | 2 | short | 请求时不起作用;请求返回时使用 |
Pack打包
包头是定长的,通过计算可知包头占7个字节,并且包头在包体之前。比如用户test需要登录,密码是123456
1 |
|
以上的代码中,pack(“a30a32”, $username, $password);a30表示30个a,您当然可以连续写30个a,但我想您不会这么傻。如果是a*的话,则表示任意多个a。通过服务器端的输出来看,PHP发送了30个字节过去,服务器端也接收了30个字节,但因为填充的\0是空字符,所以您不会看到有什么不一样的地方,a32同理
unpack解包
unpack是用来解包经过pack打包的数据包,如果成功,则返回数组。其中格式化字符和执行pack时一一对应,但是需要额外的指定一个key,用作返回数组的key。多个字段用/分隔。
1 |
|