本文目录导读:

我来详细解释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";
});
});
这个解决方案提供了完整的设备报文解析框架,可以根据实际协议格式进行调整,关键点包括:
- 支持多种报文格式
- 包含校验机制
- 模块化设计,易于扩展
- 提供报文构造和解析的完整流程
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播