一、前言介绍

开发一套能够支撑几万台北斗定位设备数据接收的服务端,用于接收北斗定位器定位数据的平台。项目基于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位报警。能仔细看到这的应该是能帮到你。

C#对接部标JT808协议实现北斗定位设备数据接收服务端的更多相关文章

  1. Http协议:客户端提交数据给服务端和从服务端获得数据,像WebView也是向百度的服务端发出一条Http请求,服务端返回HTML页面,客户端(浏览器)解析后展示出页面

    提交数据和获得数据的方式有很多,这里介绍一种,使用HttpURLConnection来向服务器提交数据或者获得数据. 获得数据: //传入网址,获得请求网页数据(XML文件数据或JSON文件数据) p ...

  2. 基于Redis构建10万+终端级的高性能部标JT808协议的Gps网关服务器(转)

    原文地址:http://www.jt808.com/?p=1282 在开发一个大规模的部标GPS监控平台的时候,就算我们花费再多的时间设计和规划,我们也并不能准确的预测出自己未来的车载终端接入量有多大 ...

  3. iOS xmpp协议实现聊天之openfire的服务端配置(一)

    今天弄这个openfire服务端的配置直接苦了一逼,只是好在最后最终配置好了.首先感谢@月光的尽头的博客给了我莫大的帮助. 切入正题,首先说一下iOS xmpp协议实现聊天openfireserver ...

  4. MQTT协议学习及实践(Linux服务端,Android客户端的例子)

    前言 MQTT(Message Queuing Telemetry Transport),是一个物联网传输协议,它被设计用于轻量级的发布/订阅式消息传输,旨在为低带宽和不稳定的网络环境中的物联网设备提 ...

  5. Java 实现TCP/IP协议的收发数据(服务端)

    功能如下: 注: 只有服务端,没有客户端,测试时采用第三方软件作为客户端的. 收发数据目前能正常收发数据,只是中文的会变成乱码显示. 采用Thread类实现一个收发数据的线程. 服务端代码: impo ...

  6. iOS xmpp协议实现聊天之openfire的服务端配置(二)

    本篇主要说一下怎样利用命令行来正确配置MySql. 首先打开终端: 1.为mysql起一个别名 alias mysql=/usr/local/mysql/bin/mysql 2.创建mysql的管理员 ...

  7. 基于Java Netty框架构建高性能的Jt808协议的GPS服务器(转)

    原文地址:http://www.jt808.com/?p=971 使用Java语言开发一个高质量和高性能的jt808 协议的GPS通信服务器,并不是一件简单容易的事情,开发出来一段程序和能够承受数十万 ...

  8. 网络编程 UDP协议 TCP局域网客户端与服务端上传下载电影示例

    UDP协议 (了解) 称之为数据包协议,又称不可靠协议. 特点: 1) 不需要建立链接. 2) 不需要知道对方是否收到. 3) 数据不安全 4) 传输速度快 5)能支持并发 6) 不会粘包 7) 无需 ...

  9. 基于Java Netty框架构建高性能的部标808协议的GPS服务器

    使用Java语言开发一个高质量和高性能的jt808 协议的GPS通信服务器,并不是一件简单容易的事情,开发出来一段程序和能够承受数十万台车载接入是两码事,除去开发部标808协议的固有复杂性和几个月长周 ...

  10. 基于Websocket+SpringMVC4推送部标Jt808终端报警(转)

    原文地址:http://www.jt808.com/?p=1263 在开发部标监控平台的时候,我们要及时的将部标终端报警推送到web界面上,以弹窗的形式提供给用户显示,要将报警显示在界面上,部标808 ...

随机推荐

  1. Jina AI x 矩池云Matpool |神经搜索引擎,一键构建

    图片.视频.语音等非结构化数据在快速增长,随着深度学习技术的不断升级,非结构化数据的搜索也逐渐形成可能.在这样的背景下,专注于神经搜索技术的商业开源软件公司--Jina AI,提出了神经搜索 (Neu ...

  2. ABP的版本升级,从7.2.2升级到7.2.3

    1.升级ABP CLI 见前面的文章:ABP开发需要用到的命令 更新最新版本: ~~~ dotnet tool update -g Volo.Abp.Cli ~~~ 2.升级ABP Suite 见前面 ...

  3. DataGear 数据可视化看板整合前端框架Vue

    DataGear 看板JS对象的loadUnsolvedCharts()函数,用于异步加载页面端动态生成的图表元素,利用它,可以很方便整合Angular.React.Vue等前端框架. 本文以Vue为 ...

  4. .NET应用国际化支持-葡萄牙语下如何不区分重音的模糊查询

    葡萄牙语,作为一种罗曼语族的语言,其正字法(orthography)并不使用音标系统来标记发音,而是有一套特定的拼写规则.然而,葡萄牙语中确实使用重音符号(acentos)来标记某些元音的重音(str ...

  5. 1. JVM体系结构

    1. 前言 作为Java工程师 ,jvm对于 java的重要性不言而喻,但是 我们又对jvm了解多少 Java的跨平台性 java发布的口号 "一处编译到处运行 " 依赖于jvm, ...

  6. Rsync 备份服务搭建

    Rsync 备份服务搭建 目录 Rsync 备份服务搭建 一. 前言 二. rsync 和 sersync 2.1 rsync 基本语法 2.2 本地文件传输 2.3 ssh 远程文件传输 2.4 基 ...

  7. 词根 ten 展开 持有 /tin/tent/tain “to hold”

    词根 ten 展开 持有 /tin/tent/tain "to hold" 记忆方式:en是拿出.忘了从哪里看的了.t是动作过去. 如果是 过去的时候已经拿出来,那就是 展开 延展 ...

  8. linux脚本免密的方法/不用输入密码

    第一种方法:使用管道(上一个命令的 stdout 接到下一个命令的 stdin):在脚本首行添加 #!/bin/bashecho password | sudo -S apt-get update 第 ...

  9. Windows逆向之配置虚拟机环境

    安装虚拟机环境 首先下载吾爱破解论坛专用版虚拟机软件VMWare Workstation 12,注册成功:再通过虚拟机启动论坛提供的WinXP系统镜像,在我的win11上完美运行. 实现文件互传 为了 ...

  10. 文件的特殊权限:SUID,SGID,SBIT

    文件的特殊权限:SUID,SGID,SBIT Set UID SUID权限只用于二进制文件 运行者必须有X权限 运行者在程序运行过程(run-time)中具有二进制文件拥有者(owner)的权限 例子 ...