基于CodeIgniter 4解析Teltonika FMB140二进制数据异常求助
解决Teltonika FMB140二进制数据解析问题(CodeIgniter 4)
问题根源
你当前的代码错误假设IMEI之后直接是timestamp等字段,但Teltonika FMB140的AVL数据协议有严格的二进制结构,并非简单的连续字节拼接。另外,直接用hexdec处理原始二进制字节是错误的——二进制数据需要按字节提取后转换为对应的数据类型(如64位时间戳、32位经纬度整数),还要注意小端字节序的要求。
正确解析逻辑
Teltonika设备通信分两个阶段:
- IMEI握手阶段:设备先发送「2字节长度码+IMEI字符串」,服务器回复
0x01确认。 - AVL数据阶段:握手成功后,设备发送标准AVL数据包,结构为:
- 4字节:数据包总长度(小端)
- 1字节:AVL记录数量
- N个AVL记录(每个含时间戳、优先级、GPS数据、IO数据等)
- 1字节:结束标记(需与记录数量一致)
关键数据转换规则
- 时间戳:8字节小端无符号整数,单位为毫秒
- 经度/纬度:4字节小端有符号整数,需除以
10^7转换为十进制坐标 - 卫星数:1字节无符号整数
- 所有多字节数据均采用小端字节序
CodeIgniter 4 修正代码示例
<?php namespace App\Controllers; use CodeIgniter\Controller; class TeltonikaReceiver extends Controller { public function index() { $port = xxxxx; // 替换为你的端口 $serverIp = 'xxxxxxxx'; // 替换为你的服务器IP // 创建Socket $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if (!$socket) die("Socket创建失败: " . socket_strerror(socket_last_error())); // 绑定端口 if (!socket_bind($socket, $serverIp, $port)) die("绑定失败: " . socket_strerror(socket_last_error($socket))); // 监听连接 if (!socket_listen($socket, 5)) die("监听失败: " . socket_strerror(socket_last_error($socket))); echo "等待设备连接...\n"; $clientSocket = socket_accept($socket); if (!$clientSocket) die("接受连接失败: " . socket_strerror(socket_last_error($socket))); socket_getpeername($clientSocket, $clientAddress, $clientPort); echo "设备连接: $clientAddress:$clientPort\n"; // 设置超时 socket_set_option($clientSocket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 10, 'usec' => 0]); $handshakeDone = false; while (true) { $data = socket_read($clientSocket, 4096); // 增大缓冲区适配AVL数据包 if ($data === false || strlen($data) === 0) break; if (!$handshakeDone) { // 处理IMEI握手 $imeiLenBytes = substr($data, 0, 2); $imeiLength = unpack('v', $imeiLenBytes)[1]; // 小端解析2字节长度 $imei = substr($data, 2, $imeiLength); echo "收到IMEI: $imei\n"; // 发送确认 socket_write($clientSocket, "\x01", 1); $handshakeDone = true; continue; } // 处理AVL数据包 $this->parseAvlData($clientSocket, $data); } socket_close($clientSocket); socket_close($socket); } private function parseAvlData($clientSocket, string $data) { // 解析数据包总长度(4字节小端) $packetLen = unpack('V', substr($data, 0, 4))[1]; // 解析AVL记录数量(1字节) $recordCount = ord(substr($data, 4, 1)); $offset = 5; for ($i = 0; $i < $recordCount; $i++) { // 解析时间戳(8字节小端,毫秒) $timestampBytes = substr($data, $offset, 8); $timestamp = unpack('P', $timestampBytes)[1]; // P表示小端64位无符号整数 $offset += 8; // 解析优先级(1字节) $priority = ord(substr($data, $offset, 1)); $offset += 1; // 解析GPS数据块(16字节) $gpsData = substr($data, $offset, 16); $offset += 16; // 经度:4字节小端有符号整数,转换为十进制 $longitude = unpack('l', substr($gpsData, 0, 4))[1] / 10000000; // 纬度:4字节小端有符号整数,转换为十进制 $latitude = unpack('l', substr($gpsData, 4, 4))[1] / 10000000; // 卫星数:1字节 $satellites = ord(substr($gpsData, 8, 1)); echo "=== AVL记录 $i ===\n"; echo "时间戳(毫秒): $timestamp\n"; echo "优先级: $priority\n"; echo "经度: $longitude\n"; echo "纬度: $latitude\n"; echo "卫星数: $satellites\n"; } // 验证结束标记并回复确认 $endMarker = ord(substr($data, $offset, 1)); if ($endMarker === $recordCount) { socket_write($clientSocket, pack('C', $recordCount), 1); echo "AVL数据解析完成,已回复确认\n"; } else { echo "AVL数据包校验失败\n"; } } } ?>
关键注意事项
- 设备配置:确保FMB140的服务器设置为
TCP协议,端口与代码一致,数据格式选择Teltonika AVL(不要选JSON、NMEA等其他格式)。 - 字节序匹配:PHP的
unpack函数中,v对应小端16位无符号、V对应小端32位无符号、P对应小端64位无符号、l对应小端32位有符号,必须严格对应协议要求。 - 缓冲区大小:AVL数据包可能超过1024字节,需将
socket_read的缓冲区设置为足够大的值(如4096)。 - 异常处理:实际部署时需添加数据包长度不匹配、解析失败等场景的异常捕获逻辑,避免服务崩溃。
参考建议
- 直接查阅Teltonika官方AVL协议文档(设备手册中可获取),里面有完整的二进制结构定义。
- 可封装
pack/unpack相关操作成工具类,方便后续项目复用。
内容的提问来源于stack exchange,提问作者Ardita
相关产品推荐
相关产品推荐

