一、前言介绍

开发一套能够支撑几万台北斗定位设备数据接收的服务端,用于接收北斗定位器定位数据的平台。项目基于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. RibbonRoutingFilter是如何工作的

    在讲RibbonRoutingFilter是如何工作之前,也有一些比较重要的类需要去提前了解. 重要的类 RequestContext 请求上下文,用于存储线程中对应的请求以及响应 public cl ...

  2. 【Azure Redis 缓存 Azure Cache For Redis】Redis支持的版本及不同版本迁移风险

    问题描述 1. Azure Redis缓存支持的版本包括4.0以及6.0(预览) 这种情形下,可以使用PaaS服务提供的 Azure Redis 缓存(4.0版本).Azure Redis对6.0的支 ...

  3. 【Azure 应用服务】在App Service中调用外部服务API时需要携带客户端证书,而多次调用的情况下会出现WindowsCryptographicException Keyset does not exist异常

    问题描述 在App Service中调用外部服务API时需要携带客户端证书,而多次调用的情况下会出现WindowsCryptographicException Keyset does not exis ...

  4. 简单封装 Flurl

    FlurlHttpClient类 public class FlurlHttpClient { private readonly FlurlClient client; public FlurlHtt ...

  5. ElasticSearch基本查询使用(2)

    在介绍本章之前,需要先打开安装的Kibana页面, 并打开命令行工具页面: 并且根据上节的介绍,我们需要根据中文搜索,所以需要在建立映射时,指定中文字段的分词器为Ik分词器, 默认为英文分词器,每个中 ...

  6. C++ //set/multiset 容器 //set不可以插入重复的数字 multiset可以插入重复的数字 //ste容器构造和赋值 //set大小和交换 //set 插入和删除 //set查找和统计 //set 和 multiset 区别 //pair 对组创建 //set存放自定义数据类型 //set内置数据 进行排序

    1 //set/multiset 容器 //set不可以插入重复的数字 multiset可以插入重复的数字 2 //ste容器构造和赋值 //set大小和交换 //set 插入和删除 3 //set查 ...

  7. Codeforces Round 928 (Div. 4)(A、B、C、D、E、G)

    目录 A B C D E G A 统计A.B输出 #include <bits/stdc++.h> #define int long long #define rep(i,a,b) for ...

  8. 辨析Java与网络通信中的编码与解码

    在Java字符流上下文中的编码和解码,以及在网络通信中的编码概念. 在Java中,当我们谈论字符流(如Reader和Writer)时,编码和解码主要涉及将字符数据转换为字节数据,以及将字节数据转换回字 ...

  9. Nacos服务跨分组调用

    一. 问题背景 nacos有两种服务隔离的机制,一个是空间namespace,一般我们用namespace区分环境,另外一个是分组group,nacos的默认调用机制是同namespace下的同gro ...

  10. chrome 快速执行 snippets 1. F12 2. Ctrl+Shift+P 3. show snippets 4. 上下选择 5. Ctrl + Enter

    chrome 快速执行 snippets F12 Ctrl+Shift+P show snippets 上下选择 Ctrl + Enter