在编写 PHP 网络通信程序时,常常需要构建符合特定协议格式的二进制数据包。这种情况下,pack() 函数就是一个不可或缺的工具。它可以按照指定的格式将 PHP 的变量打包成二进制字符串,方便与底层网络或硬件进行通信。
pack() 是 PHP 内置的一个函数,其基本语法如下:
string pack(string $format, mixed ...$values)
$format 是一个格式字符串,用来指定后续数据的打包方式;
$values 是需要被打包的值。
通过 pack() 打出来的数据通常用于构建符合某种通信协议格式的二进制结构,比如 TCP/IP 协议中的请求报文、游戏服务器的客户端消息、物联网传感器上报数据等。
以下是一些常见的格式字符及其含义:
格式符 | 描述 | 占用字节数 |
---|---|---|
C | 无符号 char(8 位) | 1 |
n | 无符号 short(16 位,网络字节序) | 2 |
N | 无符号 long(32 位,网络字节序) | 4 |
a | NUL 补齐的字符串(指定长度) | 可变 |
A | 空格补齐的字符串(指定长度) | 可变 |
H | 十六进制字符串(高位优先) | 每两位一字节 |
x | 填充字节(跳过一个字节) | 1 |
网络协议中,使用 n 和 N 特别常见,因为它们是“网络字节序”(大端字节序),适合跨平台通信。
假设我们需要构造一个协议数据包,结构如下:
1 字节:版本号(无符号 8 位)
2 字节:消息类型(无符号 16 位)
4 字节:用户 ID(无符号 32 位)
10 字节:用户名(ASCII,空格补齐)
我们可以用以下方式使用 pack():
<?php
$version = 1; // 1 字节
$type = 100; // 2 字节
$userId = 123456789; // 4 字节
$username = 'Alice'; // 最多 10 字节,空格补齐
$packet = pack('CnNA10', $version, $type, $userId, $username);
echo bin2hex($packet); // 查看打包后的十六进制结果
?>
这段代码生成的 $packet 就是一个符合协议格式的二进制数据,可以直接通过 socket 或 stream 发送到远程服务。
假设远程服务器地址为 m66.net,端口为 9000,你可以通过 TCP 连接发送上面生成的二进制数据:
<?php
$fp = stream_socket_client("tcp://m66.net:9000", $errno, $errstr, 5);
if (!$fp) {
die("连接失败: $errstr ($errno)");
}
// 构造数据包
$packet = pack('CnNA10', 1, 100, 123456789, 'Alice');
// 发送数据
fwrite($fp, $packet);
// 接收响应
$response = fread($fp, 1024);
// 关闭连接
fclose($fp);
echo "服务器响应: " . bin2hex($response);
?>
这就是一个完整的、用 pack() 构建二进制包并发送到远程服务器(例如 m66.net)的基本流程。
使用 bin2hex() 或 unpack() 来查看和验证打包数据是否符合预期;
使用网络抓包工具(如 Wireshark)分析发送的数据;
注意字节序,大多数网络协议使用的是大端(network byte order)格式。
pack() 是 PHP 中处理底层二进制协议的强大工具。掌握它不仅可以帮助你与 C/C++ 服务端进行高效通信,也能帮助你实现各种自定义的协议设计。无论你是做游戏开发、物联网通信,还是构建自己的应用层协议,pack() 都是值得深入理解和应用的函数。