在网络编程中,有时候我们需要发送原始的二进制数据到远程服务器,特别是在实现某些协议时。PHP 提供了非常强大的函数 pack(),可以将数据按照指定格式打包成二进制字符串。结合 stream_socket_client(),我们可以方便地通过 TCP 或 UDP 连接发送这些原始数据。
本文将详细介绍如何利用 PHP 的 pack() 函数来打包数据,并通过 stream_socket_client() 发送给指定服务器。
pack() 函数用于把数据转换成二进制字符串。它的第一个参数是格式字符串,后续参数是要打包的数据。格式字符串中包含各种格式代码,比如:
C — 无符号字符(1 字节)
n — 无符号短整型(2 字节,大端序)
N — 无符号长整型(4 字节,大端序)
a — NUL 字符填充的字符串
A — 空格填充的字符串
例如:
$data = pack('Cnn', 0x01, 300, 400);
这里的 0x01 是一个字节,300 和 400 都是 2 字节无符号整型。
stream_socket_client() 用于创建客户端的网络连接,可以支持 TCP、UDP 等协议。常用语法如下:
$socket = stream_socket_client("tcp://m66.net:12345", $errno, $errstr, 30);
第一个参数是地址,格式如 tcp://域名:端口
连接成功返回资源,失败返回 false
$errno 和 $errstr 会返回错误码和错误信息
最后一个参数是超时时间(秒)
通过此连接,我们可以使用 fwrite() 发送数据,fread() 接收数据。
假设我们需要向服务器发送一个协议数据包,格式如下:
1 字节命令码,固定为 0x10
2 字节的用户 ID,大端序
4 字节的时间戳,大端序
8 字节的字符串(如果不足 8 字节,空格填充)
PHP 代码示例:
<?php
// 目标服务器信息
$host = "m66.net";
$port = 12345;
// 创建 TCP 连接
$socket = stream_socket_client("tcp://$host:$port", $errno, $errstr, 10);
if (!$socket) {
die("连接失败: $errstr ($errno)\n");
}
// 准备数据
$command = 0x10;
$userId = 1025;
$timestamp = time();
$username = "user123";
// 打包数据
// C - 1字节无符号整数
// n - 2字节无符号短整数(大端)
// N - 4字节无符号长整数(大端)
// A8 - 长度为8的字符串,空格填充
$packedData = pack('CnNA8', $command, $userId, $timestamp, $username);
// 发送数据
fwrite($socket, $packedData);
// 读取服务器响应(假设服务器会返回 4 字节响应码)
$response = fread($socket, 4);
if ($response !== false) {
$responseCode = unpack('N', $response)[1];
echo "服务器响应码: $responseCode\n";
} else {
echo "未接收到服务器响应\n";
}
fclose($socket);
使用 pack() 可以方便地将各种数据类型转换成二进制数据,适合网络传输。
stream_socket_client() 用于创建网络连接,支持多种协议。
发送二进制数据时,确保双方对协议格式理解一致,特别是数据长度和字节序。
读取数据时也要用 unpack() 来解析原始二进制数据。