【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接口的同行们了。。。

原文地址:https://www.cnblogs.com/cutter-point/p/9490834.html

时间: 2024-10-12 10:25:45

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

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

Atitit.操作注册表 树形数据库 注册表的历史 java版本类库总结 1. 注册表是树形数据库 1 2. 注册表的由来 1 3. Java  操作注册表 2 3.1. 使用Preferences API (限定访问路径了) 2 3.2. 使用JNI 3 3.3. Jregistrykey 推荐 4 3.4. Jregistry 4 4. org.openqa.selenium.os.WindowsUtils 4 4.1. 以及进程管理 4 1. 注册表是树形数据库 树形数据库,但不提供类似S

JAVA版本微信公众账号开源项目版本发布-jeewx1.0(捷微)

JeeWx, 敏捷微信开发,简称"捷微". 捷微是一款免费开源的微信公众账号开发平台. 平台介绍: 一.简介 jeewx是一个开源,高效,敏捷的微信开发平台采用JAVA语言,它是基于jeecg这个企业级快速开发框架实现的. jeewx的目的是最大化的简化微信开发的流程,使用开发者能把最好的精力放到微信具体业务开发,并能以最快的时间完成.把一些常规而频繁的工作交由jeewx来处理即可,平台兼备的代码生成器,在线开发,可以快速的完成企业应用.为此jeewx提供了详细的二次开发文档,关键代码

微信公众号支付 js api java版本

说起来.微信支付真是一堆坑. 居然官网都没有java版本的完整代码. 就算是php版本的.还都有错误.且前后各种版本.各种文档一大堆....不停的误导开发人员. 花了一天半时间.总算实现了微信公众号支付.和pc端的微信扫码支付.其他不说了.直接给思路 本人做的是微信V3版本的微信支付.微信的官方文档中.提供的demo 只有一些工具类.这些类还是很有作用的. https://mp.weixin.qq.com/paymch/readtemplate?t=mp/business/course3_tmp

kafka----kafka API(java版本)

Apache Kafka包含新的Java客户端,这些新的的客户端将取代现存的Scala客户端,但是为了兼容性,它们仍将存在一段时间.可以通过一些单独的jar包调用这些客户端,这些包的依赖性都比较小,同时老的Scala客户端仍会存在. 一.Producer   API 我们鼓励所有新开发都使用新的java版本producer.这个客户端是经过生产环境测试的,并且一般情况下会比先前的Scala客户端要更快而且具有更多的特性.你可以通过添加对客户端jar包的依赖来调用这个客户端,如下所示,使用mave

使用Java开发OpenCV3程序-3.OpenCV的组件结构以及java版本的数据结构

OpenCV组件结构 关于OpenCV的组件结构“浅墨”大神给出了详细的解释,详细的参照:一览众山小:OpenCV 2.4.8 or OpenCV 2.4.9组件结构全解析 但在OpenCV3.1中还是有些变化的,在opencv/build/include/opencv2目录下有OpenCV的所有模块, 一共有这么多模块,在浅墨的文章中也做了详细的介绍,有些模块已经没有了.然后再看看java中所包含的模块,可以从opencv.jar中看到所实现的模块: 明显看到跟C++版本的少了很多模块,但是基

微信开发资料大全 java版本;教程文档、代码、视频

一.官网 1.1 账号申请 https://mp.weixin.qq.com 1.2 开发者模式 1.1 登录后,选择开发者中心 1.3 测试账号申请 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login 1.4 开发接口文档 http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html 1.5 调试工具接口 http://mp.weixin.qq.

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

atitit.验证码识别step3----去除边框---- 图像处理类库 attilax总结java版本 1. 去除边框思路原理 1 2. Thumbnailator 是一个用来生成图像缩略图.裁切.旋转.添加水印等操作 2 3. OpenCL的Java库 JavaCL 2 4. Java Image Filters是一款基于Java的图像处理类库,特别是在图像滤镜特效方面, 2 4.1.1. 色彩调整 2 4.1.2. 变形和扭曲 5 5. JJIL 是一个Java 的图像处理类库,有超过60

java版本的神经网络——开源框架JOONE实践

由于实验室事情缘故,需要将Python写的神经网络转成Java版本的,但是python中的numpy等啥包也不知道在Java里面对应的是什么工具,所以索性直接寻找一个现成可用的Java神经网络框架,于是就找到了JOONE,JOONE是一个神经网络的开源框架,使用的是BP算法进行迭代计算参数,使用起来比较方便也比较实用,下面介绍一下JOONE的一些使用方法. JOONE需要使用一些外部的依赖包,这在官方网站上有,也可以在这里下载.将所需的包引入工程之后,就可以进行编码实现了. 首先看下完整的程序,

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

eoLinker-AMS开源版JAVA版本正式发布! 深感广大开发者的支持与厚爱,我们一直在努力为大家提供更多更好的接口服务.截止至2018年4月3日,eoLinker-AMS 开源版for PHP已经更新到了3.5.0,平均每周更新一个新版本--出色的优化迭代速度,上佳的功能支持,周到的用户支持服务,一切只为成就万物互联! eoLinker尊重每一种语言的使用者,也深知用户需求的多样性.为此,我们在发布eoLinker-AMS JAVA版的同时,正式启动多语言AMS开源计划!下一步,我们将会制