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
排查思路与解决建议
移除连接关闭操作,保持长连接
代码中处理完登录包后调用了socket_close($client),这会直接断开TCP连接。Concox设备登录成功后依赖长连接持续发送后续数据,断开后设备无法再传输心跳、位置包。必须删除这行代码。修改循环逻辑,持续监听单连接的后续数据
当前代码每次接受连接后仅读取一次数据,正确做法是在接受连接后进入子循环,持续读取该客户端的数据包,直到连接断开:while (true) { $client = socket_accept($sock); // 持续处理该客户端的所有数据包 while (true) { $raw = socket_read($client, 1024); if ($raw === false || $raw === '') { // 连接断开,退出子循环 socket_close($client); break; } // 数据包解析、处理逻辑放在这里 $hexData = bin2hex($raw); // ... 原有协议判断代码 ... } }对所有数据包发送响应
Concox协议要求服务器对每个收到的数据包(包括心跳、位置包)回复对应确认包,否则设备会停止发送数据:- 心跳包(0x23):回复格式为
7878 05 23 [序列号] [CRC校验] 0d0a - 位置包(0x12):回复格式为
7878 05 12 [序列号] [CRC校验] 0d0a
- 心跳包(0x23):回复格式为
确保CRC计算符合协议要求
验证GetCrc16函数是否严格遵循Concox指定的CRC16-CCITT XModem标准,计算错误会导致设备认为响应无效,终止通信。保证数据包读取完整性
当前固定读取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
相关产品推荐
相关产品推荐

