第一次遇到和客户做数据对接,客户不需要提供任何接口的场景……
905.4-2014协议,是交通运输部公路科学研究院起草定制的一个协议标准,它也是基于TCP之上的一个应用层传输协议。协议详情,请参考此文。
谈谈我对这个协议的理解。
首先,我们知道,互联网的数据是由0和1构成的,我们在浏览器中能看到对应的数据,是因为浏览器接收的数据遵循了一定的规范,专业点讲就是协议。http协议就是这样的一个协议,它把传输在网络中的数据变得具有极高的可读性。
两个平台之间进行数据交换时,所交换的消息的数据结构由五部分组成。
905.4王国是个纪律严明的国度,长久以来,便形成了有问必答的一个交流方式。不像人类那么复杂,会因为情绪的问题刻意避开问题,显得那么不礼貌。
这个王国有总是会分为两个派系,Server(上级平台)和Client(下级平台)。一个无休止的问,一个无休止的答。
上级平台与下级平台之间应采用面向连接的链路通信方式,具体要求如下:
上级平台对下级平台安全验证流程应遵循以下规定:
_REQ
结尾;而下级平台向上级平台发送的请求消息一般以“UP”开头,以后缀_REQ
结尾;_RSP
来标识绪尾。下表列举了在905.4协议中可以实现的一些业务功能。
消息种类 | 业务数据类型名称 | 业务数据类型标识 | 数值 |
---|---|---|---|
链路管理类 | 链路登录请求消息 | UP_CONNECT_REQ | 0x2001 |
链路管理类 | 链路登录应答消息 | UP_CONNECT_RSP | 0x2002 |
链路管理类 | 链路注销请求消息 | UP_CONNECT_REQ | 0x2003 |
链路管理类 | 链路注销应答消息 | UP_DISCONNECT_RSP | 0x2004 |
链路管理类 | 链路连接保持请求消息 | UP_MINKTEST_REQ | 0x2005 |
链路管理类 | 链路连接保持应答消息 | UP_LINKTEST_RSP | 0x2006 |
链路管理类 | 链路断开通知消息 | UP_DISCONNECT_INFORM | 0x2007 |
链路管理类 | 下级平台主动关闭链路通知消息 | UP_CLOSELINK_INFORM | 0x2008 |
链路管理类 | 上级平台主动关闭链路通知消息 | DOWN_CLOSELINK_INFORM | 0x9008 |
车辆动态信息交换类 | 链路动态信息交换消息 | UP_EXG_MSG | 0x2100 |
信息统计类 | 统计信息交换消息 | UP_SUM_MSG | 0x2200 |
静态信息交换类 | 链路静态信息交换消息 | UP_BASE_MSG | 0x2300 |
/**
* 返回应答消息
* @param requestBytes
* @return
*/
public static byte[] buildResponse(byte[] requestBytes,SocketChannel clientChannel){
log.info("正在解析请求数据:" + ByteUtil.bytesToHex(requestBytes));
//检查请求的登录状态
AuthHelper authHelper = SpringContextUtils.getBean(AuthHelper.class);
HashMap<String, Object> headerMap = LocationParserHelper.getHeaderMap(requestBytes);
boolean checkLogin = authHelper.checkLogin(headerMap);
int requestMsgId = MapUtil.getInt(headerMap, "MSG_ID");
DataParserWorker dataParserWorker;
MsgEnum msgEnum = MsgEnum.getMsgEnum(requestMsgId);
//如果交换类消息,则需要从消息体中确认消息的子类型
if (MsgEnum.UP_EXG_MSG.equals(msgEnum)){
byte[] dataTypeArr = ArrayUtils.subarray(requestBytes, HEADER_LENGTH, 25);
msgEnum = MsgEnum.getMsgEnum(ByteUtil.toShort(dataTypeArr));
if(msgEnum == null){
log.warn("交换扩展子业务,本系统不作处理……");
return new byte[0];
}
log.info("解析子业务成功:" + msgEnum.getDescp());
}
if (checkLogin || MsgEnum.UP_CONNECTION_REQ.equals(msgEnum) ) {
switch (msgEnum) {
case UP_CONNECTION_REQ:
String ip = null;
try {
ip = clientChannel.getRemoteAddress().toString().replace("/","");
ip = ip.split(":")[0];
} catch (IOException e) {
e.printStackTrace();
log.error("IP地址不正确");
}
dataParserWorker = new AuthWorker(requestBytes,ip);
break;
//车辆运营数据上报
case UP_EXG_MSG_REAL_RUN_REQ:
dataParserWorker = new BusinessReportWorker(requestBytes);
break;
case UP_EXG_MSG_REAL_L0CATION_REQ://定位数据采集
dataParserWorker = new LocationReportWorker(requestBytes);
break;
case UP_LINKTEST_REQ://链路连接请求
dataParserWorker = new PingWorker(requestBytes);
break;
default:
throw new IllegalStateException("Unexpected value: " + requestMsgId);
}
} else {
//发送登录反馈
log.info("客户端未登录或登录已超时:" + headerMap.get("MSG_GNSSCENTERID"));
dataParserWorker = new SessionOverWorker(requestBytes);
}
return dataParserWorker.doParse();
}
子涵把整个协议实现过程用到的一些技术知识点进行了整理,欢迎大家一起学习~。