You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Concox V5 GPS终端PHP通信异常:登录后无心跳/位置数据包

Concox V5 GPS设备通信PHP脚本问题排查

我正在开发一个PHP脚本,用于和Concox V5 GPS追踪设备通信。目前已成功接收并处理登录数据包,但无法收到心跳(0x23)、位置(0x12)及其他类型的数据包。

服务器PHP代码

<?php
$host = '0.0.0.0'; // 监听所有接口
$port = 5023;

// 创建TCP流套接字
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

// 绑定套接字到地址/端口
socket_bind($sock, $host, $port) or die('Could not bind to address');

// 开始监听连接
socket_listen($sock);

// 保持服务器运行的循环
while (true) {
    // 接受传入连接
    $client = socket_accept($sock);

    // 读取客户端输入
    $raw = socket_read($client, 1024);

    // 打印接收的数据
    echo "\nReceived data: $raw\n";
    
    $hexData = bin2hex($raw);
    
    echo "\nHex Data : $hexData\n";

    $start_bit = substr($hexData, 0, 4);
    $length = substr($hexData, 4, 2);
    $protocolNumber = substr($hexData, 6, 2);

    if ($protocolNumber == '01') {
        echo "Protocol Number : $protocolNumber\n";
        echo "Login Packet\n";
        $terminalId = substr($hexData, 8, 16);
        $imei = substr($hexData, 9, 15);
        $info_content = substr($hexData, 8, 24);
        $model_check = substr($hexData, 24, 4);
        $time_zone = substr($hexData, 28, 4);
        $info_serial_number = substr($hexData, 32, 4);
        $error_check = substr($hexData, 36, 4);
        $stop_bit = substr($hexData, 40, 4);

        $data_for_checksum = substr($hexData, 4, 32);
        $binary_data_for_checksum = hex2bin($data_for_checksum);

        $newString = chr(0x05).chr(0x01).hex2bin($info_serial_number);
        $crc16 = GetCrc16($newString, strlen($newString));
        $crc16h = floor($crc16 / 256);
        $crc16l = $crc16 - $crc16h * 256;
        
        $crc = sprintf('%02x%02x', $crc16h, $crc16l);

        echo "########################################################\n";
        echo "Start Bit : $start_bit\n";
        echo "Length : $length\n";
        echo "Protocol Number : $protocolNumber\n";
        echo "Information Content : $info_content\n";
        echo "Terminal ID : $terminalId\n";
        echo "Model Check : $model_check\n";
        echo "Time Zone : $time_zone\n";
        echo "IMEI : $imei\n";
        echo "Info Serial Number : $info_serial_number\n";
        echo "Error Check : $error_check\n";
        echo "Stop Bit : $stop_bit\n";
        echo "Data for Checksum : $data_for_checksum\n";
        echo "-------------------------------------------------------\n";
        
        echo "New String : $newString\n";
        echo "CRC16 Calculated : $crc16\n";
        echo "crc16h : $crc16h\n";
        echo "crc16l : $crc16l\n";
        echo "Final CRC16 Calculated : $crc\n";
        echo "-------------------------------------------------------\n";

        echo "########################################################\n";

        // 可选:向客户端发送响应
        $res1 = '78780501';  
        $res2 = $info_serial_number; 
        $res3 = $crc;
        $res4 = '0d0a'; 
        $sendCommands = $res1.$res2.$res3.$res4;
        echo "Response from Server : ".$sendCommands."\n";

        $send_cmd = '';
        for ($i = 0; $i < strlen($sendCommands); $i += 2) {
            $send_cmd .= chr(hexdec(substr($sendCommands, $i, 2)));
        }

        socket_send($client, $send_cmd, strlen($send_cmd), 0);

        // 关闭客户端套接字
        socket_close($client);
    } elseif ($protocolNumber == '12') {
        echo "Protocol Number : $protocolNumber\n";
    } elseif ($protocolNumber == '13') {
        echo "Protocol Number : $protocolNumber\n";
    } elseif ($protocolNumber == '15') {
        echo "Protocol Number : $protocolNumber\n";
    } elseif ($protocolNumber == '16') {
        echo "Protocol Number : $protocolNumber\n";
    } else {
        echo "Protocol Number : $protocolNumber\n";
    }
}

// 关闭主套接字
socket_close($sock);

// CRC16-CCITT (XModem) 校验和计算
function GetCrc16($data, $length) {
    $crc = 0xFFFF;
    for ($i = 0; $i < $length; $i++) {
        $crc ^= ord($data[$i]);
        for ($j = 0; $j < 8; $j++) {
            if ($crc & 0x0001) {
                $crc = ($crc >> 1) ^ 0x8408;
            } else {
                $crc >>= 1;
            }
        }
    }
    return $crc;
}
?>

问题现象

  • 已成功接收并处理登录数据包(login packet)。
  • 登录完成后,无法从设备获取心跳(0x23)、位置(0x12)或其他类型的数据包。

疑问

  • 我的服务器实现是否存在问题导致该异常?
  • 是否需要发送特定的确认或保活数据包以持续接收数据?
  • 登录数据包接收后,是否需要遵循特定的协议流程才能接收其他类型的数据包?

输出示例

Hex Data : 78780d0103509253775940270018245a0d0a
Protocol Number : 01
Login Packet
########################################################
Start Bit : 7878
Length : 0d
Protocol Number : 01
Information Content : 0350925377594027
Terminal ID : 0350925377594027
IMEI : 350925377594027
Info Serial Number : 0018
Error Check : 245a
Stop Bit : 0d0a
Data for Checksum : 0d0103509253775940270018
-------------------------------------------------------
New String : 0018
CRC16 Calculated : 12449
crc16h : 48
crc16l : 161
Final CRC16 Calculated : 30a1
-------------------------------------------------------
########################################################
Response from Server : 78780501001830a10d0a
Sent

排查思路与解决建议

  1. 移除连接关闭操作,保持长连接
    代码中处理完登录包后调用了socket_close($client),这会直接断开TCP连接。Concox设备登录成功后依赖长连接持续发送后续数据,断开后设备无法再传输心跳、位置包。必须删除这行代码。

  2. 修改循环逻辑,持续监听单连接的后续数据
    当前代码每次接受连接后仅读取一次数据,正确做法是在接受连接后进入子循环,持续读取该客户端的数据包,直到连接断开:

    while (true) {
        $client = socket_accept($sock);
        // 持续处理该客户端的所有数据包
        while (true) {
            $raw = socket_read($client, 1024);
            if ($raw === false || $raw === '') {
                // 连接断开,退出子循环
                socket_close($client);
                break;
            }
            // 数据包解析、处理逻辑放在这里
            $hexData = bin2hex($raw);
            // ... 原有协议判断代码 ...
        }
    }
    
  3. 对所有数据包发送响应
    Concox协议要求服务器对每个收到的数据包(包括心跳、位置包)回复对应确认包,否则设备会停止发送数据:

    • 心跳包(0x23):回复格式为7878 05 23 [序列号] [CRC校验] 0d0a
    • 位置包(0x12):回复格式为7878 05 12 [序列号] [CRC校验] 0d0a
  4. 确保CRC计算符合协议要求
    验证GetCrc16函数是否严格遵循Concox指定的CRC16-CCITT XModem标准,计算错误会导致设备认为响应无效,终止通信。

  5. 保证数据包读取完整性
    当前固定读取1024字节可能导致数据包截断,建议根据协议头的length字段读取完整数据包:

    // 先读取头部获取长度
    $raw = socket_read($client, 4);
    if (strlen($raw) < 4) break;
    $hexHeader = bin2hex($raw);
    $length = hexdec(substr($hexHeader, 4, 2));
    // 读取剩余部分:长度字段内容 + CRC + 结束位
    $raw .= socket_read($client, $length + 4);
    

内容的提问来源于stack exchange,提问作者Chetan Yadnik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 13:09:50