中间件服务实践

时间:2021-06-03 04:13:37

序言

16年年底,做了一个车载行业的项目,是基于公司的设备对出租车905协议解析保持与平台和设备其他进程保持通信的中间层服务。这个服务我们暂称为TaxiUsi,他和平台以及设备进程以及Client都有通信,且都是双向的,整体来说还是比较复杂的,由于机密问题,这里只给出整体的一个实现思路和流程,作为项目的总结,下面是整体的原理图:

中间件服务实践

可以看到,Client端和外设设备是通过Binder和TaxiUsi进行一个IPC操作,和905平台进行的是一个网络连接操作(Socket),而外设设备是通过蓝牙和硬件进行一个通信获取数据。而TaxiUsi的主要作用是和905平台一直保持一个通信的关系,并且对上报905平台数据一个组包,对下发数据包进行一个拆包,解包,传输的功能。可以看到TaxiUsi是连接三者之间的桥梁,是整个通信循环的心脏,对于整体项目而言,还是非常的重要的。

协议解析

首先来看看对905协议的解析,协议的解析非常的简单,但是有几点还是要引起我们的注意。

  • 传输规则

    一般网络传输大部分的协议都遵循大端模式和小端模式,什么是大小端模式呢?

    所谓的大端模式(Big-endian),是指数据的高字节,保存在内存的低地址中,而数据的低字节,保存在内存的高地址中;所谓的小端模式(Little-endian),是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。

    其实大小端本义是pc内存保存数据的一种方式,比如0x8086一个十六进制的数据,占用两个字节的内存,如果采用大端保存数据的话,则是 86 80 ,发送数据则从低地址开始读取,则发送过去的是 8086,假如对方机器采用小端模式保存的话,则接受到的数据就是0x8680,显然这不是我们想要的结果。

  • 转义规则

    在网络传输的过程中,我们需要标识一个“标签”来标识一个完整数据包的头尾,这样,接受端才能正确的解包,得到想要的数据。一般的像905协议用的0x7E来标识,但是这又有个问题了,假如数据包中有个数据正好是0x7E,那该怎么办,接受端会错解包,这样会有违我们的初心了。这时,我们会有个转义的操作,如下所示:
    0x7e ——-> 0x7d0x02
    0x7d ——-> 0x7d0x01
    但是这样处理,也会有一定的问题,什么问题呢?先来了解下整个数据包的格式:
    中间件服务实践

    每个数据包都会有一个校验位,比如接受端接受到数据,如果对数据包进行处理,得到的校验位是相同的,则是我们需要的数据包,否则就舍弃。那么传输的过程必须遵循以下规定:

    发送消息时: 消息封装->计算并填充校验码->转义
    接受消息时: 转义还原->验证校验码->解析消息

  • 对粘包、断包的处理

    首先来理解下粘包和断包的概念,其实这两者没有太大的差别,只是网络传输过程中一种很常见的现象,为什么会出现这种现象呢?这得从我们TCP协议说起,了解TCP协议的童鞋应该知道,协议中有很多的算法,比如拥塞阻塞算法等,这些算法都是维持协议稳定和效率不可缺失的一部分,当我们客户端一直疯狂的向服务端发送数据,这时,服务端会将客户端发来的数据包缓存在一个队列中,服务端拿出来一个一个处理,然后在一起回复,那你可能还会问,那假如客户端一直不停的发数据,那服务端一直处理到什么时候才回复呢?这要根据TCP连接的滑动窗口的大小来确定了。还有种情况就是,当网络情况不好的时候,服务端发过来的数据客户端没有应答,那么服务端会重传,这时候也可能会导致粘包。那断包什么时候会发生呢?就是当回复的数据大小超过服务端缓存队列的大小,那么会分两次发送,这时中间的那个数据包可能断开了,或许可能那个数据包中的数据非常的重要,这就是断包产生的情况。其实,解决的方法也很简单,用一个队列或者链表一直将服务端发来的数据写入,我们另开启一条线程,一直从中读取即可。因为,当时项目中用到的是Mina框架,Mina框架中有解码器,我们只需继承它内置的解码器即可。如果对Mina不熟的可以看下它的官方文档 Apache Mina ,至于解码的代码就不贴出了。

  • 解析处理

    主要是对字节和Java基本类型之间的转换,比较简单,我也从网络搜集整理了一份。如下所示:

public class ParseUtil {

private static final String TAG = "ParseUtil";


/**
* 字符串转换成指定长度的字符串
* @param value
* @param len
* @return
*/

public static String stringToString(String value,int len){
if(value == null){
value = "";
}
int length = value.length();
if(length > len){
value = value.substring(0, len);
}else{
for (int i = 0; i < (len-length); i++) {
value = "0"+value;
}
}
return value;

}

/**
* 获取指定长度"\0"结尾的数组转字符串
* @param data 字节数据包
* @param startIndex 开始位置
* @param length 截取长度
* @return 截取后的字节数据包
*/

public static byte[] byteToSubStringToByte(byte data[],int startIndex, int length){

try {
String dataHex = ParseUtil.parseByte2HexStr(ParseUtil.byteTobyte(data, startIndex, length));
//用“00”结尾截取十六进制字符串
String[] strarr = replaceStr(dataHex,"00","##").split("##");
byte[] strbyte = null;
if(strarr.length == 0){
strbyte = new byte[0];
}else{
if("".equals(strarr[0])){
strbyte = new byte[0];
}else{
strbyte = parseHexStr2Byte(strarr[0]);
}

}
return strbyte;
} catch (Exception e) {
e.printStackTrace();
return null;
}

}


/**
* 将字符串转换成二进制
* @param str
* @return
*/

public static byte[] stringToByte(String str){
try {
return (str+"\0").getBytes("GBK");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 将整数转换成二进制字节(先低字节后高字节)
* @param iSource
* @param iArrayLen
* @return
*/

public static byte[] int2Bytes(int iSource, int iArrayLen) {
byte[] bLocalArr = new byte[iArrayLen];
for (int i = 0; (i < 4) && (i < iArrayLen); i++) {
bLocalArr[i] = (byte) (iSource >> 8 * i & 0xFF);
}
return bLocalArr;
}

/**
* 获取整数转换成二进制字节
* @return
*/

public static String int2BytesStr(int iSource, int iArrayLen){
String str = parseByte2HexStr(int2Bytes(iSource,iArrayLen));
return str;
}

/**
* 获取指定长度字节数
* @param src
* @param startIndex
* @param length
* @return
*/

public static byte[] byteTobyte(byte[] src, int startIndex, int length)
{
byte[] des = new byte[length];
int i = 0;
for (int j = startIndex; i < length; ++j) {
des[i] = src[j];
++i;
}
return des;
}


public static byte[] longToByteOne(long num){
byte[] b = new byte[1];
b[0] = (byte)(int)(num >>> 0);
return b;
}

/**将二进制转换成16进制
* @param buf
* @return
*/

public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}

/**将16进制转换为二进制
* @param hexStr
* @return
*/

public static byte[] parseHexStr2Byte(String hexStr) {

if (hexStr.length() < 1)

return null;

byte[] result = new byte[hexStr.length()/2];

for (int i = 0;i< hexStr.length()/2; i++) {

int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);

int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);

result[i] = (byte) (high * 16 + low);

}

return result;
}

/**
* 字符串转换成ascii的16进制
* @param value
* @return
*/

public static String stringToAsciiHexString(String value){
StringBuffer sbu = new StringBuffer();
char[] chars = value.toCharArray();
for (int i = 0; i < chars.length; i++) {
sbu.append(Integer.toHexString((int)chars[i]));
}
return sbu.toString();
}

/**
* Bit转Byte
*/

public static byte BitToByte(String byteStr) {
int re, len;
if (null == byteStr) {
return 0;
}
len = byteStr.length();
if (len != 4 && len != 8) {
return 0;
}
if (len == 8) {// 8 bit处理
if (byteStr.charAt(0) == '0') {// 正数
re = Integer.parseInt(byteStr, 2);
} else {// 负数
re = Integer.parseInt(byteStr, 2) - 256;
}
} else {//4 bit处理
re = Integer.parseInt(byteStr, 2);
}
return (byte) re;
}

/**
* 1) 报文的发送端需要将待发送报文的数据内容中(从版本号至校验位,包括版本号和校验位)出现的1002H转义为0x10100202,将出现的1003H转义为0x10100303。
* @param str
* @return
*/

public static byte[] tropeByte(String str,int type){
byte[] result = new byte[str.length()/2];
if(type == 1){
if(str.indexOf("1002")>=0){
str = str.replaceAll("1002", "10100202");
}

if(str.indexOf("1003")>=0){
str = str.replaceAll("1003", "10100203");
}
}
// if(str.indexOf("10")>=0){
// str = str.replaceAll("10", "1010");
// }
str = replaceStr(str,"10", "1010");
result = ParseUtil.parseHexStr2Byte(str);

return result;
}

/**
* 为保证数据传输的透明,需对信息字段中出现的标志位进行转义处理,定义如下
* 7EH 《————》 7DH+02H;
* 7DH 《————》 7DH+01H;
* @param str
* @param type
* @return
*/

public static byte[] tropeYXByte(String str,int type){

byte[] result = new byte[str.length()/2];

if(type == 1){
str = replaceStr(str,"7D", "7D01");
str = replaceStr(str,"7E", "7D02");
// if(str.indexOf("7D")>=0){
// str = str.replaceAll("7D", "7D01");
// }
//
// if(str.indexOf("7E")>=0){
// str = str.replaceAll("7E", "7D02");
// }
}
str = replaceStr(str,"10", "1010");
result = ParseUtil.parseHexStr2Byte(str);

return result;
}

/**
* 转义操作
* @param str
* @param thq
* @param thh
* @return
*/

public static String replaceStr(String str,String thq,String thh){

StringBuffer des = new StringBuffer();
String s = "";
str = str+" ";
for(int i=0;i< str.length();i++){

if(i%2==0){
if(s.equals(thq)){
s = thh;
}

des.append(s);
s = "";
s = s+str.charAt(i);
}else{
s = s+str.charAt(i);
}
}
return des.toString();

}

/**
* 转义操作(2个字符转4个字符)
* @param str
* @param thq
* @param thh
* @return
*/

public static String replaceTwoToFour(String str,String thq,String thh){

StringBuffer des = new StringBuffer();
String s = "";
str = str+" ";
for(int i=0;i< str.length();i++){

if(i%2==0){
if(s.equals(thq)){
s = thh;
}

des.append(s);
s = "";
s = s+str.charAt(i);
}else{
s = s+str.charAt(i);
}
}
return des.toString();

}

/**
* 4个字符转2个字符
* @param data
* @param thq
* @param thh
* @return
*/

public static String replaceFourToTwo(String data,String thq,String thh){
StringBuffer dataBuffer = new StringBuffer();
for(int i=0;i< data.length();i+=2){
String dStr = data.substring(i, i+2);
dataBuffer.append(dStr);
String desc = dataBuffer.toString();
if(desc.endsWith(thq)){
dataBuffer = new StringBuffer();
desc = desc.substring(0, desc.length()-4);
dataBuffer.append(desc);
dataBuffer.append("##");
}
}
return dataBuffer.toString().replaceAll("##", thh);
}

/**
* 对DTU协议进行逆转义(1010转10)
* @param bytes
* @return
*/

public static byte[] dtuTransferredMeaning(byte[] bytes){

StringBuffer des = new StringBuffer();
String str = "";
for (int j = 0; j < bytes.length; j++) {

String hex = Integer.toHexString(bytes[j] & 0xFF).toUpperCase();
if (hex.length() == 1) {
hex = '0' + hex;
}

if(hex.equals("10")){
str = str+hex;
if(str.indexOf("1010")>=0){
str = str.replaceAll("1010", "10");
des.append(str);
str = "";
}

}else{
if(str.indexOf("1010")>=0){
str = str.replaceAll("1010", "10");
}
des.append(str);
str = "";
des.append(hex);
}
}
return ParseUtil.parseHexStr2Byte(des.toString());
}

/**
* @param bytes
* @return
*/

public static byte[] taximeterToplightReversal(byte[] bytes){
String str = parseByte2HexStr(bytes);
str = str.replaceAll("10100202", "1002").replaceAll("10100303", "1003");
return parseHexStr2Byte(str);

}


/**
* 转义
* @param bytes
* @return
*/

public static byte[] yxReversal(byte[] bytes){
String data = parseByte2HexStr(bytes).toUpperCase();
if(data.indexOf("7D02")>=0){
data = replaceFourToTwo(data, "7D02", "7E");
}

if(data.indexOf("7D01")>=0){
data = replaceFourToTwo(data, "7D01", "7D");
}
return parseHexStr2Byte(data);
}


/**
* 9A转义
* @param bytes
* @param type
* @return
*/

public static byte[] data9AEscape(byte[] bytes,int type){
String data = parseByte2HexStr(bytes).toUpperCase();

if(type == 1){
if(data.indexOf("9D02")>=0){
data = replaceFourToTwo(data, "9D02", "9A");
}

if(data.indexOf("9D01")>=0){
data = replaceFourToTwo(data, "9D01", "9D");
}
return parseHexStr2Byte(data);
}else{
byte[] result = new byte[data.length()/2];
data = replaceTwoToFour(data,"9D", "9D01");
data = replaceTwoToFour(data,"9A", "9D02");
result = parseHexStr2Byte(data);
return result;
}
}



/**
* 高地位反相排序
* @param bytes
* @return
*/

public static byte[]sortToByte(byte[] bytes){
byte[] des = new byte[bytes.length];
int i = 0;
for (int j = bytes.length-1; j >=0; j--) {
des[i] = bytes[j];
i++;
}
return des;
}

/**
* 异或运算和
* @param bytes
* @return
*/

public static byte[] byteOrbyte(byte[] bytes){
byte[] orbyte = new byte[1];
byte value = bytes[0];
for (int i = 1; i < bytes.length; i++) {
value = (byte) (value^bytes[i]);
}
orbyte[0] = value;
return orbyte;
}

/**
*String的字符串转换成unicode的String
*/

public static String stringToUnicode(String strText) throws Exception {
char c;
String strRet = "";
int intAsc;
String strHex;
for (int i = 0; i < strText.length(); i++) {
c = strText.charAt(i);
intAsc = (int) c;
strHex = Integer.toHexString(intAsc);
if (intAsc > 128) {
strRet += "\\u" + strHex;
} else {
// 低位在前面补00
strRet += "\\u00" + strHex;
}
}
return strRet;
}
/**
*unicode的String转换成String的字符串
*/

public static String unicodeToString(String hex) {
int t = hex.length() / 6;
StringBuilder str = new StringBuilder();
for (int i = 0; i < t; i++) {
String s = hex.substring(i * 6, (i + 1) * 6);
// 高位需要补上00再转
String s1 = s.substring(2, 4) + "00";
// 低位直接转
String s2 = s.substring(4);
// 将16进制的string转为int
int n = Integer.valueOf(s1, 16) + Integer.valueOf(s2, 16);
// 将int转换为字符
char[] chars = Character.toChars(n);
str.append(new String(chars));
}
return str.toString();
}

/**
* 得到经纬度的度、分(分+小数点后四位)
* @param latlng
* @param type 1 返回度 2 返回分 3 返回小数点后(1至2)两位 4 返回小数点(3至4)两位
* @return 十六进制字符串
*/

public static String getLatLng(double latlng,int type){

try {
int reLatlng = 0;
double min = (latlng-(int)latlng)*60;
String minStr = String.valueOf(min).substring(String.valueOf(min).indexOf(".")+1, String.valueOf(min).length());
int size = minStr.length();
if(minStr.length()<4){
for (int i = 0; i < (4-size); i++) {
minStr +="0";
}
}
switch (type) {
case 1:
reLatlng = (int)latlng;
break;
case 2:
reLatlng = (int) ((latlng-(int)latlng)*60);
break;
case 3:
reLatlng = Integer.parseInt(minStr.substring(0, 2));
break;
case 4:
reLatlng = Integer.parseInt(minStr.substring(2, 4));
break;
default:
break;
}
return ParseUtil.parseByte2HexStr(ParseUtil.longToByteOne((reLatlng)));
} catch (Exception e) {
AppLog.e(ExceptionUtil.getInfo(e), e);
e.printStackTrace();
return "00";
}
}

/**
* 解码经纬度
* @param latlngbyte
* @return
*/

public static Double decoderLatlng(byte[] latlngbyte){
try {
int no=0;
byte[] ad = ParseUtil.byteTobyte(latlngbyte, no, 1);
int adi = Integer.parseInt(ParseUtil.parseByte2HexStr(ad),16);
no+=1;
byte[] ac = ParseUtil.byteTobyte(latlngbyte, no, 1);
no+=1;
byte[] ac1 = ParseUtil.byteTobyte(latlngbyte, no, 1);
no+=1;
byte[] ac2 = ParseUtil.byteTobyte(latlngbyte, no, 1);
no+=1;
StringBuffer acBuffer = new StringBuffer();
acBuffer.append(Integer.parseInt(ParseUtil.parseByte2HexStr(ac),16));
acBuffer.append(Integer.parseInt(ParseUtil.parseByte2HexStr(ac1),16));
acBuffer.append(Integer.parseInt(ParseUtil.parseByte2HexStr(ac2),16));
Double acDouble = Double.parseDouble(acBuffer.toString())/10000;
Double latlng = adi + acDouble/60;
DecimalFormat df = new DecimalFormat("#.000000");
return Double.parseDouble(df.format(latlng));
} catch (Exception e) {
e.printStackTrace();
AppLog.e(ExceptionUtil.getInfo(e), e);
return 0.000000;
}
}

/**
* 得到16进制 年 月 日 时 分 秒
* @param formart
* @return 十六进制字符串
*/

public static String getDateTime(String formart){
String str = "00";
try {
String systemdate = new SimpleDateFormat(formart).format(Calendar.getInstance().getTime());
str = parseByte2HexStr(longToByteOne(Long.parseLong(systemdate)));
} catch (Exception e) {
AppLog.e(ExceptionUtil.getInfo(e), e);
e.printStackTrace();
}

return str;

}

/**
* 将二进制转换成字符串
* @param src
* @param startIndex
* @param length
* @return
*/

public static String byteToString(byte[] src, int startIndex, int length){
byte[] des = new byte[length];
int i = 0;
for (int j = startIndex; i < length; ++j) {
des[i] = src[j];
++i;
}
String str = null;
try {
str= new String(des,"gb2312");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
AppLog.e(ExceptionUtil.getInfo(e), e);
e.printStackTrace();
}
return str.trim();
}

/**
* 数字字符串转ASCII码字符串
* @param content 转换内容
* @return 转换后的ASCII码字符串
*/

public static String StringToAsciiString(String content) {
String result = "";
int max = content.length();
for (int i = 0; i < max; i++) {
char c = content.charAt(i);
String b = Integer.toHexString(c);
result = result + b;
}
return result;
}

/**
* 字节转换成位
* @param bytes
* @return
*/

public static String byteTobit(byte[] bytes){
String str = "";
for (int j = 0; j < bytes.length; j++) {
for(int i = 7; i >= 0; --i){
str +=(bytes[j] & (1 << i)) == 0 ? '0' : '1';
}
}
return str;
}



/**
* BCD码转为10进制串(阿拉伯数据)
* @param bytes 字节数组
* @return 转换后的字符串
*/

public static String bcd2Str(byte[] bytes){
StringBuffer temp=new StringBuffer(bytes.length*2);

for(int i=0;i<bytes.length;i++){
temp.append((byte)((bytes[i]& 0xf0)>>>4));
temp.append((byte)(bytes[i]& 0x0f));
}
return temp.toString().substring(0,1).equalsIgnoreCase("0")?temp.toString().substring(1):temp.toString();
}

/**
* 10进制串转为BCD码
* @param asc 字符串
* @return BCD码
*/

public static byte[] str2Bcd(String asc) {
int len = asc.length();
int mod = len % 2;

if (mod != 0) {
asc = "0" + asc;
len = asc.length();
}

byte abt[] = new byte[len];
if (len >= 2) {
len = len / 2;
}

byte bbt[] = new byte[len];
abt = asc.getBytes();
int j, k;

for (int p = 0; p < asc.length()/2; p++) {
if ( (abt[2 * p] >= '0') && (abt[2 * p] <= '9')) {
j = abt[2 * p] - '0';
} else if ( (abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) {
j = abt[2 * p] - 'a' + 0x0a;
} else {
j = abt[2 * p] - 'A' + 0x0a;
}

if ( (abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) {
k = abt[2 * p + 1] - '0';
} else if ( (abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) {
k = abt[2 * p + 1] - 'a' + 0x0a;
}else {
k = abt[2 * p + 1] - 'A' + 0x0a;
}

int a = (j << 4) + k;
byte b = (byte) a;
bbt[p] = b;
}
return bbt;
}

/**
* 转换成指定长度的字符串 少的部分以空格填充
* @param s
* @param fieldLength
* @return
*/

public static String appendSpaceRight(String s, int fieldLength){
if (s == null) {
s = "";
}
String ret = s;
int stringLength = s.length();
if (stringLength < fieldLength) {
for (int i = stringLength; i < fieldLength; ++i) {
ret = ret + " ";
}
}
else if (stringLength > fieldLength) {
ret = ret.substring(0, fieldLength);
}
return ret;
}

/**
* 转换成指定长度的字符串 少的部分以0填充
* @param s
* @param fieldLength
* @return
*/

public static String appendRight(String s, int fieldLength){
if (s == null) {
s = "";
}
String ret = s;
int stringLength = s.getBytes().length;
if (stringLength < fieldLength) {
for (int i = stringLength; i < fieldLength; ++i) {
ret = ret + "0";
}
}
else if (stringLength > fieldLength) {
ret = ret.substring(0, fieldLength);
}
return ret;
}

/**
* 返回指定长度的字节数组
* @param str
* @param len
* @return
*/

public static byte[] getByteToByte(String str,int len){
byte[] body = new byte[len];
int dstPos = 0;
//省域ID
System.arraycopy(str.getBytes(), 0, body, dstPos, str.getBytes().length);
return body;
}


// 将byte数组转换成InputStream
public static InputStream byteTOInputStream(byte[] in) throws Exception {

ByteArrayInputStream is = new ByteArrayInputStream(in);

return is;

}

/**
* 字符串转换成十六进制字符串
*/


public static String str2HexStr(String str) {

char[] chars = "0123456789ABCDEF".toCharArray();

StringBuilder sb = new StringBuilder("");

byte[] bs = str.getBytes();

int bit;

for (int i = 0; i < bs.length; i++) {

bit = (bs[i] & 0x0f0) >> 4;

sb.append(chars[bit]);

bit = bs[i] & 0x0f;

sb.append(chars[bit]);

}

return sb.toString();

}

/**

* 十六进制转换字符串

*/


public static String hexStr2Str(String hexStr) {

String str = "0123456789ABCDEF";

char[] hexs = hexStr.toCharArray();

byte[] bytes = new byte[hexStr.length() / 2];

int n;

for (int i = 0; i < bytes.length; i++) {

n = str.indexOf(hexs[2 * i]) * 16;

n += str.indexOf(hexs[2 * i + 1]);

bytes[i] = (byte) (n & 0xff);

}

return new String(bytes);

}

public static char ascii2Char(int ASCII) {
return (char) ASCII;
}

public static int char2ASCII(char c) {
return (int) c;
}
/**
* 将 ascii转换成字符串
* @param ASCIIs
* @return
*/

public static String ascii2String(String ASCIIs) {
String[] ASCIIss = ASCIIs.split(" ");
StringBuffer sb = new StringBuffer();
for (int i = 0; i < ASCIIss.length; i++) {
sb.append((char) ascii2Char(Integer.parseInt(ASCIIss[i])));
}
return sb.toString();
}

/**
* byte——>String
* @param src
* @return
*/

public static String bytesToHexString(byte[] src){
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}

/**
* 字符串编码转换的实现方法
* @param str 待转换编码的字符串
* @param newCharset 目标编码
* @return
* @throws UnsupportedEncodingException
*/

public static String changeCharset(String str, String newCharset)
throws UnsupportedEncodingException {
if (str != null) {
//用默认字符编码解码字符串。
byte[] bs = str.getBytes();
//用新的字符编码生成字符串
return new String(bs, newCharset);
}
return null;
}

/**
* 校验和
* @param msg 需要计算校验和的byte数组
* @param length 校验和位数
* @return 计算出的校验和数组
*/

public static byte[] sumCheck(byte[] msg, int length) {
long mSum = 0;
byte[] mByte = new byte[length];

/** 逐Byte添加位数和 */
for (byte byteMsg : msg) {
long mNum = ((long)byteMsg >= 0) ? (long)byteMsg : ((long)byteMsg + 256);
mSum += mNum;
} /** end of for (byte byteMsg : msg) */

/** 位数和转化为Byte数组 */
for (int liv_Count = 0; liv_Count < length; liv_Count++) {
mByte[length - liv_Count - 1] = (byte)(mSum >> (liv_Count * 8) & 0xff);
}

return mByte;
}

/** 从字符串中获取数字*/
public static String filtrateNumber(String text){
if(text != null && !text.equals("")){
String number = "";
String regEx="[^0-9]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(text);
number = m.replaceAll("").trim();
return number;
}else{
return null;
}
}

/**
* 带小数点字符串转整数
* @param str
* @param num
* @return
*/

public static int stringToint(String str,int num){
try {
return (int) (Double.parseDouble(str)*num);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}


代码设计

分析完协议的解析,我们就要把重点放到如何去设计框架的组成和实现他们之间的通信。其实如何实现得要看他的所需的功能而定的,比如TaxiUsi必须要保持长连接和平台保持通信,所以用了Mina。

  • 基本框架

    其实,我这个项目就三个核心模块,一个网络通信模块,一个IPC远程接口模块,一个本地持久层模块(因为没有发送出去的数据要保存在数据库)。如下图所示:
    中间件服务实践

    从图中可以看出整体业务逻辑还是蛮复杂的,这个只是整个程序的框架图,下面考虑一些实现的难点。

  • 消息重传机制

    细心的你可能会发现图中有个MsgPackage,这是我对数据包的又一层封装,那为什么要封装呢,主要有两个目的,第一种可能网络情况不是很好,需要我们临时把消息写入数据库,等网络状态的时候再重新发送,还有种情况是平台下发数据需要我们回复的,这样很容易和我们主动上报的数据发生混淆,按照905协议,消息重传机制满足的公式为: f(x+1) = f(x) * (N+1),其中f(x+1)表示每次重传后的应答超时时间,N表示重传次数。我们需要考虑两种情况,一种是平台主动发送消息给TaxiUsi服务,如果我们顺利回复平台成功,则流程结束,如果回复失败,则将消息传递给消息队列,根据下次发送的时间间隔再次发送,第二种,是我们自己主动上报给平台消息,当发送成功的时候,我们同样需要将消息发送给消息队列,如果在指定时间内,平台回复我们了,我们再清除消息队列中对应的消息,如果发送失败了,则传送给平台失败,我们不需要处理,下面给出TaxiUsi主动发送给平台消息的逻辑处理图:
    中间件服务实践

  • 其他机制

    在网络传输过程中,连接断了是很正常的事情,我们要设计出一套能够快速响应并且低功耗的连接策略,比如什么时候重新连接,多长时间检测一次都是我们所需要考虑的事情,尽量使用最少的系统资源用到极致。还有在系统资源不足的情况下,或者内部因素导致的,比如异常等,导致线程挂掉了,那么我们还需要运行个后台线程,用于检测他们的生命状态,如果死亡,则立即重启。