原文地址:http://www.jt808.com/?p=971
使用Java语言开发一个高质量和高性能的jt808 协议的GPS通信服务器,并不是一件简单容易的事情,开发出来一段程序和能够承受数十万台车载接入是两码事,除去开发部标jt808协议的固有复杂性和几个月长周期的协议Bug调试,作为大批量794车载终端接入的服务端,需要能够处理网络的闪断、客户端的重连、安全认证和消息的编解码、半包处理等。如果没有足够的网络编程经验积累和深入了解部标jt808协议文档,自研的GPS服务器往往需要半年甚至数年的时间才能最终稳定下来,这种成本即便对一个大公司而言也是个严重的挑战。
1)我们在开发部标协议的GPS服务器,需要解决以下几个问题:
1. 通信服务器固有的连接管理,能够处理网络的闪断、客户端的重连、安全认证和消息的编解码、半包处理;
2. 海量终端接入的高并发性能;
3. 良好的内存管理,车载终端的连接本事是基于GPRS的无线连接,车辆在野外高速移动过程中,信号处于不稳定状态,虽然是基于长连接,但是这种连接不断的中断和接入,服务器在处理终端接入,数据解析,报警分析,批量入库的时候,能够内存分配合理,不会造成内存泄漏,在百万次的调用中不会造成内存累计上升;
2)要开发一个高性能的GPS服务器的三个主题:
1) 传输:用什么样的通道将数据发送给对方,BIO、NIO或者AIO,IO模型在很大程度上决定了框架的性能。
2) 协议:采用什么样的通信协议,HTTP或者内部私有协议。协议的选择不同,性能模型也不同。相比于公有协议,内部私有协议的性能通常可以被设计的更优。
3) 线程:数据报如何读取?读取之后的编解码在哪个线程进行,编解码后的消息如何派发,Reactor线程模型的不同,对性能的影响也非常大。
Netty是业界最流行的NIO框架,可以很好的解决上面三个问题,它的可靠性、高性能和可扩展性已经得到了上百上千的商用项目验证,相对于.NET中的基于完成端口的通信框架,它要优越的多,它的优点总结如下:
1. API使用简单,开发门槛低;
2. 功能强大,内聚了很多实用的功能,简化用户的开发;
3. 定制性好,通过ChannelPipeline机制可以灵活的进行功能定制和扩展;
4. 性能高;
5. 成熟稳定,社区活跃,Bug的修复周期比较短,新功能不断的被加入,用户可以体验到更多、更实用的功能。
6. 经历了大规模不同行业的商用考验,架构质量得到了充分的验证。
Netty为了向使用者屏蔽NIO通信的底层细节,在和用户交互的边界做了封装,目的就是为了减少用户开发工作量,降低开发难度。ServerBootstrap是Socket服务端的启动辅助类,用户通过ServerBootstrap可以方便的创建Netty的服务端。
3)编码器和解码器的设计
对于jt808协议的解析处理,需要编写自定义的解码器了,目前Netty提供了多个基础编码器可以供开发者进行继承和拓展,开发的时候,需要了解这几个解码器的主要作用,主要用于那些通信数据传输的场景。
为了降低用户的开发难度,Netty对常用的功能和API做了装饰,以屏蔽底层的实现细节。编解码功能的定制,对于熟悉Netty底层实现的开发者而言,直接基于ChannelHandler扩展开发,难度并不是很大。但是对于大多数初学者或者不愿意去了解底层实现细节的用户,需要提供给他们更简单的类库和API,而不是ChannelHandler。
Netty在这方面做得非常出色,针对编解码功能,它既提供了通用的编解码框架供用户扩展,又提供了常用的编解码类库供用户直接使用。在保证定制扩展性的基础之上,尽量降低用户的开发工作量和开发门槛,提升开发效率。
通常我们也习惯将编码(Encode)称为序列化(serialization),它将对象序列化为字节数组,用于网络传输、数据持久化或者其它用途。
反之,解码(Decode)/反序列化(deserialization)把从网络、磁盘等读取的字节数组还原成原始对象(通常是原始对象的拷贝),以方便后续的业务逻辑操作。
Netty预置的编解码功能列表如下:base64、Protobuf、JBoss Marshalling、spdy等。
在GPS行业当中,对于终端通信协议的设计有多种:
1)基于字符串设计的方式,这种方式就是终端发送一个ASCII字符串,然后服务器获取后基于约定的分隔符分割为一个数组,再依次从数组中获取对应下标的数据,这种方式通常是深圳小的硬件公司的技术水平较低的开发团队喜欢采用,这种方式容易理解,但传输字节较多,效率较低,比较占用流量,不适用于基于流量套餐的无线卡传输。
2)基于定长协议的传输。
① 消息定长,例如每个报文的大小为固定长度200字节,如果不够空位补空格。 ② 在包尾增加回车换行符进行分割,例如FTP协议。
③ 将消息分为消息头和消息体,消息头中包含表示消息总长度(或者消息体总体长度) 的字段,通常涉及思路为消息头的第一个字段使用init32来表示消息的总长度。
④ 更复杂的应用层协议,例如部标808协议
部标协议数据包设计的特点:
1) 基于分隔符,包头和包围用0x7E来区分一个完整的数据包;
2) 动态包头,不想其他协议在设计的时候,包头长度都是固定长度,而808协议的包头长度是不固定的;
3) 包体是动态长度,长度从包头中读取。
解码器就是要根据协议设计的数据包的规则,来对字节流进行解析,解码成完整的数据包。Netty提供了一下几种基础的解码器提供给我们,供继承或直接使用:
1)LineBasedFrameDecoder的工作原理是它依次便利ByteBuf中的可读字节,判断看是否有“\n”或者“\r\n”,如果有就以此位置为结束位置,从可读索引到结束位置区间的字节就组成了一行。他是以换行符为结束标志的解码器,支持携带结束符或者不携带结束符两种解码方式,同时支持配置当行的最大长度。如果连续读取带最大长度后任然没有发现换行符,就会抛出异常,同时忽略掉之前读到的异常码流。
2)StringDecoder的功能非常简单,就是将接收到的对象转换成字符串,然后继续调用后面的handler。LineBasedFrameDecoder+StringDecoder组合就是按行切换的文本解码器,它被设计用来支持TCP的粘包和拆包。
3)DelimiterBasedFrameDecoder特殊符号解码器,其已经过滤掉了分隔符。
4)FixedLengthFrameDecoder固定长度解码器,它能够按照指定的长度对消息进行自动解码。利用FixedLengthFrameDecoder解码器无论一次接受到多少数据报,它都会按照构造函数中设置的固定长度进行解码,如果是半包消息,FixedLengthFrameDecoder会缓存半包消息并等待下个包到达后进行拼包,直到取到一个完整的包。
4)基于Netty开发一个部标808协议的服务器,具体的步骤如下:
1)我们使用Netty要做的工作就是编写编码器和解码器,然后按照Netty的要求来编写调用,最后得到一个完整的jt808协议的数据包。
2)按照数据处理链条,分工职责,为了提高终端接入能力和数据分析、入库能力,将终端消息的处理分成独立的五级处理模块,每个处理模块都是异步独立的,每个模块内都含有独立的处理队列,互不影响,提高数据的吞吐量和系统的响应能力。
1)第一级:实时数据解析入库,入库能力决定了客户端所看到的实时数据是否延迟;
2)第二级:报警分析并入库(包括32种jt808协议规定的报警、停车报警和路线偏移报警),报警分析只有快速分析才能快速的推送到前端客户端;
3) 第三级:消息应答和指令下发,应答可以有一定的延迟,而不影响整个系统性能。
4)第四级:报表统计,由于油量统计、里程统计、上线率统计,需要定时扫描数据库,生成每个时段的数据统计提供给报表查询使用.
5)第五级:日志记录和显示
由于netty和Mina都能非常好的和spring容器集成在一起,引入spring框架,基于面向接口编程,系统可以同时支持netty和mina两种通信框架,用户可以根据自己的环境,通过配置决定是用mina或者是netty。
5)连接管理和日志报文监控
在windows系统,可以运行swing界面进行监控,如果运行在阿里云的linux系统上,可以直接编写java shell脚本,基于后台服务运行,通过java 命令来调用当前的连接,日志监控,主要依赖于log文件。
6) jt808系统源码易于和第三方系统如PHP平台进行集成,提供了接口和清晰的数据库文档。
jt808服务器,协议解析、命令下发、报警解析、数据库入库、数据统计、压力测试等各方面都要考虑的非常充分完善,808协议全协议栈的实现,还要有库表文档和字段说明。
808服务器主要用的库表:
部门表 |
车辆表 |
终端表 |
实时表 |
历史轨迹表 |
驾驶员表 |
电子运单表 |
报警统计报表 |
报警推送表 |
电子围栏表 |
线段表 |
终端命令表 |
终端参数表 |
基础数据表 |
多媒体上传记录 |
行驶记录仪表 |
里程统计中间表 |
部门上线率统计表 |
油量变化记录 |
油量和里程统计表 |
车辆上线率统计表 |
五分钟一次油量和里程记录 |
6) 本808服务器对于部标jt808全部协议栈都进行了充分的支持:
序号 |
项目名称 |
触发条件 |
预期回应 |
01 |
终端心跳 |
根据设定的心跳时间参数,定时自动上报 |
主动上发,需要平台提供通用应答,长时间无法获得应答将导致程序自动断开重连。 |
02 |
设置终端参数 |
平台下发 |
回复通用应答,可设置的参数参考行标协议文本8.8 |
03 |
超速设置 |
平台下发 |
回复通用应答 |
04 |
疲劳驾驶设置 |
平台下发 |
回复通用应答 |
05 |
超时停车设置 |
平台下发 |
回复通用应答 |
06 |
查询终端参数 |
平台下发 |
回复0×0104应答参照行标协议文本8.10描述,回复的参数信息包含前述设置指令所设定修改的内容 |
07 |
终端控制 |
平台下发 |
回复通用应答 |
08 |
位置信息查询 |
平台下发 |
回复固化的位置信息:26.033435N,119.139317E 高度23.59,速度0,角度0 |
09 |
临时位置跟踪控制 |
平台下发 |
回复通用应答 |
10 |
文本信息下发 |
平台下发 |
回复通用应答 |
11 |
追加事件 |
平台下发 |
回复通用应答 |
12 |
删除特定事件 |
平台下发 |
回复通用应答 |
13 |
更新事件 |
平台下发 |
回复通用应答 |
14 |
修改事件 |
平台下发 |
回复通用应答 |
15 |
删除全部事件 |
平台下发 |
回复通用应答 |
16 |
追加新事件 |
平台下发 |
回复通用应答 |
17 |
提问下发 |
平台下发 |
程序立即回复通用应答,稍后由用户手动操作回复提问 |
18 |
信息点播菜单追加 |
平台下发 |
回复通用应答 |
19 |
信息点播菜单修改 |
平台下发 |
回复通用应答 |
20 |
信息点播菜单更新 |
平台下发 |
回复通用应答 |
21 |
信息点播菜单删除 |
平台下发 |
回复通用应答 |
22 |
信息点播菜单追加 |
平台下发 |
回复通用应答 |
23 |
信息点播菜单修改 |
平台下发 |
回复通用应答 |
24 |
信息点播菜单更新 |
平台下发 |
回复通用应答 |
25 |
信息服务 |
平台下发 |
回复通用应答 |
26 |
电话回拨 |
平台下发 |
回复通用应答,不触发任何逻辑动作,请关注日志 |
27 |
电话回拨_监听 |
平台下发 |
回复通用应答,不触发任何逻辑动作,请关注日志 |
28 |
设置电话本_删除 |
平台下发 |
回复通用应答 |
29 |
设置电话本_更新电话本 |
平台下发 |
回复通用应答 |
30 |
设置电话本_追加电话本 |
平台下发 |
回复通用应答 |
31 |
设置电话本_修改电话本 |
平台下发 |
回复通用应答 |
32 |
车辆控制_车门解锁 |
平台下发 |
回复通用应答 |
33 |
车辆控制_车门加锁 |
平台下发 |
回复通用应答 |
34 |
更新圆形区域 |
平台下发 |
回复通用应答 |
35 |
删除圆形区域 |
平台下发 |
回复通用应答 |
36 |
更新矩形区域 |
平台下发 |
回复通用应答 |
37 |
删除矩形区域 |
平台下发 |
回复通用应答 |
38 |
更新多边形区域 |
平台下发 |
回复通用应答 |
39 |
删除多边形区域 |
平台下发 |
回复通用应答 |
40 |
更新路线 |
平台下发 |
回复通用应答 |
41 |
删除所有路线 |
平台下发 |
回复通用应答 |
42 |
行驶记录仪数据采集命令 |
平台下发 |
根据形式记录议命令字发回测试数据,数据可能会产生分包 |
43 |
行驶记录仪参数下传命令 |
平台下发 |
回复通用应答 |
44 |
抓拍立即上传 |
平台下发 |
回复通用应答,随后分包上发固化的多媒体数据,上发数据与多媒体通道有关* |
45 |
录像立即上传 |
平台下发抓拍指令附带录像提示 |
回复通用应答,随后分包上发固化的多媒体数据,上发数据与多媒体通道有关* |
46 |
检索多媒体列表 |
平台下发 |
根据查询的多媒体类型回复固化的多媒体列表数据,参考行标协议8.46,通道与时间参数将被解析于日志中,但是不会影响列表内容 |
47 |
存储多媒体上传 |
平台下发 |
根据通道*和多媒体类型定义回传多媒体数据,时间和事件等参数将被正确解析与日志中,但是不会影响上发的多媒体内容 |
48 |
录音32Kbps立即上传 |
平台下发 |
回复通用应答 |
49 |
数据下行透传 |
平台下发 |
回复通用应答 |
50 |
平台RSA公钥 |
平台下发 |
回复终端RSA公钥,本软件将把平台下发的RSA公钥回发以验证协议的正确性 |