1、我们先设置一些常量数据

package cn.cutter.ztesoft.HuWeiMML.constrant;

/**
* @description: AAA接口常量设置
* @author: xiaof
* @create: 2018-07-26 10:07
**/
public class InfAAAMissionConstrant { /**
*订单号,工单号,宽带账号
*/
public static final String AAA_ORDER_ID = "orderId";
public static final String AAA_WORK_ORDER_ID = "workOrderId";
public static final String AAA_ACCOUNT = "accNbr";
public static final String USER_MOBILE = "mobile"; /**
* 配置信息
*/
// public static final String IOM_IP; public static final String AAA_IP = "IP";
public static final String AAA_PORT = "PORT";
public static final String AAA_USER_NAME = "USERNAME";
public static final String AAA_PASS_WORD = "PASSWORD";
public static final String AAA_CONFIG_TYPE = "AAA_SOCKET_INFO"; public static final String SERVICEFLAG = "AAA"; //消息头 AAA_MSG_STARTING_INT \x1C\x1D\x1E\x1F
public static final String AAA_MSG_STARTING = "`SC`"; public static final int AAA_MSG_STARTING_INT = 0x1C1D1E1F; public static final byte[] AAA_MSG_STARTING_BYTE = {0x1C, 0x1D, 0x1E, 0x1F}; /**
* AAA IIN类型格式发送字节消息 0x60, 0x53, 0x43, 0x60
*/
public static final byte[] AAA_IIN_MSG_STARTING_BYTE = {0x60, 0x53, 0x43, 0x60}; public static final int AAA_MSG_STARTTAG_LEN = 4; //消息开始标识长度 public static final int AAA_MSG_COMM_LEN = 12; /**
* 消息头长度
*/
public static final int AAA_MSG_HEAD_LEN = 20; /**
* 消息长度部位
*/
public static final int AAA_MSG_INFO_LEN = 4; //消息版本号
public static final String AAA_MSG_VERSION = "1.00"; public static final Integer AAA_MSG_VERSION_LEN = 4; public static final int AAA_MSG_STARTING_LEN = 4; /**
* 终端标识
*/
public static final String AAA_MSG_TERMINAL = "internal"; public static final Integer AAA_MSG_TERMINAL_LEN = 8; public static final Integer AAA_SERVICE_CODE_LEN = 8; public static final Integer AAA_MAX_HEAD_LEN = 56; // 会话控制字包括:DLGLGN,DLGBEG,DLGCON,DLGEND。
// 说明
// 操作员登录MML Server时客户端发DLGLGN,在进行其他操作时,客户端均发DLGCON。
// 操作员退出时,MMLServer给营帐的返回消息中会话控制字为DLGEND,表示会话的结束。
/**
* 会话id长度
*/
public static final int AAA_DLG_ID_LEN = 8; public static final String AAA_DLG_LGN = "DLGLGN"; public static final String AAA_DLG_BEG = "DLGBEG"; public static final String AAA_DLG_CON = "DLGCON"; public static final String AAA_DLG_END = "DLGEND"; /**
* 会话控制字长度
*/
public static final int AAA_DLG_CONTROLLER_LEN = 6; /**
* 会话保留字
*/
public static final int AAA_DLG_RSVD_LEN_DLGRSVD = 4;
/**
* 会话头长度
*/
public static final int AAA_DLG_HEAD_LEN = 18; // 事务控制字包括:TXBEG,TXCON,TXEND。
// 说明
// 由Provision发起的操作,其事务控制字填写TXBEG。
// 当一条MML命令的消息结束时,MML Server返回给Provision的事务控制字为TXEND,表示一条事务结束。 /**
* 事务id长度
*/
public static final int AAA_TX_ID_LEN = 8; public static final String AAA_TX_BEG = "TXBEG"; public static final String AAA_TX_CON = "TXCON"; public static final String AAA_TX_END = "TXEND"; public static final Integer AAA_TX_CONTROLLER_LEN = 6; /**
* 事务保留字长度
*/
public static final int AAA_TX_RSVD_LEN_DLGRSVD = 4; /**
* 事务头长度
*/
public static final int AAA_TX_HEAD_LEN = 18; /**
* 校验和长度
*/
public static final Integer AAA_CHK_LEN = 4; /**
* IIN校验和长度
*/
public static final Integer AAA_IIN_CHK_LEN = 8; /**
* 报文字段
*/
/**
* 返回值。十进制整数类型。
* 0表示执行成功,其他返回值的解释请参见DESC字段
*/
public static final String RETN = "RETN"; /**
* 查询属性名列表,以“&”分隔。
*/
public static final String ATTR = "ATTR"; /**
* 返回的记录的值,用&分割结果。s
*/
public static final String RESULT = "RESULT"; }

2、创建对应的信息vo载体

package cn.cutter.ztesoft.HuWeiMML.vo;

/**
* @program:
* @description:
* @author: xiaof
* @create: 2018-07-26 15:25
**/
public class MsgInfo {
/**
* 消息和消息长度
*/
private byte msg[];
private int msgLen; // 查询用户信息获取 用户IP地址 USERIPADDRESS,USERPORT 用户端口号,业务标识SERVICEFLAG 默认AAA
private String userIpAddress;
private String userPort;
private String serviceFlag; //AAA服务类型,默认C280
private String serviceCode = "C280"; //AAA用来做指令标识
private String workOrderId; private String userName;
private String passWord; public byte[] getMsg() {
return msg;
} public void setMsg(byte[] msg) {
this.msg = msg;
} public int getMsgLen() {
return msgLen;
} public void setMsgLen(int msgLen) {
this.msgLen = msgLen;
} public String getUserIpAddress() {
return userIpAddress;
} public void setUserIpAddress(String userIpAddress) {
this.userIpAddress = userIpAddress;
} public String getUserPort() {
return userPort;
} public void setUserPort(String userPort) {
this.userPort = userPort;
} public String getServiceFlag() {
return serviceFlag;
} public void setServiceFlag(String serviceFlag) {
this.serviceFlag = serviceFlag;
} public String getServiceCode() {
return serviceCode;
} public void setServiceCode(String serviceCode) {
this.serviceCode = serviceCode;
} public String getWorkOrderId() {
return workOrderId;
} public void setWorkOrderId(String workOrderId) {
this.workOrderId = workOrderId;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} public String getPassWord() {
return passWord;
} public void setPassWord(String passWord) {
this.passWord = passWord;
}
}

3、创建编码解码器,进行报文的编码解码(关键,划重点哦,特别是校验和的计算)

package cn.cutter.ztesoft.HuWeiMML.Template;

import cn.cutter.ztesoft.HuWeiMML.vo.MsgInfo;

import java.io.IOException;

/**
* @program:
* @description: MML消息解码,编码器
* @author: xiaof
* @create: 2018-08-15 11:38
**/
public interface MsgCoder { /**
* 指令编码
* @param msg
* @return
* @throws IOException
*/
byte[] toWire(MsgInfo msg) throws IOException; /**
* 指令解码
* @param input
* @return
* @throws IOException
*/
byte[] fromWire(byte input[]) throws IOException;
}
package cn.cutter.ztesoft.HuWeiMML.Template;

import cn.cutter.ztesoft.HuWeiMML.constrant.InfAAAMissionConstrant;
import cn.cutter.ztesoft.HuWeiMML.vo.MsgInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import java.util.Arrays; /**
*
*
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | 0x60, 0x53, 0x43, 0x60 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | msg length 4B |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* | msg head 20B |
* | |
* | |
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* | Conversation head (18B) |
* | |
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
* | |
* | transaction head (18) |
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* ~ operator msg(N * 4B) ~
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* | check(8B) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* @program:
* @description: MML指令编码解析
* @author: xiaof
* @create: 2018-08-15 11:40
**/
public class MMLMsgBinCoder implements MsgCoder { private static final Log logger = LogFactory.getLog(MMLMsgBinCoder.class); @Override
public byte[] toWire(MsgInfo msg) { //1.输出消息开始标识 4字节
byte beginMarkBytes[] = InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE; //2.输出消息的长度4字节 从消息头到操作信息结束(包括填充的空格)的长度,16进制字符(0-F)表示的16位整数(4B),取值范围为56到65000(10进制)
int msglen = InfAAAMissionConstrant.AAA_MAX_HEAD_LEN + msg.getMsgLen();
int len = 4 - msglen % 4;
msglen += len; //使消息长度为4字节的倍数
//转换为16进制的字符
byte msgLengthBytes[] = numToHexStr(msglen, InfAAAMissionConstrant.AAA_MSG_INFO_LEN).getBytes(); //3.消息头 20字节 版本号(4B)+终端标识(8B)+服务名(8B)
byte msgHeadBytes[] = msgHead(msg.getServiceCode()); //4.会话头 18字节
byte dlgrsvdBytes[] = dlgrsvd(msg.getUserIpAddress(), msg.getWorkOrderId()); //5.事务头 18字节
byte txheadBytes[] = txHead(msg.getUserIpAddress(), msg.getWorkOrderId()); //6.操作信息N*4字节
int operatorLen = (4 - msg.getMsgLen() % 4) + msg.getMsgLen();
String operatorMsg = changeToByteStr(new String(msg.getMsg()), operatorLen);
byte operatorBytes[] = operatorMsg.getBytes(); //7.校验和 8字节
// 校验和=对“消息头+会话头+事务头+操作信息”组成的字符数组按32位分段后异或所得的结果再取反得到的值。
byte checkBytes[] = new byte[msglen];
//消息头 20B
System.arraycopy(msgHeadBytes, 0, checkBytes, 0, InfAAAMissionConstrant.AAA_MSG_HEAD_LEN);
//会话头 18b
System.arraycopy(dlgrsvdBytes, 0, checkBytes, 0 + InfAAAMissionConstrant.AAA_MSG_HEAD_LEN, InfAAAMissionConstrant.AAA_DLG_HEAD_LEN);
//事务头 18B
System.arraycopy(txheadBytes, 0, checkBytes, InfAAAMissionConstrant.AAA_MSG_HEAD_LEN
+ InfAAAMissionConstrant.AAA_DLG_HEAD_LEN, InfAAAMissionConstrant.AAA_TX_HEAD_LEN);
//操作信息
System.arraycopy(operatorBytes, 0, checkBytes, InfAAAMissionConstrant.AAA_MSG_HEAD_LEN
+ InfAAAMissionConstrant.AAA_DLG_HEAD_LEN + InfAAAMissionConstrant.AAA_TX_HEAD_LEN, operatorLen);
// byte checkModBytes[] = checkSum(msglen, checkBytes);
byte checkModBytes[] = createCheckSumString(checkBytes); //组合所有信息=开始标识+消息长度+消息头+会话头+事务头+操作信息+校验和
byte resultBytes[] = new byte[InfAAAMissionConstrant.AAA_MSG_STARTING_LEN + InfAAAMissionConstrant.AAA_MSG_INFO_LEN
+ msglen + InfAAAMissionConstrant.AAA_IIN_CHK_LEN];
// byte resultBytes[] = new byte[InfAAAMissionConstrant.AAA_MSG_STARTING_LEN + InfAAAMissionConstrant.AAA_MSG_INFO_LEN
// + msglen]; //开始标识 4B
System.arraycopy(InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE, 0, resultBytes, 0,
InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE.length);
//消息长度 4B
System.arraycopy(msgLengthBytes, 0, resultBytes, InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE.length,
msgLengthBytes.length);
//消息头 20B 消息头+会话头+事务头+操作信息
System.arraycopy(checkBytes, 0, resultBytes, InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE.length
+ msgLengthBytes.length, checkBytes.length);
// 校验和
System.arraycopy(checkModBytes, 0, resultBytes, InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE.length
+ msgLengthBytes.length + checkBytes.length, checkModBytes.length); return resultBytes;
} @Override
public byte[] fromWire(byte[] input) {
//1.读取MML不包含开始标识和长度字节
byte curBytes[] = input;
byte msgBytes[] = null; try {
//2.解析消息头(20B)=版本号(4B)+终端标识(8B)+服务名(8B)
curBytes = Arrays.copyOfRange(curBytes, InfAAAMissionConstrant.AAA_MSG_VERSION_LEN
+ InfAAAMissionConstrant.AAA_MSG_TERMINAL_LEN + InfAAAMissionConstrant.AAA_SERVICE_CODE_LEN, curBytes.length); //3.解码会话头 会话头(18)=会话ID(8B)+会话控制字(6B)+会话保留字(4B)
curBytes = Arrays.copyOfRange(curBytes, InfAAAMissionConstrant.AAA_DLG_ID_LEN
+ InfAAAMissionConstrant.AAA_DLG_CONTROLLER_LEN + InfAAAMissionConstrant.AAA_DLG_RSVD_LEN_DLGRSVD, curBytes.length); //4.解码事务头 事务头=事务ID(8B)+事务控制字(6B)+事务保留字(4B)
curBytes = Arrays.copyOfRange(curBytes, InfAAAMissionConstrant.AAA_TX_ID_LEN
+ InfAAAMissionConstrant.AAA_TX_CONTROLLER_LEN + InfAAAMissionConstrant.AAA_DLG_RSVD_LEN_DLGRSVD, curBytes.length); //5.解码操作信息
int recMsgLen = input.length - InfAAAMissionConstrant.AAA_MAX_HEAD_LEN - InfAAAMissionConstrant.AAA_IIN_CHK_LEN;
msgBytes = Arrays.copyOfRange(curBytes, 0, recMsgLen); //6.解码校验和 “消息头+会话头+事务头+操作信息”组成的字符数组按32位分段后异或所得的结果再取反得到的值。
curBytes = Arrays.copyOfRange(curBytes, recMsgLen, InfAAAMissionConstrant.AAA_IIN_CHK_LEN);
byte chkBytes[] = Arrays.copyOfRange(input, 0, InfAAAMissionConstrant.AAA_MAX_HEAD_LEN); //计算校验和
byte chkStr[] = createCheckSumString(chkBytes);
//7.判断校验是否通过
if(Arrays.equals(curBytes, chkBytes)) {
throw new Exception("校验不匹配");
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
} //8.返回解码之后的信息
return msgBytes;
} public static byte[] createCkeckSum(byte msg[])
{
int i = 0;
int j = 0;
byte checksum[] = new byte[4];
for (i = 0; i < msg.length / 4; i++)
for (j = 0; j < 4; j++)
checksum[j] ^= msg[i * 4 + j]; for (j = 0; j < msg.length % 4; j++)
checksum[j] ^= msg[i * 4 + j]; for (i = 0; i < 4; i++)
{
int k = ~checksum[i] & 0xff;
checksum[i] = (byte)k;
} return checksum;
} public static byte[] createCheckSumString(byte msg[])
{
byte checksum[] = createCkeckSum(msg);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 4; i++)
{
String s = Integer.toHexString(checksum[i] & 0xff).toUpperCase();
if (s.length() < 2)
sb.append("0").append(s);
else
sb.append(s);
} return sb.toString().getBytes();
} private String numToHexStr(int numCount, int defaultLen) { // StringBuffer sb = new StringBuffer();
//1.10进制转换为16进制,并且是4位的16进制
StringBuffer hexStr = new StringBuffer(Integer.toHexString(numCount));
for(int i = hexStr.length(); i < defaultLen; ++i) {
hexStr.insert(0, '0');
} return hexStr.toString();
} private String changeToByteStr(String str, int defaultLen) {
StringBuffer sourceSb = new StringBuffer(); //判断目标字符串是否满足对应长度要求
int i = 0;
while(i < defaultLen) { if(i < str.length()) {
sourceSb.append(str.charAt(i));
} else {
sourceSb.append(" ");
} ++i;
}
return sourceSb.toString();
} /**
* 消息头=版本号(4B)+终端标识(8B)+服务名(8B)
* 消息头20个字节
*/
private byte[] msgHead(String serviceCode) {
StringBuffer sb = new StringBuffer();
//版本号 4个字节 sb.append(changeToByteStr(InfAAAMissionConstrant.AAA_MSG_VERSION, InfAAAMissionConstrant.AAA_MSG_VERSION_LEN)); //终端标识
sb.append(changeToByteStr(InfAAAMissionConstrant.AAA_MSG_TERMINAL, InfAAAMissionConstrant.AAA_MSG_TERMINAL_LEN)); //服务名,不出什么意外,默认就是C280
sb.append(changeToByteStr(serviceCode, InfAAAMissionConstrant.AAA_SERVICE_CODE_LEN)); return sb.toString().getBytes();
} /**
* 会话头信息
* 会话头=会话ID(8B)+会话控制字(6B)+会话保留字(4B)
*/
private byte[] dlgrsvd(String ip, String workOrderId) {
StringBuffer sb = new StringBuffer();
//我们会话id 工单id
// String plgid = changeToByteStr(workOrderId, InfAAAMissionConstrant.AAA_DLG_ID_LEN);
String plgid = numToHexStr(Integer.valueOf(workOrderId), InfAAAMissionConstrant.AAA_DLG_ID_LEN);
sb.append(plgid);
// 会话控制字包括:DLGLGN,DLGBEG,DLGCON,DLGEND。
// 说明
// 操作员登录MML Server时客户端发DLGLGN,在进行其他操作时,客户端均发DLGCON。
// 操作员退出时,MMLServer给营帐的返回消息中会话控制字为DLGEND,表示会话的结束。
sb.append(changeToByteStr(InfAAAMissionConstrant.AAA_DLG_LGN, InfAAAMissionConstrant.AAA_DLG_CONTROLLER_LEN));
//会话保留字
// 会话保留字与事务保留字共同存储IP地址。
// 说明 长度为4个字节的十六进制字符
// 访问AAA客户端的IP地址,并使用会话保留字和事务保留字传递客户端的IP地址。
// 例如:十进制的IP地址为长度为4个字节的十六进制字符10.10.25.1,转换为十六进制的IP地址为A.A.19.1,前4个字节存储到会话保留字中,后4个字节保留到事务保留字。
// String ip = "127.0.0.1";
String ipNum[] = ip.split("\\.");
StringBuffer ipSb = new StringBuffer(); //获取十进制的IP地址为长度为4个字节的十六进制字符10.10.25.1,转换为十六进制的IP地址为A.A.19.1,前4个字节存储到会话保留字中,后4个字节保留到事务保留字。
//1.循环遍历4个数据,转换为16进制字符串,取前4个字节
for(int i = 0; i < ipNum.length; ++i) {
String hexStr = " ";
if(!ipNum[i].equals("")) {
hexStr = Integer.toHexString(Integer.valueOf(ipNum[i]));
}
for(int j = hexStr.length(); j < 2; ++j) {
hexStr = "0" + hexStr;
}
ipSb.append(hexStr);
}
//删除最后一个点
// ipSb.deleteCharAt(ipSb.length() - 1); sb.append(changeToByteStr(ipSb.substring(0, 4), InfAAAMissionConstrant.AAA_DLG_RSVD_LEN_DLGRSVD)); return sb.toString().getBytes();
} /**
* 事务头=事务ID(8B)+事务控制字(6B)+事务保留字(4B)
* @return
*/
private byte[] txHead(String ip, String workOrderId) {
// 事务ID由客户端产生。如果没有并行的操作,所有的事务ID都可以填1。如果需要使用并行操作,则客户端必须保证当前并行的所有操作中事务ID是不同的。
// 长度为8个字节的整数,用16进制字符表示。
StringBuffer sb = new StringBuffer(); //事务id
String txId = numToHexStr(Integer.valueOf(workOrderId), InfAAAMissionConstrant.AAA_TX_ID_LEN);
sb.append(txId);
// 事务控制字包括:TXBEG,TXCON,TXEND。
// 说明
// 由Provision发起的操作,其事务控制字填写TXBEG。
// 当一条MML命令的消息结束时,MML Server返回给Provision的事务控制字为TXEND,表示一条事务结束。
String txControl = InfAAAMissionConstrant.AAA_TX_CON;
sb.append(changeToByteStr(txControl, InfAAAMissionConstrant.AAA_TX_CONTROLLER_LEN));
//事务保留字(4B)
// 访问AAA客户端的IP地址,并使用会话保留字和事务保留字传递客户端的IP地址。
// AAA访问客户端的IP地址,并使用会话保留字和事务保留字传递客户端的IP地址。 例如:十进制的IP地址为长度为4个字节的十六进制字符10.10.25.1,
// 转换为十六进制的IP地址为A.A.19.1,前4个字节存储到会话保留字中,后4个字节保留到事务保留字。 长度为4个字节的十六进制字符
//1.分解ip,取后置的4个字节
String ipNum[] = ip.split("\\.");
//组装16进制值
StringBuffer hexStr = new StringBuffer();
for(int i = 0; i < ipNum.length; ++i) {
hexStr.append(Integer.toHexString(Integer.valueOf(ipNum[i]))).append(".");
} hexStr = hexStr.deleteCharAt(hexStr.length() - 1);
//取最后4个字节
// String ipResult = hexStr.substring(hexStr.length() - 4, hexStr.length());
//事务头=事务ID(8B)+事务控制字(6B)+事务保留字(4B)
sb.append(changeToByteStr(hexStr.substring(hexStr.length() - 4, hexStr.length()), InfAAAMissionConstrant.AAA_TX_RSVD_LEN_DLGRSVD)); return sb.toString().getBytes();
} /**
* 校验和IIN模式 4字节
* 校验和=对“消息头+会话头+事务头+操作信息”组成的字符数组按32位分段后异或所得的结果再取反得到的值
* @return
*/
private byte[] checkSum(int msgLen, byte msg[]) {
byte res[] = new byte[4]; for(int i = 0; i < msgLen; i+=4) {
res[0] ^= msg[i + 0];
res[1] ^= msg[i + 1];
res[2] ^= msg[i + 2];
res[3] ^= msg[i + 3];
} //最后取反
res[0] = (byte) ~res[0];
res[1] = (byte) ~res[1];
res[2] = (byte) ~res[2];
res[3] = (byte) ~res[3]; String resStr = new String("");
for (int i = 0; i < 4; i++) {
resStr = resStr + byte2hex(res[i]);
} // String resStr = new String(res);
// for (int i = 0; i < 4; i++) {
// resStr = resStr + byte2hex(res[i]);
// } // 将16进制数扩展为对应字符数组(如0xE8--->"E8")
// for(int i = 7; i >= 0; --i) {
// if(i % 2 == 1) {
// //低4位所代表16进制表字符扩展为一个字节
// res[i] = (byte) (res[i / 2] & 0x0F + '0');
// if(res[i] > '9') {
// res[i] = (byte) (res[i] + 'A' - '0' - 10);
// }
// } else {
// ////高4位所代表16进制表字符扩展为一个字节
// res[i] = (byte) (((res[i / 2] >> 4) & 0x0F) + '0');
// if(res[i] > '9') {
// res[i] = (byte) (res[i] + 'A' - '0' - 10);
// }
// }
// } return resStr.getBytes();
} /**
* 将单字节转成16进制
*
* @param b
* @return
*/
private String byte2hex(byte b) {
StringBuffer buf = new StringBuffer();
char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
int high = ((b & 0xf0) >> 4);
int low = (b & 0x0f);
buf.append(hexChars[high]);
buf.append(hexChars[low]);
return buf.toString();
} }

4、创建对应的成帧器,来获取发送每一帧信息

package cn.cutter.ztesoft.HuWeiMML.Template;

import java.io.IOException;
import java.io.OutputStream; /**
* @program:
* @description: 成帧器
* @author: xiaof
* @create: 2018-08-15 10:34
**/
public interface Framer { /**
* 添加成帧信息并将制定消息输出到制定流
* @param message
* @param out
* @throws IOException
*/
void frameMsg(byte message[], OutputStream out) throws IOException; /**
* 扫描指定的流,抽取下一条消息
* @return
* @throws IOException
*/
byte[] nextMsg() throws IOException; }
package cn.cutter.ztesoft.HuWeiMML.Template;

import cn.cutter.ztesoft.HuWeiMML.constrant.InfAAAMissionConstrant;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays; /**
* @program:
* @description: MML成帧器
* @author: xiaof
* @create: 2018-08-15 10:35
**/
public class MMLFramer implements Framer { private static final Log logger = LogFactory.getLog(MMLFramer.class); private static final int MAX_MESSAGE_LENGTH = 65535;
private static final int BYTE_MASK = 0xff;
private static final int SHORT_MASK = 0xffff;
private static final int BYTE_SHIFT = 8; private DataInputStream in; public MMLFramer(InputStream in) {
this.in = new DataInputStream(in);
} @Override
public void frameMsg(byte[] message, OutputStream out) throws IOException {
//1.判断消息是否超长了
if(message.length > MAX_MESSAGE_LENGTH) {
throw new IOException("消息超长了");
} //输出消息
out.write(message);
out.flush();
} @Override
public byte[] nextMsg() throws IOException {
boolean isMsg = false;
int length = 0; try {
//1.读取2个字节,用来获取信息长度信息
byte beginMark[] = new byte[4];
byte msgLength[] = new byte[4];
in.read(beginMark);
//判断是否,IIN开始标识
if(Arrays.equals(beginMark, InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE)) {
isMsg = true;
} else {
return new byte[0];
} //读取消息长度 从消息头到操作信息结束(包括填充的空格)的长度,16进制字符(0-F)表示的16位整数(4B),取值范围为56到65000(10进制)
in.read(msgLength);
length = tranByteToInt(msgLength); } catch (IOException e) {
// e.printStackTrace(); //输出报错信息
logger.error(e.getMessage(), e);
return null;
}
//2.创建相应长度的字节数组
byte msg[] = new byte[length + InfAAAMissionConstrant.AAA_IIN_CHK_LEN];
//3.读取相应数据长度字节进入数组
in.readFully(msg); //这个方法会不断读取数据,直到数组填满,否则阻塞 return msg;
} /**
* 转换消息长度
* @return
*/
private int tranByteToInt(byte byteMsgLength[]) {
//获取数据16进制
String hexStr = new String(byteMsgLength); int result = Integer.parseInt(hexStr, 16); return result;
} }

5、根据模板模式,设计模板类,用来与MML服务器通信

package cn.cutter.ztesoft.HuWeiMML.Template;

import java.io.InputStream;
import java.io.OutputStream; /**
* @program:
* @description: MML操作接口
* @author: xiaof
* @create: 2018-08-15 15:17
**/
public interface MMLOperatorInvoke { void doCammand(InputStream in, OutputStream os); }
package cn.cutter.ztesoft.HuWeiMML.Template;

import cn.cutter.ztesoft.HuWeiMML.vo.MsgInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import java.io.*;
import java.net.Socket; /**
* @program: 湖北移动智慧装维支撑系统
* @description: 华为MML指令IIN类型
* @author: xiaof
* @create: 2018-08-15 10:29
**/
public class AAAMMLIINTemplate { private static final Log logger = LogFactory.getLog(AAAMMLIINTemplate.class); private static final String MML_LOGOUT = "logout:";
private static final String MML_LOGIN_COMMAND = "LOGIN:USER={1},PSWD={2}"; public static void sendMML(String ip, int port, MsgInfo msgInfo, MMLOperatorInvoke mmlOperatorInvoke) { //1.声明相应输入输出对象变量
InputStream is = null;
OutputStream os = null;
BufferedReader br = null; try {
//2.创建socket对象
Socket socket = new Socket(ip, port);
socket.setSoTimeout(10 * 1000); //10s超时
//3.建立相应输入输出流
is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
os = socket.getOutputStream(); //先发送登陆指令
MsgCoder msgCoder = new MMLMsgBinCoder();
String loginMsgCommand = MML_LOGIN_COMMAND.replace("{1}", msgInfo.getUserName())
.replace("{2}", msgInfo.getPassWord());
msgInfo.setMsg(loginMsgCommand.getBytes());
msgInfo.setMsgLen(loginMsgCommand.length());
msgInfo.setServiceCode("C280");
byte loginMsg[] = msgCoder.toWire(msgInfo);
int loginTimes = 0;
boolean isOk = false;
os.write(loginMsg);
os.flush();
byte buf[] = new byte[65500]; logger.info("发送AAA登陆信息:" + new String(loginMsg)); while(loginTimes < 3 && !isOk) {
is.read(buf);
logger.info(new String(buf));
++loginTimes;
if(buf.length > 0)
isOk = true;
} if(isOk)
mmlOperatorInvoke.doCammand(is, os); } catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
try {
//退出登陆logut
MsgCoder msgCoder = new MMLMsgBinCoder();
msgInfo.setMsg(MML_LOGOUT.getBytes());
msgInfo.setMsgLen(MML_LOGOUT.length());
byte logoutMsg[] = msgCoder.toWire(msgInfo);
logger.info("发送AAA登出信息:" + new String(logoutMsg));
os.write(logoutMsg);
os.flush();
if(br != null) {
br.close();
}
if(is != null) {
is.close();
}
if(os != null) {
os.close();
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
} public static void sendMMLHeartBeat(String ip, int port, MsgInfo msgInfo, MMLOperatorInvoke mmlOperatorInvoke) { //1.声明相应输入输出对象变量
InputStream is = null;
OutputStream os = null;
BufferedReader br = null; try {
//2.创建socket对象
Socket socket = new Socket(ip, port);
socket.setSoTimeout(10 * 1000); //10s超时
//3.建立相应输入输出流
is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
os = socket.getOutputStream(); //先发送登陆指令 mmlOperatorInvoke.doCammand(is, os); } catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
try {
//退出登陆logut
if(br != null) {
br.close();
}
if(is != null) {
is.close();
}
if(os != null) {
os.close();
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
} }

6、发送指令操作

直接调用(各个地方的某些字段可能不同,这个参考常量文件设置,还有部分参数在msginfo中设置,其余部分基本不需要修改,直接使用)

AAAMMLIINTemplate.sendMML 方法即可

最后想说一句,这个接口是真的不友好,特别是跟我联调的那哥们都不了解他们自己的服务器,接口文档也不详细,问啥都是不知道,哎,真的是伤,

脑壳疼,希望这里能帮助广大没办法只能调华为的这个鬼MML接口的同行们了。。。

【MML】华为MML AAA接口联调,Java版本的更多相关文章

  1. Java版本APP接口安全设计

    Java版本APP接口安全设计 安全设计分为两种: 1.传输安全. 2. 会话安全. 1.传输安全 怎么保证接口经过网络传输不被抓包获取? 1.如果只是使用对称性算法,破解APP拿到加密密钥就可以解密 ...

  2. 命名和目录接口 JNDI-The Java Naming and Directory Interface

    命名和目录接口 JNDI-The Java Naming and Directory Interface JNDI (The Java Naming and Directory Interface)为 ...

  3. Atitit.操作注册表 树形数据库 注册表的历史 java版本类库总结

    Atitit.操作注册表 树形数据库 注册表的历史 java版本类库总结 1. 注册表是树形数据库 1 2. 注册表的由来 1 3. Java  操作注册表 2 3.1. 使用Preferences  ...

  4. 崔用志-微信开发-java版本

    崔用志-微信开发-java版本 今天看到一些关于微信开发的知识蛮好的博客,分享给大家,希望对大家有帮助. 微信开发准备(一)--Maven仓库管理新建WEB项目 微信开发准备(二)--springmv ...

  5. 前后端分离ueditor富文本编辑器的使用-Java版本

    最近在写一个自己的后台管理系统(主要是写着玩的,用来熟悉后端java的知识,目前只是会简单的写点接口),想在项目中编写一个发布新闻文章的功能,想到了使用百度的ueditor富文本编辑器,网上找了很多j ...

  6. vue菜鸟从业记:公司项目里如何进行前后端接口联调

    最近我的朋友王小闰进入一家新的公司,正好公司项目采用的是前后端分离架构,技术栈是王小闰非常熟悉的vue全家桶,后端用的是Java语言. 在前后端开发人员碰面之后,协商确定好了前端需要的数据接口(扯那么 ...

  7. 31天重构学习笔记(java版本)

    准备下周分享会的内容,无意间看到.net版本的重构31天,花了两个小时看了下,可以看成是Martin Fowler<重构>的精简版 原文地址:http://www.lostechies.c ...

  8. atitit.验证码识别step3----去除边框---- 图像处理类库 attilax总结java版本

    atitit.验证码识别step3----去除边框---- 图像处理类库 attilax总结java版本 1. 去除边框思路原理 1 2. Thumbnailator 是一个用来生成图像缩略图.裁切. ...

  9. eoLinker-AMS开源版JAVA版本正式发布

    eoLinker-AMS开源版JAVA版本正式发布! eoLinker深感广大开发者的支持与厚爱,我们一直在努力为大家提供更多更好的接口服务.截止至2018年4月3日,eoLinker-AMS 开源版 ...

随机推荐

  1. filebeat 收集的进度日志查看

    filebeat 收集的日志进度和文件在data 目录中是有保存的 默认路径地址: /usr/share/filebeat/data 里面有两个文件: meta.json:{"uuid&qu ...

  2. 博客三--tensorflow的队列及线程基本操作

    连接我的开源中国账号:https://my.oschina.net/u/3770644/blog/3036960查询

  3. mysql锁机制(Innodb引擎)

    InnoDB实现了两种类型的行锁. 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同的数据集的排他锁. 排他锁(X):允许获得排他锁的事务更新数据,但是组织其他事务获得相同数据集的共享锁和排他 ...

  4. Sublime text 2/3 [Decode error - output not utf-8] 完美解决方法

    原文链接 http://blog.csdn.net/bbdxf/article/details/25594703 [Decode error - output not utf-8]或者[Decode  ...

  5. mysqlGTID主从配置

    GTID主从简介 GTID是基于mysql生成的事务ID,由服务器ID和事务ID组成. 这个ID在主库及从库上都是唯一的. 这个特性可以让mysql的主从复制变得更加简单,一致性更加可靠. GTID优 ...

  6. String StringBuilder 包装类

    1. String 概述 程序中直接写上双引号的字符串就在字符串常量池中,new的不在池当中 java6之前常量池在方法区,java7以后将字符串常量池放在堆中 因为字符串是对象,应该在堆中 相同的字 ...

  7. pandas 读写 Excel 格式的数据

    import pandas as pd #读入数据: df = pd.read_excel('data_in.xlsx') #导出数据: writer = pd.ExcelWriter('data_o ...

  8. C# ListView应用

    C#  ListView应用 1. 添加表头标题的方法 a. 直接在ListView控件上编写 b. 通过代码编写 //动态添加lstv_ReaderList列表头 /* lstv_ReaderLis ...

  9. jQuery子页面获取父页面元素并绑定事件

    父页面HTML文件: <ul id="faul"> <li class="sonli">子页面列表1</li> <li ...

  10. spring实现一个简单的事务管理

    前两天给公司的数据库操作加了事务管理,今天博客就更一下这个吧. 先说明:本文只是简单得实现一下事务,事务的具体内容,比如事务的等级,事务的具体实现原理等等... 菜鸟水平有限,暂时还更不了这个,以后的 ...