【MML】华为MML AAA接口联调,Java版本
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版本的更多相关文章
- Java版本APP接口安全设计
Java版本APP接口安全设计 安全设计分为两种: 1.传输安全. 2. 会话安全. 1.传输安全 怎么保证接口经过网络传输不被抓包获取? 1.如果只是使用对称性算法,破解APP拿到加密密钥就可以解密 ...
- 命名和目录接口 JNDI-The Java Naming and Directory Interface
命名和目录接口 JNDI-The Java Naming and Directory Interface JNDI (The Java Naming and Directory Interface)为 ...
- Atitit.操作注册表 树形数据库 注册表的历史 java版本类库总结
Atitit.操作注册表 树形数据库 注册表的历史 java版本类库总结 1. 注册表是树形数据库 1 2. 注册表的由来 1 3. Java 操作注册表 2 3.1. 使用Preferences ...
- 崔用志-微信开发-java版本
崔用志-微信开发-java版本 今天看到一些关于微信开发的知识蛮好的博客,分享给大家,希望对大家有帮助. 微信开发准备(一)--Maven仓库管理新建WEB项目 微信开发准备(二)--springmv ...
- 前后端分离ueditor富文本编辑器的使用-Java版本
最近在写一个自己的后台管理系统(主要是写着玩的,用来熟悉后端java的知识,目前只是会简单的写点接口),想在项目中编写一个发布新闻文章的功能,想到了使用百度的ueditor富文本编辑器,网上找了很多j ...
- vue菜鸟从业记:公司项目里如何进行前后端接口联调
最近我的朋友王小闰进入一家新的公司,正好公司项目采用的是前后端分离架构,技术栈是王小闰非常熟悉的vue全家桶,后端用的是Java语言. 在前后端开发人员碰面之后,协商确定好了前端需要的数据接口(扯那么 ...
- 31天重构学习笔记(java版本)
准备下周分享会的内容,无意间看到.net版本的重构31天,花了两个小时看了下,可以看成是Martin Fowler<重构>的精简版 原文地址:http://www.lostechies.c ...
- atitit.验证码识别step3----去除边框---- 图像处理类库 attilax总结java版本
atitit.验证码识别step3----去除边框---- 图像处理类库 attilax总结java版本 1. 去除边框思路原理 1 2. Thumbnailator 是一个用来生成图像缩略图.裁切. ...
- eoLinker-AMS开源版JAVA版本正式发布
eoLinker-AMS开源版JAVA版本正式发布! eoLinker深感广大开发者的支持与厚爱,我们一直在努力为大家提供更多更好的接口服务.截止至2018年4月3日,eoLinker-AMS 开源版 ...
随机推荐
- oracle primary key & foreign key
主键:一个表中只有一个主键约束,但是一个主键约束可以由数据表中的多个列组成:primary key alter table TName add constraints pk_name PRIMARY ...
- Linux中硬链接和软链接的区别
看了这篇文章之后,豁然开朗.直接放链接,感谢作者的分享. https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/#ico ...
- 初识STM32中的USMART组件
今天看了usmart那部分的模块,感觉使我们stm32的学习变更加方便,你可以通过串口查看和检验你所注册过的函数. USMART配步骤1.将USMART包添加到工程中,头文件要包括path2.添加所需 ...
- T-1-java语言基础
一.Linux的由来和发展 Linux是开源的操作系统 Linux是服务器端的操作系统 java主要用于服务器端 二.Linux目录结构(与Windows不同) 文件系统不同:Windows是盘符 ...
- P3805 【模板】manacher算法
#include <bits/stdc++.h> #define up(i,l,r) for(register int i = (l);i <= (r); i++) #define ...
- ORM创建多表以及多表的增删改查
一. 多表的创建 1. 一对一 在哪个表中设置都行,但是添加数据的时候需要现在没有外键的表中添加数据 models.OneToOneField(to="表名",to_field=& ...
- UITextField 输入金额,小数点的控制输入
#pragma mark --- UITextFieldDelegate ---- (BOOL)textField:(UITextField *)textField shouldChangeChara ...
- python装饰器同时支持有参数和无参数的练习题
''' 预备知识: …… @decorator def f(*args,**kwargs): pass # 此处@decorator 等价于 f = decorator(f) @decorator2 ...
- 【CF486E】LIS of Sequence题解
[CF486E]LIS of Sequence题解 题目链接 题意: 给你一个长度为n的序列a1,a2,...,an,你需要把这n个元素分成三类:1,2,3: 1:所有的最长上升子序列都不包含这个元素 ...
- 玩玩微信公众号Java版之七:自定义微信分享
前面已经学会了微信网页授权,现在微信网页的功能也可以开展起来啦! 首先,我们先来学习一下分享,如何在自己的页面获取分享接口及让小伙伴来分享呢? 今天的主人公: 微信 JS-SDK, 对应官方链接为:微 ...