zoukankan      html  css  js  c++  java
  • 【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 x1Cx1Dx1Ex1F
        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接口的同行们了。。。

  • 相关阅读:
    python接口自动化测试十三:url编码与解码
    python接口自动化测试十二:对返回的json的简单操作
    python接口自动化测试十一:传参数:data与json
    python接口自动化测试九:重定向相关
    python接口自动化测试十:字典、字符串、json之间的简单处理
    python接口自动化测试八:更新Cookies、session保持会话
    python接口自动化测试七:获取登录的Cookies
    python接口自动化测试六:时间戳,防重复处理
    python接口自动化测试五:乱码、警告、错误处理
    python接口自动化测试四:代码发送HTTPS请求
  • 原文地址:https://www.cnblogs.com/cutter-point/p/9490834.html
Copyright © 2011-2022 走看看