C#對接部標JT808協議實現北斗定位設備數據接收服務端

一、前言介紹

開發一套能夠支撐幾萬臺北鬥定位設備數據接收的服務端,用於接收北斗定位器定位數據的平臺。項目基於windows平臺,C#語言開發框架Net Framework4.8,TCP主要基於SuperSocket用來構建一個服務器端 Socket 程序,JT808.Protocol JT/T808協議快速開發包,開發工具採用了微軟的Visual Studio 2022,數據庫MSSQL2014(分庫分表)。

二、主要流程說明

註冊->鑑權->心跳->位置上報

首次連接必須註冊,註冊成功後返回鑑權碼,之後每次建立連接必須先鑑權

否則不響應心跳和地址上報。

三、常用工具

字符串和16進制轉換: http://www.bejson.com/convert/ox2str/

在線進制轉換:       http://tool.oschina.net/hexconvert/

計算校驗碼工具:     http://www.ip33.com/bcc.html

也可以找一個模擬JT808發包收包的工具

四、開發包引用

引用的包都是開源可修改的,建議下載源碼附加進項目中,便於修改調試。

1、SuperSocket

https://github.com/kerryjiang/SuperSocket

採用了SuperSocket1.6.1版本簡化Socket服務端的開發工作。

在Visual Studio中可以使用NuGet安裝SuperSocket。

 

2、JT808.Protocol版本 2.6.5.0

https://github.com/SmallChi/JT808

 

五、主要類實現

1、自定義請求(RequestInfo)

主要用來封裝JT808數據包,便於下一步處理。

JT808RequestInfo.cs

public class JT808RequestInfo : IRequestInfo
{
 
    public JT808RequestInfo() { }
 
    public string Key{get;set;} = "jt808";
 
    public byte[] SourceBytes { get;set;}
 
    /// <summary>
    /// 起始符
    /// </summary>
    public const byte BeginFlag = 0x7e;
    /// <summary>
    /// 終止符
    /// </summary>
    public const byte EndFlag = 0x7e;
 
    public JT808RequestInfo(ushort msgId, JT808HeaderMessageBody messageBodyProperty, JT808Version version, string terminalPhoneNo, ushort msgNum, byte[] bodies, byte checkCode)
    {
        MsgId = msgId;
        MessageBodyProperty = messageBodyProperty;
        Version = version;
        TerminalPhoneNo = terminalPhoneNo;
        MsgNum = msgNum;
        Bodies = bodies;
        CheckCode = checkCode;
    }
 
    /// <summary>
    /// 起始符,1字節
    /// </summary>
    public byte Begin { get; set; } = BeginFlag;
 
    /// <summary>
    /// 消息ID,2字節
    /// </summary>
    public ushort MsgId { get; set; }
 
    /// <summary>
    /// 消息體屬性
    /// </summary>
    public JT808HeaderMessageBody MessageBodyProperty { get; set; }
 
    /// <summary>
    /// 808版本號
    /// </summary>
    public JT808Version Version { get; set; }
 
    /// <summary>
    /// 終端手機號
    /// 根據安裝後終端自身的手機號轉換。手機號不足 12 位,則在前補充數字,大陸手機號補充數字 0,港澳臺則根據其區號進行位數補充
    /// (2019版本)手機號不足 20 位,則在前補充數字 0
    /// </summary>
    public string TerminalPhoneNo { get; set; }
 
    /// <summary>
    /// 消息流水號
    /// 發送計數器
    /// 佔用兩個字節,爲發送信息的序列號,用於接收方檢測是否有信息的丟失,上級平臺和下級平臺接自己發送數據包的個數計數,互不影響。
    /// 程序開始運行時等於零,發送第一幀數據時開始計數,到最大數後自動歸零
    /// </summary>
    public ushort MsgNum { get; set; }
 
    /// <summary>
    /// 消息總包數
    /// </summary>
    public ushort PackgeCount { get; set; }
    /// <summary>
    /// 報序號 從1開始
    /// </summary>
    public ushort PackageIndex { get; set; }
 
    /// <summary>
    /// 數據體
    /// </summary>
    public byte[] Bodies { get; set; }
 
    /// <summary>
    /// 校驗碼
    /// 從消息頭開始,同後一字節異或,直到校驗碼前一個字節,佔用一個字節。
    /// </summary>
    public byte CheckCode { get; set; }
 
    /// <summary>
    /// 終止符
    /// </summary>
    public byte End { get; set; } = EndFlag;
}

2、自定義AppSession

AppSession 代表一個和客戶端的邏輯連接,基於連接的操作應該定於在該類之中。你可以用該類的實例發送數據到客戶端,接收客戶端發送的數據或者關閉連接。

SocketSession.cs

/// <summary>  
/// 自定義連接類SocketSession,繼承AppSession,並傳入到AppSession  
/// </summary>  
public class SocketSession : AppSession<SocketSession, JT808RequestInfo>
{
    public override void Send(string message)
    {
        Console.WriteLine("發送消息:" + message);
        base.Send(message);
    }
 
 
    protected override void OnSessionStarted()
    {
        //輸出客戶端IP地址  
        Console.WriteLine(this.LocalEndPoint.Address.ToString());
        //this.Send("Hello User,Welcome to SuperSocket Telnet Server!");
    }
 
    
 
 
    /// <summary>  
    /// 連接關閉  
    /// </summary>  
    /// <param name="reason"></param>  
    protected override void OnSessionClosed(CloseReason reason)
    {
        base.OnSessionClosed(reason);
    }
 
    //protected override void HandleUnknownRequest(JT808RequestInfo requestInfo)
    //{
    //    Console.WriteLine($"遇到未知的請求 Key:" + requestInfo.Key + $" Body:" + requestInfo.Body);
    //    base.HandleUnknownRequest(requestInfo);
    //}
 
    /// <summary>  
    /// 捕捉異常並輸出  
    /// </summary>  
    /// <param name="e"></param>  
    protected override void HandleException(Exception e)
    {
        this.Send("error: {0}", e.Message);
    }
 
 
 
}

3、自定義AppServer

AppServer 代表了監聽客戶端連接,承載TCP連接的服務器實例。理想情況下,我們可以通過AppServer實例獲取任何你想要的客戶端連接,服務器級別的操作和邏輯應該定義在此類之中。

SocketServer.cs

public class SocketServer : AppServer<SocketSession, JT808RequestInfo>
{
 
    public SocketServer()
        : base(new MyReceiveFilterFactory())
    {
 
        //業務處理線程1
        Thread rcfsth = new Thread(xxxxx);
        rcfsth.IsBackground = true;
        rcfsth.Start();
 
        //業務處理線程2
        Thread locationTh = new Thread(xxxxxxxx);
        locationTh.IsBackground = true;
        locationTh.Start();
    }
    protected override bool Setup(IRootConfig rootConfig, IServerConfig config)
    {
        Console.WriteLine("正在準備配置文件");
        return base.Setup(rootConfig, config);
    }
 
    protected override void OnStarted()
    {
        Console.WriteLine("服務已開始");
        base.OnStarted();
    }
 
    protected override void OnStopped()
    {
        Console.WriteLine("服務已停止");
        base.OnStopped();
    }
 
 
    /// <summary>  
    /// 輸出新連接信息  
    /// </summary>  
    /// <param name="session"></param>  
    protected override void OnNewSessionConnected(SocketSession session)
    {
        base.OnNewSessionConnected(session);
        //輸出客戶端IP地址  
        Console.Write("\r\n" + session.LocalEndPoint.Address.ToString() + ":連接");
    }
 
 
    /// <summary>  
    /// 輸出斷開連接信息  
    /// </summary>  
    /// <param name="session"></param>  
    /// <param name="reason"></param>  
    protected override void OnSessionClosed(SocketSession session, CloseReason reason)
    {
        base.OnSessionClosed(session, reason);
        Console.Write("\r\n" + session.LocalEndPoint.Address.ToString() + ":斷開連接");
    }
}

4、自定義接收過濾器(ReceiveFilter)

由於JT808數據包每個數據包起始標記和結束標記都以7E作爲特殊標記,所以這裏採用BeginEndMarkReceiveFilter - 帶起止符的協議,來接收客戶端發來的數據包。

將該過濾器接收到的數據包根據JT808-2019協議內容去處理,並封裝成JT808RequestInfo對象返回,這裏我使用了Skip().Take()方式去取其中的字節數組,如果對效率有要求的小夥伴可以用Array.Copy()方式取。

MyReceiveFilter.cs

public class JT808ReceiveFilter : BeginEndMarkReceiveFilter<JT808RequestInfo>
    {

        /// <summary>
        /// log4net 記錄日誌
        /// </summary>
        private static readonly ILog Logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        //開始和結束標記也可以是兩個或兩個以上的字節
        private readonly static byte[] BeginMark = new byte[] { 0x7e };
        private readonly static byte[] EndMark = new byte[] { 0x7e };


        //private readonly static byte[] decode7d01 = new byte[] { 0x7d, 0x01 };
        //private readonly static byte[] decode7d02 = new byte[] { 0x7d, 0x02 };

        public JT808ReceiveFilter() : base(BeginMark, EndMark)
        {
        }

        protected override JT808RequestInfo ProcessMatchedRequest(byte[] readBuffer, int offset, int length)
        {

            //解析808協議

            byte[] sourceBytes = readBuffer;
            string readBufferStr = ByteUtils.ToHexStrFromByte(readBuffer).Replace("7D 02", "7E").Replace("7D 01", "7D");
            Logger.Info(readBufferStr);

            //============================採用 SmallChi 的解包方法
            //JT808Serializer JT808Msg = new JT808Serializer();
            //ReadOnlySpan<byte> readOnlySpan = sourceBytes;
            JT808Package jT808Package = new JT808Package();
            var reader = new JT808MessagePackReader(sourceBytes);
            reader.Decode();
            IJT808Config jT808Config = new DefaultGlobalConfig();

            jT808Package = jT808Package.Deserialize(ref reader, jT808Config);

            JT808RequestInfo jT808RequestInfo = new JT808RequestInfo((ushort)jT808Package.Header.MsgId, jT808Package.Header.MessageBodyProperty, jT808Package.Version, jT808Package.Header.TerminalPhoneNo, (ushort)jT808Package.Header.MsgNum, jT808Package.Bodies, jT808Package.CheckCode);
            jT808RequestInfo.PackgeCount =jT808Package.Header.PackgeCount;
            jT808RequestInfo.PackageIndex = jT808Package.Header.PackageIndex;
            jT808RequestInfo.SourceBytes = sourceBytes;


            return jT808RequestInfo;


        }
    }
    /// <summary>
    /// 默認全局配置
    /// </summary> 
   public class DefaultGlobalConfigExt : GlobalConfigBase
    {
        /// <summary>
        /// 配置Id
        /// </summary>
        public override string ConfigId { get; protected set; }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="configId"></param>
        public DefaultGlobalConfigExt(string configId = "Default")
        {
            ConfigId = configId;
        }
}

5、實現接收過濾器工廠(ReceiveFilterFactory)

MyReceiveFilterFactory.cs

public class MyReceiveFilterFactory : IReceiveFilterFactory<JT808RequestInfo>
{
    public IReceiveFilter<JT808RequestInfo> CreateFilter(IAppServer appServer, IAppSession appSession, IPEndPoint remoteEndPoint)
    {
        return new MyReceiveFilter();
    }
}

6、自定義Command處理封裝好的數據包內容

命令行協議是一種被廣泛應用的協議。一些成熟的協議如 Telnet, SMTP, POP3 和 FTP 都是基於命令行協議的。 在SuperSocket 中, 如果你沒有定義自己的協議,SuperSocket 將會使用命令行協議, 這會使這樣的協議的開發變得很簡單。

public class JT808PackCommand : CommandBase<SocketSession, JT808RequestInfo>
{
 
 
   
 
    /// <summary>
    /// 平臺通用應答
    /// </summary>
    /// <param name="session"></param>
    /// <param name="requestInfo"></param>
    /// <param name="ackMsgId"></param>
    public void PlatformCommonReply(SocketSession session, JT808RequestInfo requestInfo,ushort ackMsgId)
    {
        JT808Package jT808Package = new JT808Package();
        jT808Package.Header = new JT808Header
        {
            MsgId = (ushort)JT808MsgId._0x8001,
            ManualMsgNum = 0,
            TerminalPhoneNo = requestInfo.TerminalPhoneNo
        };
        JT808_0x8001 jT808_8001 = new JT808_0x8001();
        jT808_8001.MsgNum = requestInfo.MsgNum;
        jT808_8001.AckMsgId = ackMsgId; 
        jT808_8001.JT808PlatformResult = JT808PlatformResult.succeed;
        jT808Package.Bodies = jT808_8001;
        JT808Serializer jT808Serializer = new JT808Serializer();
        byte[] data = jT808Serializer.Serialize(jT808Package, JT808Version.JTT2019);
        session.Send(data, 0, data.Length);
        //Console.WriteLine(jT808_8001.Description);
    }
 
    public override string Name
    {
        get { return "jt808"; }
    }
 
    public override void ExecuteCommand(SocketSession session, JT808RequestInfo requestInfo)
    {
        ushort msgId = requestInfo.MsgId;
        //Console.WriteLine("收到一條jt808消息,消息ID:" + msgId);
 
       
 
        try
        {
            switch (msgId)
            {
                //終端通用應答
                case 0x0001:
                    {
                      PlatformCommonReply(session, requestInfo, jT808_0X0001.ReplyMsgId);
                    }
                    break;
                //查詢服務器時間
                case 0x0004:
                    {
                        //Console.WriteLine("查詢服務器時間");
 
                        JT808Package jT808Package = new JT808Package();
                        jT808Package.Header = new JT808Header
                        {
                            MsgId = (ushort)JT808MsgId._0x8004,
                            ManualMsgNum = 0,
                            TerminalPhoneNo = requestInfo.TerminalPhoneNo
                        };
 
                        JT808_0x8004 jT808_8004 = new JT808_0x8004();
                        jT808_8004.Time = DateTime.UtcNow;
                        jT808Package.Bodies = jT808_8004;
                        JT808Serializer jT808Serializer = new JT808Serializer();
                        byte[] data = jT808Serializer.Serialize(jT808Package, JT808Version.JTT2019);
 
                        session.Send(data, 0, data.Length);
 
                    }
                    break;
                //終端註冊
                case 0x0100:
                    {
                        Custom_JT808_0x0100 jT808_0X0100 = Custom_JT808_0x0100.Deserialize(requestInfo.Bodies);
 
 
                        JT808Package jT808Package = new JT808Package();
                        jT808Package.Header = new JT808Header
                        {
                            MsgId = (ushort)JT808MsgId._0x8100,
                            ManualMsgNum = 0,
                            TerminalPhoneNo = requestInfo.TerminalPhoneNo
                        };
                        JT808_0x8100 jT808_8100 = new JT808_0x8100();
                        jT808_8100.AckMsgNum = requestInfo.MsgNum;
                        jT808_8100.JT808TerminalRegisterResult = JT808TerminalRegisterResult.success;
                        jT808_8100.Code = jT808_0X0100.TerminalId + "," + jT808_0X0100.PlateNo;
                        jT808Package.Bodies = jT808_8100;
 
                        JT808Serializer jT808Serializer = new JT808Serializer();
                        byte[] data = jT808Serializer.Serialize(jT808Package, JT808Version.JTT2019);
 
                        session.Send(data, 0, data.Length);
 
                    }
 
                    break;
                //終端鑑權
                case 0x0102:
                    {
                        Custom_JT808_0x0102 jT808_0102 = Custom_JT808_0x0102.Deserialize(requestInfo.Bodies);
 
                        //鑑權成功後保存JT808終端客戶端信息
                        JT808ClientCache.AddJT808ClientCache(requestInfo.TerminalPhoneNo, session.SessionID);
 
                        PlatformCommonReply(session, requestInfo, requestInfo.MsgId);
                    }
                    break;
 
                //位置信息彙報
                case 0x0200:
                    {
                        Custom_JT808_0x0200 jT808_0X0200 = Custom_JT808_0x0200.Deserialize(requestInfo.Bodies);
                        Console.WriteLine(jT808_0X0200.Description);
 
                        PlatformCommonReply(session, requestInfo, requestInfo.MsgId);
                    }
                    break;
                //定位數據批量上傳
                case 0x0704:
                    {
                        PlatformCommonReply(session, requestInfo, requestInfo.MsgId);
                    }
                    break;
                //多媒體數據上傳
                case 0x0801:
                    {
                        //如果有分包內容
                        if (requestInfo.PackgeCount > 0)
                        {
                            //Console.WriteLine(requestInfo.PackageIndex);
                            string phone = requestInfo.TerminalPhoneNo;
                            //第一個多媒體數據包
                            if (requestInfo.PackageIndex == 1)
                            {
                                Custom_JT808_0x0801 jT808_0X0801 = Custom_JT808_0x0801.Deserialize(requestInfo.Bodies);
 
                                //添加多媒體數據包緩存
                                MultimediaCache.AddMultimediaCache(phone, jT808_0X0801);
 
                                PlatformCommonReply(session, requestInfo, requestInfo.MsgId);
                            }
                            //中間的數據包
                            if (requestInfo.PackageIndex > 1 && requestInfo.PackageIndex < requestInfo.PackgeCount)
                            {
                                //追加多媒體數據包緩存
                                MultimediaCache.AppendMultimediaByte(phone, requestInfo.Bodies);
                                PlatformCommonReply(session, requestInfo, requestInfo.MsgId);
                            }
                            //最後一個包,保存數據,並回應
                            if (requestInfo.PackageIndex == requestInfo.PackgeCount)
                            {
                                //追加多媒體數據包緩存
                                MultimediaCache.AppendMultimediaByte(phone, requestInfo.Bodies);
                                //保存到本地
                                Custom_JT808_0x0801 jT808_0X0801 = MultimediaCache.GetMultimediaCache(phone);
                                byte[] multimediaDataPackage = jT808_0X0801.MultimediaDataPackage;
 
                                //Console.WriteLine(ByteUtils.ToHexStrFromByte(multimediaDataPackage).Replace(" ",""));
 
                                uint mediaId = jT808_0X0801.MultimediaId;
                                //ByteUtils.BytesToFile(multimediaDataPackage, "sample.jpg");
                                File.WriteAllBytes("Img\\" + requestInfo.MsgNum + "_saved_image.jpg", multimediaDataPackage);
 
                                //移除多媒體數據包緩存
                                MultimediaCache.RemoveMultimediaCache(phone);
                                //應答
                                JT808Package jT808Package = new JT808Package();
                                jT808Package.Header = new JT808Header
                                {
                                    MsgId = (ushort)JT808MsgId._0x8800,
                                    ManualMsgNum = 0,
                                    TerminalPhoneNo = requestInfo.TerminalPhoneNo
                                };
                                JT808_0x8800 jT808_0X8800 = new JT808_0x8800();
                                jT808_0X8800.MultimediaId = mediaId;
                                jT808_0X8800.RetransmitPackageCount = 0;
                                jT808_0X8800.RetransmitPackageIds = new byte[0];//一定要定義一個空數組
                                jT808Package.Bodies = jT808_0X8800;
 
                                JT808Serializer jT808Serializer = new JT808Serializer();
                                byte[] data = jT808Serializer.Serialize(jT808Package, JT808Version.JTT2019);
 
                                session.Send(data, 0, data.Length);
 
                            }
                        }
                    }
                    break;
                default:
                    {
                        PrintMessage.PrintLn("未知命令" + msgId.ToString("x8"),ConsoleColor.Yellow);
                        PlatformCommonReply(session, requestInfo, requestInfo.MsgId);
                    }
 
                    break;
 
            }
        }catch (Exception ex)
        {
            LogHelper.WriteError2(ex, "ExecuteCommand error");
        } 
    }
}

其中Custom_xxx是去解析JT808數據包中的包內容並封裝的,解析方式參考過濾器那裏,可以自己根據808協議去寫。如果用了SmallChi的方式在過濾器中,可以直接使用JT808Package去處理業務代碼,無需定義自己的Custom_xxx去解析包內容。本文直接使用JT808Package處理業務,業務數據分庫分表處理。

六、JT808包解析說明-重要

平臺應答指的是服務端接收到數據後回發給客戶端(北斗設備)的。

1.心跳:
終端請求: 7e000200000857502162890001c67e
7e # 標識位
000200000857502162890001 # 消息頭
    0002 # 消息ID
    0000 # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    085750216289 # 終端手機號或設備號,這裏是設備號
    0001 # 流水號
c6 # 校驗碼
7e # 標識位

平臺通用應答:7e8001000508575021628900010001000200437e
7e # 標識位
800100050857502162890001 # 消息頭
    8001 # 消息ID
    0005 # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    085750216289 # 終端手機號或設備號,這裏是設備號
    0001 # 流水號
0001000200 # 消息體
    0001 # 應答流水號,對終端發送消息的流水號
    0002 # 應答ID,對應終端發送消息的ID
    00   # 結果     
c6 # 校驗碼
7e # 標識位

2.註冊包:
終端註冊-請求:7e0100002e0188554850150025002c0133373039363054372d54383038000000000000000000000000003033323931373001e6b599413636363636557e
7e # 標識位
0100002e0188554850150025 # 消息頭
    0100 # 消息ID
    002e # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    018855485015 # 終端手機號或設備號,這裏是設備號
    0025 # 流水號
002c0133373039363054372d54383038000000000000000000000000003033323931373001e6b599413636363636 # 消息體
    002c # 省份id
    0133 # 城市id
    3730393630 # 製造商id     
    54372d5438303800000000000000000000000000 # 終端型號
    30333239313730 # 終端id
    01 # 車牌顏色
    e6b599413636363636 # 車牌標識    
55 # 校驗碼
7e # 標識位

終端註冊-平臺應答:7e810000100188554850150025002500313535323935353938373437307f7e
7e # 標識位
810000100188554850150025 # 消息頭
    8100 # 消息ID
    0010 # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    018855485015 # 終端手機號或設備號,這裏是設備號
    0025 # 流水號
00250031353532393535393837343730 # 消息體
    0025 # 應答流水號,對終端發送消息的流水號
    00   # 結果 
    31353532393535393837343730 # 鑑權碼,平臺生成的鑑權碼爲字符串 1552955987470,但轉換爲16進制,就是31353532393535393837343730
7f # 校驗碼
7e # 標識位

4.位置上報:
終端請求: 7e020000386857502162891ac000000100000c100101d5c86a0732c2610000000000001903131538032b040000000030011e310100e10400000000e20804600036583a8e7a657e
7e # 標識位
020000386857502162891ac0 # 消息頭
    0200 # 消息ID
    0038 # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    685750216289 # 終端手機號或設備號,這裏是設備號
    1ac0 # 流水號
00000100000c100101d5c86a0732c2610000000000001903131538032b040000000030011e310100e10400000000e20804600036583a8e7a # 消息體
    00000100000c100101d5c86a0732c261000000000000190313153803 #消息體-位置基本信息
    00000100 # 報警標誌
    000c1001 # 狀態
    01d5c86a # 緯度     
    0732c261 # 經度
    0000 # 高度
    0000 # 速度
    0000 # 方向    
    190313153803 # 時間
    2b040000000030011e310100e10400000000e20804600036583a8e7a #消息體-位置附加信息,可選
    2b0400000000 # 2b 模擬量    04 長度爲4   00000000 附加信息內容
    30011e       # 30 信號強度  01 長度爲1   1e       附加信息內容
    310100       # 31 衛星數    01 長度爲1   00       附加信息內容
    e10400000000 # e1 自定義    04 長度爲4   00000000 附加信息內容
    e20804600036583a8e7a # e1 自定義    08 長度爲8   04600036583a8e7a 附加信息內容
65 # 校驗碼
7e # 標識位

平臺通用應答: 7e800100050857502162891ac01ac0020000437e
7e # 標識位
800100050857502162891ac0 # 消息頭
    8001 # 消息ID
    0005 # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    085750216289 # 終端手機號或設備號,這裏是設備號
    1ac0 # 流水號
1ac0020000 # 消息體
    1ac0 # 應答流水號,對終端發送消息的流水號
    0200 # 應答ID,對應終端發送消息的ID
    00   # 結果     
43 # 校驗碼
7e # 標識位

5.鑑權
終端請求: 7e010200060188554850150001323835313131db7e
7e # 標識位
010200060188554850150001 # 消息頭
    0102 # 消息ID
    0006 # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    018855485015 # 終端手機號或設備號,這裏是設備號
    0001 # 流水號
323835313131 # 消息體
    323835313131 # 鑑權碼,這裏是16進制,轉換成字符串爲285111
db # 校驗碼
7e # 標識位

平臺通用應答: 7e8001000501885548501500010001010200567e
7e # 標識位
800100050188554850150001 # 消息頭
    8001 # 消息ID
    0005 # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    018855485015 # 終端手機號或設備號,這裏是設備號
    0001 # 流水號
0001010200 # 消息體
    0001 # 應答流水號,對終端發送消息的流水號
    0102 # 應答ID,對應終端發送消息的ID
    00   # 結果     
43 # 校驗碼
7e # 標識位

6.查詢終端參數
平臺下發請求:7e810400000188554850150001557e 
7e # 標識位
810400000188554850150001 # 消息頭
    8104 # 消息ID
    0000 # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    018855485015 # 終端手機號或設備號,這裏是設備號
    0001 # 流水號    
55 # 校驗碼
7e # 標識位

7.查詢終端參數應答(前提:先鑑權)
7e0104009e0188554850151ac01abf100000000104000000b400000002040000001e00000003040000000800000010000000001100000000120000000013147365727665722e6e6174617070667265652e636300000018040000802300000027040000000000000029040000000f0000005a04000000000000005d0200000000005e0200000000f00110000000000000000000000000000000000000f002060000000000000000f0030100987e
7e # 標識位
0104009e0188554850151ac0 # 消息頭
    0104 # 消息ID
    009e # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    018855485015 # 終端手機號或設備號,這裏是設備號
    1ac0 # 流水號
1abf100000000104000000b400000002040000001e00000003040000000800000010000000001100000000120000000013147365727665722e6e6174617070667265652e636300000018040000802300000027040000000000000029040000000f0000005a04000000000000005d0200000000005e0200000000f00110000000000000000000000000000000000000f002060000000000000000f0030100 # 消息體
    1abf # 應答流水號,對應平臺下發終端參數查詢的流水號
    10   # 應答參數個數
    0000000104000000b4 # 00000001 終端心跳間隔    04 長度爲4    000000b4 參數值
    00000002040000001e #和上面參數類似,就不一一註解了。
    000000030400000008 
    0000001000 
    0000001100 
    0000001200 
    00000013147365727665722e6e6174617070667265652e6363 
    000000180400008023 
    000000270400000000 
    00000029040000000f 
    0000005a0400000000 
    0000005d020000 
    0000005e020000 
    0000f0011000000000000000000000000000000000 
    0000f00206000000000000 
    0000f0030100
98 # 校驗碼
7e # 標識位

8.設置終端參數
7e8103003001885548501500010700000001040000003c00000002040000000a00000003040000000a00000010000000001100000000120000000013005d7e
7e # 標識位
810300300188554850150001 # 消息頭
    8103 # 消息ID
    0030 # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    018855485015 # 終端手機號或設備號,這裏是設備號
    0001 # 流水號
0700000001040000003c00000002040000000a00000003040000000a0000001000000000110000000012000000001300 # 消息體
    07                 # 參數總數
    00000001040000003c # 00000001 終端心跳間隔    04 長度爲4    0000003c 參數值
    00000002040000000a # 和上面參數類似,就不一一註解了。
    00000003040000000a 
    0000001000 
    0000001100 
    0000001200 
    0000001300    
5d # 校驗碼
7e # 標識位

9.終端控制
7e81050001018855485015000104517e
7e # 標識位
810500010188554850150001 # 消息頭
    8105 # 消息ID
    0001 # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    018855485015 # 終端手機號或設備號,這裏是設備號
    0001 # 流水號
04 # 消息體
    04 # 終端復位
51 # 校驗碼
7e # 標識位

10.文本信息下發
7e83000004018855485015000100736174317e
7e # 標識位
830000040188554850150001 # 消息頭
    8300 # 消息ID
    0004 # 消息體屬性,消息體屬性每個位都爲零,也即第12-15位的消息包封裝項不存在,消息體也爲空
    018855485015 # 終端手機號或設備號,這裏是設備號
    0001 # 流水號
00736174 # 消息體
    00     # 標誌
    736174 # 文本信息,這裏是16進制,對應字符串爲sat
31 # 校驗碼
7e # 標識位
View Code

七、結尾說明

本文主要用於備忘記錄,站在巨人的肩膀上看的更高更遠,感謝各開源博主。JT808協議分爲2011版、2013版、2019版本。市面上大多是2013版,少數2019版。2013和2019版本的最大區別是報文固定頭部把手機號從原來的6字節BCD碼改成了10字節BCD碼。部標1078協議文檔明確說明了,協議是在JT/T 808協議的基礎上進行增加了大量的視頻指令,以前的終端32位報警,由於增加了視頻報警,拓展爲64位報警。能仔細看到這的應該是能幫到你。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章