本文目录导读:

我来详细解释PHP项目中解析设备报文的数据协议方法:
常见报文格式及解析方案
1 固定长度报文
class FixedLengthParser {
public function parse($rawData) {
// 假设报文格式:头(2字节) + 长度(2字节) + 数据 + 校验(1字节)
$header = substr($rawData, 0, 2);
$length = unpack('n', substr($rawData, 2, 2))[1];
$data = substr($rawData, 4, $length);
$checksum = ord(substr($rawData, -1));
// 校验
if ($this->verifyChecksum($rawData)) {
return [
'header' => bin2hex($header),
'length' => $length,
'data' => $data,
'checksum' => $checksum
];
}
return false;
}
}
2 可变长度报文(带分隔符)
class VariableLengthParser {
public function parse($buffer) {
$startMarker = 0xAA; // 起始标识
$endMarker = 0xBB; // 结束标识
// 查找报文边界
$startPos = strpos($buffer, chr($startMarker));
if ($startPos === false) return false;
$endPos = strpos($buffer, chr($endMarker), $startPos);
if ($endPos === false) return false;
// 提取完整报文
$packet = substr($buffer, $startPos, $endPos - $startPos + 1);
return $this->decodePacket($packet);
}
}
完整设备协议解析示例
1 协议定义类
class DeviceProtocol {
const HEADER = 0xAA55;
const PROTOCOL_VERSION = 0x01;
// 指令类型
const CMD_HEARTBEAT = 0x01;
const CMD_DATA_REPORT = 0x02;
const CMD_CONFIG = 0x03;
// 数据类型
const DATA_TYPE_TEMPERATURE = 0x01;
const DATA_TYPE_HUMIDITY = 0x02;
const DATA_TYPE_PRESSURE = 0x03;
}
2 报文解析器
class PacketParser {
private $buffer = '';
private $deviceId;
public function __construct($deviceId) {
$this->deviceId = $deviceId;
}
/**
* 解析设备报文
* @param string $rawData 原始二进制数据
* @return array|false
*/
public function parse($rawData) {
$this->buffer .= $rawData;
while (strlen($this->buffer) >= 12) { // 最小报文长度
$packet = $this->extractPacket();
if ($packet === false) {
$this->buffer = substr($this->buffer, 1);
continue;
}
$result = $this->decodePacket($packet);
if ($result !== false) {
return $result;
}
}
return false;
}
/**
* 提取完整报文
*/
private function extractPacket() {
$pos = strpos($this->buffer, pack('n', DeviceProtocol::HEADER));
if ($pos === false || $pos > 0) {
return false;
}
// 检查最小长度
if (strlen($this->buffer) < 6) return false;
// 解析报文长度
$length = unpack('n', substr($this->buffer, 4, 2))[1];
// 检查是否收到完整报文
if (strlen($this->buffer) < $length + 6) return false;
$packet = substr($this->buffer, 0, $length + 6);
$this->buffer = substr($this->buffer, $length + 6);
return $packet;
}
/**
* 解码报文
*/
private function decodePacket($packet) {
$header = unpack('n', substr($packet, 0, 2))[1];
$version = ord(substr($packet, 2, 1));
$cmd = ord(substr($packet, 3, 1));
$length = unpack('n', substr($packet, 4, 2))[1];
$payload = substr($packet, 6, $length);
$checksum = ord(substr($packet, -1));
// 验证校验和
if (!$this->verifyChecksum($packet)) {
return false;
}
return [
'header' => dechex($header),
'version' => $version,
'command' => $cmd,
'payload' => $this->parsePayload($cmd, $payload),
'timestamp' => time()
];
}
/**
* 解析负载数据
*/
private function parsePayload($cmd, $payload) {
switch ($cmd) {
case DeviceProtocol::CMD_HEARTBEAT:
return $this->parseHeartbeat($payload);
case DeviceProtocol::CMD_DATA_REPORT:
return $this->parseDataReport($payload);
case DeviceProtocol::CMD_CONFIG:
return $this->parseConfig($payload);
default:
return ['raw' => bin2hex($payload)];
}
}
/**
* 解析心跳包
*/
private function parseHeartbeat($payload) {
return [
'device_status' => ord($payload[0]),
'battery' => ord($payload[1]),
'signal_strength' => ord($payload[2])
];
}
/**
* 解析数据上报
*/
private function parseDataReport($payload) {
$data = [];
$offset = 0;
while ($offset < strlen($payload)) {
$dataType = ord($payload[$offset]);
$dataLength = ord($payload[$offset + 1]);
$value = substr($payload, $offset + 2, $dataLength);
switch ($dataType) {
case DeviceProtocol::DATA_TYPE_TEMPERATURE:
$data['temperature'] = unpack('f', $value)[1];
break;
case DeviceProtocol::DATA_TYPE_HUMIDITY:
$data['humidity'] = unpack('f', $value)[1];
break;
case DeviceProtocol::DATA_TYPE_PRESSURE:
$data['pressure'] = unpack('f', $value)[1];
break;
}
$offset += 2 + $dataLength;
}
return $data;
}
/**
* 验证校验和
*/
private function verifyChecksum($packet) {
$data = substr($packet, 0, -1);
$checksum = ord(substr($packet, -1));
$calculated = 0;
for ($i = 0; $i < strlen($data); $i++) {
$calculated ^= ord($data[$i]);
}
return $calculated === $checksum;
}
}
3 报文构造器(发送指令)
class PacketBuilder {
/**
* 构造配置指令
*/
public static function buildConfigCommand($params) {
$payload = '';
foreach ($params as $key => $value) {
$payload .= chr($key) . chr(strlen($value)) . $value;
}
return self::buildPacket(
DeviceProtocol::CMD_CONFIG,
$payload
);
}
/**
* 构造应答报文
*/
public static function buildAck($cmd, $status = 0) {
$payload = chr($status) . chr($cmd);
return self::buildPacket(0x80 | $cmd, $payload);
}
/**
* 构造完整报文
*/
private static function buildPacket($cmd, $payload) {
$header = pack('n', DeviceProtocol::HEADER);
$version = chr(DeviceProtocol::PROTOCOL_VERSION);
$command = chr($cmd);
$length = pack('n', strlen($payload));
$data = $header . $version . $command . $length . $payload;
$checksum = self::calculateChecksum($data);
return $data . chr($checksum);
}
/**
* 计算校验和
*/
private static function calculateChecksum($data) {
$checksum = 0;
for ($i = 0; $i < strlen($data); $i++) {
$checksum ^= ord($data[$i]);
}
return $checksum;
}
}
完整使用示例
class DeviceCommunication {
private $parser;
private $deviceId;
public function __construct($deviceId) {
$this->deviceId = $deviceId;
$this->parser = new PacketParser($deviceId);
}
/**
* 处理接收到的数据
*/
public function handleReceivedData($rawData) {
$result = $this->parser->parse($rawData);
if ($result === false) {
return false;
}
// 根据指令类型处理
switch ($result['command']) {
case DeviceProtocol::CMD_HEARTBEAT:
return $this->handleHeartbeat($result['payload']);
case DeviceProtocol::CMD_DATA_REPORT:
return $this->handleDataReport($result['payload']);
case DeviceProtocol::CMD_CONFIG:
return $this->handleConfig($result['payload']);
}
}
private function handleHeartbeat($payload) {
// 更新设备在线状态
$this->updateDeviceStatus($payload);
// 返回应答
return PacketBuilder::buildAck(DeviceProtocol::CMD_HEARTBEAT);
}
private function handleDataReport($payload) {
// 存储传感器数据
$this->saveSensorData($payload);
// 检查是否需要下发配置
if ($this->needConfigUpdate()) {
return PacketBuilder::buildConfigCommand([
0x01 => 'sample_interval',
0x02 => 'upload_interval'
]);
}
return PacketBuilder::buildAck(DeviceProtocol::CMD_DATA_REPORT);
}
}
使用Socket接收和处理
// TCP服务器示例
$server = new React\Socket\Server('0.0.0.0:8080', $loop);
$server->on('connection', function (React\Socket\ConnectionInterface $connection) {
$deviceId = null;
$parser = new PacketParser();
$connection->on('data', function ($data) use ($connection, &$deviceId, $parser) {
$result = $parser->parse($data);
if ($result !== false) {
echo "Received packet from device\n";
print_r($result);
// 发送应答
$response = PacketBuilder::buildAck($result['command']);
$connection->write($response);
}
});
$connection->on('close', function () {
echo "Connection closed\n";
});
});
这个解决方案提供了完整的设备报文解析框架,可以根据实际协议格式进行调整,关键点包括:
- 支持多种报文格式
- 包含校验机制
- 模块化设计,易于扩展
- 提供报文构造和解析的完整流程