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