-
问:完整的心跳包数据是怎么组成的, 在jt808协议中
-
答: JT808是一种广泛用于车载定位跟踪系统的协议,它定义了车辆和服务器之间的通信规范。心跳包是用于维持车辆和服务器之间连接状态的数据包。下面是在JT808协议中,一个完整的心跳包数据的组成部分:
-
起始符(2字节):表示数据包的开始,固定为0x7E。
-
消息头(12字节):包含了一些基本的信息和控制字段,包括:
- 消息ID(2字节):表示该数据包是一个心跳包,固定为0x0002。
- 消息体属性(2字节):表示消息体的长度和其他属性。
- 终端手机号(6字节):表示车载终端设备的手机号码,用于唯一标识车辆。
- 消息流水号(2字节):用于标识消息的流水号,用于消息的响应和匹配。
-
校验码(1字节):通过对消息头和消息体的所有字节进行异或操作得到的结果,用于校验数据包的完整性。
-
消息体(可变长度):表示心跳包的具体内容,一般为空。
-
终止符(1字节):表示数据包的结束,固定为0x7E。
总体来说,一个完整的JT808心跳包数据的结构如下:
Copy code
起始符 | 消息头 | 校验码 | 消息体 | 终止符
0x7E | ... | ... | ... | 0x7E
其中消息头包含了消息ID、消息体属性、终端手机号和消息流水号等字段,消息体为空,校验码通过对消息头和消息体进行异或操作得到,起始符和终止符用于标识数据包的开始和结束。
请注意,上述描述只是JT808协议中心跳包数据的基本组成部分,具体的协议规范可能还包括其他字段和格式要求。具体使用时应参考JT808协议文档以及相关规范进行实际操作。
-
问:这是服务器返回的注册应答数据: 7e8100000e0195011373970002001800746573745f617574685f37de7e, 那它对应的心跳包数据应该是怎样的
-
答:根据给出的注册应答数据(7e8100000e0195011373970002001800746573745f617574685f37de7e),可以推断出相应的心跳包数据。以下是心跳包数据的分析和组成:
-
起始符(2字节):0x7E,表示数据包的开始。
-
消息头(12字节):
- 消息ID(2字节):0x0002,表示该数据包是一个心跳包。
- 消息体属性(2字节):0x0018,表示消息体长度为24字节。
- 终端手机号(6字节):0x013739700002,表示车载终端设备的手机号码。
- 消息流水号(2字节):0x9501,用于标识消息的流水号。
-
校验码(1字节):0x37,校验码通过对消息头和消息体的所有字节进行异或操作得到。
-
消息体(24字节):根据注册应答数据解析得到的消息体数据,即0x001800746573745f617574685f37。
-
终止符(1字节):0x7E,表示数据包的结束。
因此,对应的心跳包数据如下:(注意,要小写,除非服务端有相应的处理代码)
7e00020018013739700000295011800746573745f617574685f3737de7e
-
问:我将心跳包发送过去, 服务端返回:
48656c6c6f20376630303030303130623535303030303030303137663030303030313062353530303030303030312c204920616d207365727665722c206e6f7720492073656e64206d65737361676520746f20796f75
-
答: 根据服务器返回的数据进行解析,它看起来是一个ASCII编码的字符串。将其转换为可读的文本形式,它表示的是以下内容:
Hello 7f0000010b5500000017f000010b550000001, I am server, now I send message to you
这是一个简单的回复消息,其中包含了问候和说明信息。请注意,这是根据返回的字符串进行推测和解释的,确切的含义可能需要参考服务器端的逻辑和协议规范来确认。
按照之前的数据, 给我一个示例的16进制数据, 经纬度可以随意
这是一个示例的位置信息的16进制数据,其中经度和纬度值是随机生成的:
makefileCopy code
报文头: 7E
消息ID: 0200
消息体属性: 0018
终端手机号: 013739700002
消息流水号: 9501
位置信息:
经度: 123.456789 (示例值)
纬度: 45.678901 (示例值)
高程: 1000 (示例值)
速度: 60 (示例值)
方向: 270 (示例值)
日期: 2023-06-23 (示例值)
时间: 12:34:56 (示例值)
校验码: 37
报文尾: 7E
转成小写结果: 7e020000181137397000002950118014e3dcbf347b245d06863dc0ad44f013f401032153b6d937e
相关代码
- 1.客户端
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Connection\AsyncTcpConnection;
use Workerman\Worker;
use \Workerman\Lib\Timer;
//获取校验码
function calCheckSum($hex_string)
{
$cs = hexdec(substr($hex_string, 0, 2));
$str_len = strlen($hex_string);
for ($i = 2; $i < $str_len; $i += 2) {
$cs ^= hexdec(substr($hex_string, $i, 2));
}
return bin2hex(pack('C',$cs));
}
$worker = new Worker();
$status = true;
$worker->onWorkerStart = function () {
// 以websocket协议连接远程websocket服务器 ws://IP:端口
$ws_connection = new AsyncTcpConnection('tcp://101.226.23.107:8282');
// 连接成功
$ws_connection->onConnect = function ($connection) {
//注册
$msg = "7e0100002d019501137397001800000000383838383800000000000000000000000000000000000000003131333733393702d4c1423030303030327e";
//心跳包
$msg1 = '7e0002000001453409748303c8477e7e0200003a0145340972520bdb00000000000000030205d4b406b9efe800ed00000120200430194209010400000716eb16000c00b28986043103188076978800060089fffffffff77e';
//位置信息上报
$msg2 = '7e0200003a01453408683900b600000000000000030205b1f006b97930015e00000111200430182601010400000018eb16000c00b28986043103188076975700060089ffffffffb67e';
$data = hex2bin($msg);
$data1 = hex2bin($msg1);
$data2 = hex2bin($msg2);
//定时3秒
$time_interval = 3;
$diy_data_heart = hex2bin('7e00020018013739700000295011800746573745f617574685f3737de7e');
$diy_data_location = hex2bin('7e020000181137397000002950118014e3dcbf347b245d06863dc0ad44f013f401032153b6d937e');
//改为data为注册 data1心跳包 data2位置信息上报
$sendData = $diy_data_location;
// 给connection对象临时添加一个timer_id属性保存定时器id
$connection->timer_id = Timer::add($time_interval, function () use ($connection, $sendData) {
$connection->send($sendData);
});
};
// 远程websocket服务器发来消息时
$ws_connection->onMessage = function ($connection, $data) {
$data1 = bin2hex($data);
echo "receive: $data1\n";
$code = calCheckSum($data1);
echo '校验码:' .$code . PHP_EOL;
};
// 连接上发生错误时,一般是连接远程websocket服务器失败错误
$ws_connection->onError = function ($connection, $code, $msg) {
echo "error: $msg\n";
};
// 当连接远程websocket服务器的连接断开时
$ws_connection->onClose = function ($connection) {
echo "connection closed\n";
// 连接关闭时,删除对应连接的定时器
// 删除定时器
Timer::del($connection->timer_id);
};
// 设置好以上各种回调后,执行连接操作
$ws_connection->connect();
};
// 执行
Worker::runAll();
- 2.服务端
# 1.Events.php 入口文件
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use \GatewayWorker\Lib\Gateway;
/**
* 主逻辑
* 主要是处理 onConnect onMessage onClose 三个方法
* onConnect 和 onClose 如果不需要可以不用实现并删除
* 主要处理都是16进制处理
*/
class Events
{
/**
* 当客户端连接时触发
* 如果业务不需此回调可以删除onConnect
* @param int $client_id 连接id
*/
public static function onConnect($client_id)
{
// 向当前client_id发送数据
Gateway::sendToClient($client_id, "Hello $client_id");
// 向所有人发送
Gateway::sendToAll("$client_id, I am server, now I send message to you");
}
public static function onMessage($client_id, $message){
$hex_string = bin2hex($message);
$hex_string = ltrim($hex_string, '7e');
$hex_string = rtrim($hex_string, '7e');
echo "新的消息: ".$hex_string .PHP_EOL;
$hanlder=new \YourApp\Handler\Jt808Handler($hex_string, $client_id);
$hanlder->deal();
}
}
# 2.解码文件 YourApp\util\MsgDecoder.php
<?php
namespace YourApp\util;
/**
* 解析头信息
* Class MsgDecoder
* @package YourApp\util
*/
class MsgDecoder{
public function getPackageData($hex_string){
$ret=new PackageData();
//如果有分包就是16位,没有就是12位
$msg_header=$this->parseMsgHeader($hex_string);
$ret->MsgHeader=$msg_header;
$msgBodyByteStartIndex = 12*2;
//消息体
if($msg_header->isHasSubPackage()){
$msgBodyByteStartIndex=16*2;
}
$msg_body=substr($hex_string,$msgBodyByteStartIndex,$msg_header->msg_body_length*2);
$ret->msg_body=$msg_body;
// mk.最后一位是校验码, 注意客户端发送过来需要先 hex2bin()
echo 'hex_string:' . $hex_string . PHP_EOL;
$check_sub_in_pkg=substr($hex_string,strlen($hex_string)-2,2);
$calculated_check_sum=Common::calCheckSum(substr($hex_string,0,strlen($hex_string)-2));
$ret->check_sum=$check_sub_in_pkg;
// -mk 这里是判断验证码是否一致的地方
if($calculated_check_sum!=$check_sub_in_pkg){
throw new \Exception('错误,校验码不一致'. ", 最后一位是校验码:{$check_sub_in_pkg}, 解析校验码:{$calculated_check_sum}");
}
return $ret;
}
//解析头标识
private function parseMsgHeader($hex_string){
$msg_header=new MsgHeader();
//消息id word(16) 2bytes
$msg_header->msg_id=substr($hex_string,0,4);
//消息体点属性 word(16) 2bytes
// ----------------消息体属性 start--------------------------------------
$msg_body_props=substr($hex_string,4,4);
$msg_header->mgs_body_props_field=$msg_body_props;
//转成10的整数
$msg_body_props=hexdec($msg_body_props);
//[0-9]消息体长度 0000,0011,1111,1111(3ff)
$msg_header->msg_body_length=($msg_body_props & 0x3ff);
//[10-12]加密类型 0001,1100,0000,0000(1c00)
$msg_header->msg_encryption_type=(($msg_body_props & 0x1c00)>>10);
//[13] 是否有子包 0010,0000,0000,0000(2000)(是否有子包)
$msg_header->has_sub_package=(($msg_body_props & 0x2000)>>13);
//[14-15] 保留为 1100,0000,0000,0000(C000)
$msg_header->reserved_bit=(($msg_body_props & 0xc000)>>14);
//-----------------------消息体属性 end------------------------------------------------
//终端手机号,因为只有0-9 所以直接就可以使用
$msg_header->terminal_phone=substr($hex_string, 8, 12);
//信息流水号
$msg_header->flow_id=hexdec(substr($hex_string, 20, 4));
//信息包封装项
if($msg_header->isHasSubPackage()){
// 消息包封装项字段
$msg_header->package_info_filed=substr($hex_string, 24, 8);
$msg_header->total_sub_package=hexdec(substr($hex_string, 24, 4));
$msg_header->sub_package_seq=hexdec(substr($hex_string, 28, 4));
}
return $msg_header;
}
}