一、前言介紹
開發一套能夠支撐幾萬臺北鬥定位設備數據接收的服務端,用於接收北斗定位器定位數據的平臺。項目基於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 # 標識位
七、結尾說明
本文主要用於備忘記錄,站在巨人的肩膀上看的更高更遠,感謝各開源博主。JT808協議分爲2011版、2013版、2019版本。市面上大多是2013版,少數2019版。2013和2019版本的最大區別是報文固定頭部把手機號從原來的6字節BCD碼改成了10字節BCD碼。部標1078協議文檔明確說明了,協議是在JT/T 808協議的基礎上進行增加了大量的視頻指令,以前的終端32位報警,由於增加了視頻報警,拓展爲64位報警。能仔細看到這的應該是能幫到你。