[JT] 注册数据测试

211 阅读7分钟
  • 问:完整的心跳包数据是怎么组成的, 在jt808协议中

  • 答: JT808是一种广泛用于车载定位跟踪系统的协议,它定义了车辆和服务器之间的通信规范。心跳包是用于维持车辆和服务器之间连接状态的数据包。下面是在JT808协议中,一个完整的心跳包数据的组成部分:

  1. 起始符(2字节):表示数据包的开始,固定为0x7E。

  2. 消息头(12字节):包含了一些基本的信息和控制字段,包括:

    • 消息ID(2字节):表示该数据包是一个心跳包,固定为0x0002。
    • 消息体属性(2字节):表示消息体的长度和其他属性。
    • 终端手机号(6字节):表示车载终端设备的手机号码,用于唯一标识车辆。
    • 消息流水号(2字节):用于标识消息的流水号,用于消息的响应和匹配。
  3. 校验码(1字节):通过对消息头和消息体的所有字节进行异或操作得到的结果,用于校验数据包的完整性。

  4. 消息体(可变长度):表示心跳包的具体内容,一般为空。

  5. 终止符(1字节):表示数据包的结束,固定为0x7E。

总体来说,一个完整的JT808心跳包数据的结构如下:

Copy code
起始符  | 消息头       | 校验码 | 消息体 | 终止符
0x7E  | ...          | ...  | ... | 0x7E

其中消息头包含了消息ID、消息体属性、终端手机号和消息流水号等字段,消息体为空,校验码通过对消息头和消息体进行异或操作得到,起始符和终止符用于标识数据包的开始和结束。

请注意,上述描述只是JT808协议中心跳包数据的基本组成部分,具体的协议规范可能还包括其他字段和格式要求。具体使用时应参考JT808协议文档以及相关规范进行实际操作。

  • 问:这是服务器返回的注册应答数据: 7e8100000e0195011373970002001800746573745f617574685f37de7e, 那它对应的心跳包数据应该是怎样的

  • 答:根据给出的注册应答数据(7e8100000e0195011373970002001800746573745f617574685f37de7e),可以推断出相应的心跳包数据。以下是心跳包数据的分析和组成:

  1. 起始符(2字节):0x7E,表示数据包的开始。

  2. 消息头(12字节):

    • 消息ID(2字节):0x0002,表示该数据包是一个心跳包。
    • 消息体属性(2字节):0x0018,表示消息体长度为24字节。
    • 终端手机号(6字节):0x013739700002,表示车载终端设备的手机号码。
    • 消息流水号(2字节):0x9501,用于标识消息的流水号。
  3. 校验码(1字节):0x37,校验码通过对消息头和消息体的所有字节进行异或操作得到。

  4. 消息体(24字节):根据注册应答数据解析得到的消息体数据,即0x001800746573745f617574685f37。

  5. 终止符(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;
    }

}