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

  1. package cn.cutter.ztesoft.HuWeiMML.constrant;
  2.  
  3. /**
  4. * @description: AAA接口常量设置
  5. * @author: xiaof
  6. * @create: 2018-07-26 10:07
  7. **/
  8. public class InfAAAMissionConstrant {
  9.  
  10. /**
  11. *订单号,工单号,宽带账号
  12. */
  13. public static final String AAA_ORDER_ID = "orderId";
  14. public static final String AAA_WORK_ORDER_ID = "workOrderId";
  15. public static final String AAA_ACCOUNT = "accNbr";
  16. public static final String USER_MOBILE = "mobile";
  17.  
  18. /**
  19. * 配置信息
  20. */
  21. // public static final String IOM_IP;
  22.  
  23. public static final String AAA_IP = "IP";
  24. public static final String AAA_PORT = "PORT";
  25. public static final String AAA_USER_NAME = "USERNAME";
  26. public static final String AAA_PASS_WORD = "PASSWORD";
  27. public static final String AAA_CONFIG_TYPE = "AAA_SOCKET_INFO";
  28.  
  29. public static final String SERVICEFLAG = "AAA";
  30.  
  31. //消息头 AAA_MSG_STARTING_INT \x1C\x1D\x1E\x1F
  32. public static final String AAA_MSG_STARTING = "`SC`";
  33.  
  34. public static final int AAA_MSG_STARTING_INT = 0x1C1D1E1F;
  35.  
  36. public static final byte[] AAA_MSG_STARTING_BYTE = {0x1C, 0x1D, 0x1E, 0x1F};
  37.  
  38. /**
  39. * AAA IIN类型格式发送字节消息 0x60, 0x53, 0x43, 0x60
  40. */
  41. public static final byte[] AAA_IIN_MSG_STARTING_BYTE = {0x60, 0x53, 0x43, 0x60};
  42.  
  43. public static final int AAA_MSG_STARTTAG_LEN = 4; //消息开始标识长度
  44.  
  45. public static final int AAA_MSG_COMM_LEN = 12;
  46.  
  47. /**
  48. * 消息头长度
  49. */
  50. public static final int AAA_MSG_HEAD_LEN = 20;
  51.  
  52. /**
  53. * 消息长度部位
  54. */
  55. public static final int AAA_MSG_INFO_LEN = 4;
  56.  
  57. //消息版本号
  58. public static final String AAA_MSG_VERSION = "1.00";
  59.  
  60. public static final Integer AAA_MSG_VERSION_LEN = 4;
  61.  
  62. public static final int AAA_MSG_STARTING_LEN = 4;
  63.  
  64. /**
  65. * 终端标识
  66. */
  67. public static final String AAA_MSG_TERMINAL = "internal";
  68.  
  69. public static final Integer AAA_MSG_TERMINAL_LEN = 8;
  70.  
  71. public static final Integer AAA_SERVICE_CODE_LEN = 8;
  72.  
  73. public static final Integer AAA_MAX_HEAD_LEN = 56;
  74.  
  75. // 会话控制字包括:DLGLGN,DLGBEG,DLGCON,DLGEND。
  76. // 说明
  77. // 操作员登录MML Server时客户端发DLGLGN,在进行其他操作时,客户端均发DLGCON。
  78. // 操作员退出时,MMLServer给营帐的返回消息中会话控制字为DLGEND,表示会话的结束。
  79. /**
  80. * 会话id长度
  81. */
  82. public static final int AAA_DLG_ID_LEN = 8;
  83.  
  84. public static final String AAA_DLG_LGN = "DLGLGN";
  85.  
  86. public static final String AAA_DLG_BEG = "DLGBEG";
  87.  
  88. public static final String AAA_DLG_CON = "DLGCON";
  89.  
  90. public static final String AAA_DLG_END = "DLGEND";
  91.  
  92. /**
  93. * 会话控制字长度
  94. */
  95. public static final int AAA_DLG_CONTROLLER_LEN = 6;
  96.  
  97. /**
  98. * 会话保留字
  99. */
  100. public static final int AAA_DLG_RSVD_LEN_DLGRSVD = 4;
  101. /**
  102. * 会话头长度
  103. */
  104. public static final int AAA_DLG_HEAD_LEN = 18;
  105.  
  106. // 事务控制字包括:TXBEG,TXCON,TXEND。
  107. // 说明
  108. // 由Provision发起的操作,其事务控制字填写TXBEG。
  109. // 当一条MML命令的消息结束时,MML Server返回给Provision的事务控制字为TXEND,表示一条事务结束。
  110.  
  111. /**
  112. * 事务id长度
  113. */
  114. public static final int AAA_TX_ID_LEN = 8;
  115.  
  116. public static final String AAA_TX_BEG = "TXBEG";
  117.  
  118. public static final String AAA_TX_CON = "TXCON";
  119.  
  120. public static final String AAA_TX_END = "TXEND";
  121.  
  122. public static final Integer AAA_TX_CONTROLLER_LEN = 6;
  123.  
  124. /**
  125. * 事务保留字长度
  126. */
  127. public static final int AAA_TX_RSVD_LEN_DLGRSVD = 4;
  128.  
  129. /**
  130. * 事务头长度
  131. */
  132. public static final int AAA_TX_HEAD_LEN = 18;
  133.  
  134. /**
  135. * 校验和长度
  136. */
  137. public static final Integer AAA_CHK_LEN = 4;
  138.  
  139. /**
  140. * IIN校验和长度
  141. */
  142. public static final Integer AAA_IIN_CHK_LEN = 8;
  143.  
  144. /**
  145. * 报文字段
  146. */
  147. /**
  148. * 返回值。十进制整数类型。
  149. * 0表示执行成功,其他返回值的解释请参见DESC字段
  150. */
  151. public static final String RETN = "RETN";
  152.  
  153. /**
  154. * 查询属性名列表,以“&”分隔。
  155. */
  156. public static final String ATTR = "ATTR";
  157.  
  158. /**
  159. * 返回的记录的值,用&分割结果。s
  160. */
  161. public static final String RESULT = "RESULT";
  162.  
  163. }

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

  1. package cn.cutter.ztesoft.HuWeiMML.vo;
  2.  
  3. /**
  4. * @program:
  5. * @description:
  6. * @author: xiaof
  7. * @create: 2018-07-26 15:25
  8. **/
  9. public class MsgInfo {
  10. /**
  11. * 消息和消息长度
  12. */
  13. private byte msg[];
  14. private int msgLen;
  15.  
  16. // 查询用户信息获取 用户IP地址 USERIPADDRESS,USERPORT 用户端口号,业务标识SERVICEFLAG 默认AAA
  17. private String userIpAddress;
  18. private String userPort;
  19. private String serviceFlag;
  20.  
  21. //AAA服务类型,默认C280
  22. private String serviceCode = "C280";
  23.  
  24. //AAA用来做指令标识
  25. private String workOrderId;
  26.  
  27. private String userName;
  28. private String passWord;
  29.  
  30. public byte[] getMsg() {
  31. return msg;
  32. }
  33.  
  34. public void setMsg(byte[] msg) {
  35. this.msg = msg;
  36. }
  37.  
  38. public int getMsgLen() {
  39. return msgLen;
  40. }
  41.  
  42. public void setMsgLen(int msgLen) {
  43. this.msgLen = msgLen;
  44. }
  45.  
  46. public String getUserIpAddress() {
  47. return userIpAddress;
  48. }
  49.  
  50. public void setUserIpAddress(String userIpAddress) {
  51. this.userIpAddress = userIpAddress;
  52. }
  53.  
  54. public String getUserPort() {
  55. return userPort;
  56. }
  57.  
  58. public void setUserPort(String userPort) {
  59. this.userPort = userPort;
  60. }
  61.  
  62. public String getServiceFlag() {
  63. return serviceFlag;
  64. }
  65.  
  66. public void setServiceFlag(String serviceFlag) {
  67. this.serviceFlag = serviceFlag;
  68. }
  69.  
  70. public String getServiceCode() {
  71. return serviceCode;
  72. }
  73.  
  74. public void setServiceCode(String serviceCode) {
  75. this.serviceCode = serviceCode;
  76. }
  77.  
  78. public String getWorkOrderId() {
  79. return workOrderId;
  80. }
  81.  
  82. public void setWorkOrderId(String workOrderId) {
  83. this.workOrderId = workOrderId;
  84. }
  85.  
  86. public String getUserName() {
  87. return userName;
  88. }
  89.  
  90. public void setUserName(String userName) {
  91. this.userName = userName;
  92. }
  93.  
  94. public String getPassWord() {
  95. return passWord;
  96. }
  97.  
  98. public void setPassWord(String passWord) {
  99. this.passWord = passWord;
  100. }
  101. }

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

  1. package cn.cutter.ztesoft.HuWeiMML.Template;
  2.  
  3. import cn.cutter.ztesoft.HuWeiMML.vo.MsgInfo;
  4.  
  5. import java.io.IOException;
  6.  
  7. /**
  8. * @program:
  9. * @description: MML消息解码,编码器
  10. * @author: xiaof
  11. * @create: 2018-08-15 11:38
  12. **/
  13. public interface MsgCoder {
  14.  
  15. /**
  16. * 指令编码
  17. * @param msg
  18. * @return
  19. * @throws IOException
  20. */
  21. byte[] toWire(MsgInfo msg) throws IOException;
  22.  
  23. /**
  24. * 指令解码
  25. * @param input
  26. * @return
  27. * @throws IOException
  28. */
  29. byte[] fromWire(byte input[]) throws IOException;
  30. }
  1. package cn.cutter.ztesoft.HuWeiMML.Template;
  2.  
  3. import cn.cutter.ztesoft.HuWeiMML.constrant.InfAAAMissionConstrant;
  4. import cn.cutter.ztesoft.HuWeiMML.vo.MsgInfo;
  5. import org.apache.commons.logging.Log;
  6. import org.apache.commons.logging.LogFactory;
  7.  
  8. import java.util.Arrays;
  9.  
  10. /**
  11. *
  12. *
  13. * 1 1 1 1 1 1
  14. * 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
  15. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  16. * | 0x60, 0x53, 0x43, 0x60 |
  17. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  18. * | msg length 4B |
  19. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  20. * | |
  21. * | msg head 20B |
  22. * | |
  23. * | |
  24. * | |
  25. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  26. * | |
  27. * | Conversation head (18B) |
  28. * | |
  29. * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  30. * | | |
  31. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
  32. * | |
  33. * | transaction head (18) |
  34. * | |
  35. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  36. * | |
  37. * ~ operator msg(N * 4B) ~
  38. * | |
  39. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  40. * | |
  41. * | check(8B) |
  42. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  43. *
  44. * @program:
  45. * @description: MML指令编码解析
  46. * @author: xiaof
  47. * @create: 2018-08-15 11:40
  48. **/
  49. public class MMLMsgBinCoder implements MsgCoder {
  50.  
  51. private static final Log logger = LogFactory.getLog(MMLMsgBinCoder.class);
  52.  
  53. @Override
  54. public byte[] toWire(MsgInfo msg) {
  55.  
  56. //1.输出消息开始标识 4字节
  57. byte beginMarkBytes[] = InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE;
  58.  
  59. //2.输出消息的长度4字节 从消息头到操作信息结束(包括填充的空格)的长度,16进制字符(0-F)表示的16位整数(4B),取值范围为56到65000(10进制)
  60. int msglen = InfAAAMissionConstrant.AAA_MAX_HEAD_LEN + msg.getMsgLen();
  61. int len = 4 - msglen % 4;
  62. msglen += len; //使消息长度为4字节的倍数
  63. //转换为16进制的字符
  64. byte msgLengthBytes[] = numToHexStr(msglen, InfAAAMissionConstrant.AAA_MSG_INFO_LEN).getBytes();
  65.  
  66. //3.消息头 20字节 版本号(4B)+终端标识(8B)+服务名(8B)
  67. byte msgHeadBytes[] = msgHead(msg.getServiceCode());
  68.  
  69. //4.会话头 18字节
  70. byte dlgrsvdBytes[] = dlgrsvd(msg.getUserIpAddress(), msg.getWorkOrderId());
  71.  
  72. //5.事务头 18字节
  73. byte txheadBytes[] = txHead(msg.getUserIpAddress(), msg.getWorkOrderId());
  74.  
  75. //6.操作信息N*4字节
  76. int operatorLen = (4 - msg.getMsgLen() % 4) + msg.getMsgLen();
  77. String operatorMsg = changeToByteStr(new String(msg.getMsg()), operatorLen);
  78. byte operatorBytes[] = operatorMsg.getBytes();
  79.  
  80. //7.校验和 8字节
  81. // 校验和=对“消息头+会话头+事务头+操作信息”组成的字符数组按32位分段后异或所得的结果再取反得到的值。
  82. byte checkBytes[] = new byte[msglen];
  83. //消息头 20B
  84. System.arraycopy(msgHeadBytes, 0, checkBytes, 0, InfAAAMissionConstrant.AAA_MSG_HEAD_LEN);
  85. //会话头 18b
  86. System.arraycopy(dlgrsvdBytes, 0, checkBytes, 0 + InfAAAMissionConstrant.AAA_MSG_HEAD_LEN, InfAAAMissionConstrant.AAA_DLG_HEAD_LEN);
  87. //事务头 18B
  88. System.arraycopy(txheadBytes, 0, checkBytes, InfAAAMissionConstrant.AAA_MSG_HEAD_LEN
  89. + InfAAAMissionConstrant.AAA_DLG_HEAD_LEN, InfAAAMissionConstrant.AAA_TX_HEAD_LEN);
  90. //操作信息
  91. System.arraycopy(operatorBytes, 0, checkBytes, InfAAAMissionConstrant.AAA_MSG_HEAD_LEN
  92. + InfAAAMissionConstrant.AAA_DLG_HEAD_LEN + InfAAAMissionConstrant.AAA_TX_HEAD_LEN, operatorLen);
  93. // byte checkModBytes[] = checkSum(msglen, checkBytes);
  94. byte checkModBytes[] = createCheckSumString(checkBytes);
  95.  
  96. //组合所有信息=开始标识+消息长度+消息头+会话头+事务头+操作信息+校验和
  97. byte resultBytes[] = new byte[InfAAAMissionConstrant.AAA_MSG_STARTING_LEN + InfAAAMissionConstrant.AAA_MSG_INFO_LEN
  98. + msglen + InfAAAMissionConstrant.AAA_IIN_CHK_LEN];
  99. // byte resultBytes[] = new byte[InfAAAMissionConstrant.AAA_MSG_STARTING_LEN + InfAAAMissionConstrant.AAA_MSG_INFO_LEN
  100. // + msglen];
  101.  
  102. //开始标识 4B
  103. System.arraycopy(InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE, 0, resultBytes, 0,
  104. InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE.length);
  105. //消息长度 4B
  106. System.arraycopy(msgLengthBytes, 0, resultBytes, InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE.length,
  107. msgLengthBytes.length);
  108. //消息头 20B 消息头+会话头+事务头+操作信息
  109. System.arraycopy(checkBytes, 0, resultBytes, InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE.length
  110. + msgLengthBytes.length, checkBytes.length);
  111. // 校验和
  112. System.arraycopy(checkModBytes, 0, resultBytes, InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE.length
  113. + msgLengthBytes.length + checkBytes.length, checkModBytes.length);
  114.  
  115. return resultBytes;
  116. }
  117.  
  118. @Override
  119. public byte[] fromWire(byte[] input) {
  120. //1.读取MML不包含开始标识和长度字节
  121. byte curBytes[] = input;
  122. byte msgBytes[] = null;
  123.  
  124. try {
  125. //2.解析消息头(20B)=版本号(4B)+终端标识(8B)+服务名(8B)
  126. curBytes = Arrays.copyOfRange(curBytes, InfAAAMissionConstrant.AAA_MSG_VERSION_LEN
  127. + InfAAAMissionConstrant.AAA_MSG_TERMINAL_LEN + InfAAAMissionConstrant.AAA_SERVICE_CODE_LEN, curBytes.length);
  128.  
  129. //3.解码会话头 会话头(18)=会话ID(8B)+会话控制字(6B)+会话保留字(4B)
  130. curBytes = Arrays.copyOfRange(curBytes, InfAAAMissionConstrant.AAA_DLG_ID_LEN
  131. + InfAAAMissionConstrant.AAA_DLG_CONTROLLER_LEN + InfAAAMissionConstrant.AAA_DLG_RSVD_LEN_DLGRSVD, curBytes.length);
  132.  
  133. //4.解码事务头 事务头=事务ID(8B)+事务控制字(6B)+事务保留字(4B)
  134. curBytes = Arrays.copyOfRange(curBytes, InfAAAMissionConstrant.AAA_TX_ID_LEN
  135. + InfAAAMissionConstrant.AAA_TX_CONTROLLER_LEN + InfAAAMissionConstrant.AAA_DLG_RSVD_LEN_DLGRSVD, curBytes.length);
  136.  
  137. //5.解码操作信息
  138. int recMsgLen = input.length - InfAAAMissionConstrant.AAA_MAX_HEAD_LEN - InfAAAMissionConstrant.AAA_IIN_CHK_LEN;
  139. msgBytes = Arrays.copyOfRange(curBytes, 0, recMsgLen);
  140.  
  141. //6.解码校验和 “消息头+会话头+事务头+操作信息”组成的字符数组按32位分段后异或所得的结果再取反得到的值。
  142. curBytes = Arrays.copyOfRange(curBytes, recMsgLen, InfAAAMissionConstrant.AAA_IIN_CHK_LEN);
  143. byte chkBytes[] = Arrays.copyOfRange(input, 0, InfAAAMissionConstrant.AAA_MAX_HEAD_LEN);
  144.  
  145. //计算校验和
  146. byte chkStr[] = createCheckSumString(chkBytes);
  147. //7.判断校验是否通过
  148. if(Arrays.equals(curBytes, chkBytes)) {
  149. throw new Exception("校验不匹配");
  150. }
  151. } catch (Exception e) {
  152. logger.error(e.getMessage(), e);
  153. }
  154.  
  155. //8.返回解码之后的信息
  156. return msgBytes;
  157. }
  158.  
  159. public static byte[] createCkeckSum(byte msg[])
  160. {
  161. int i = 0;
  162. int j = 0;
  163. byte checksum[] = new byte[4];
  164. for (i = 0; i < msg.length / 4; i++)
  165. for (j = 0; j < 4; j++)
  166. checksum[j] ^= msg[i * 4 + j];
  167.  
  168. for (j = 0; j < msg.length % 4; j++)
  169. checksum[j] ^= msg[i * 4 + j];
  170.  
  171. for (i = 0; i < 4; i++)
  172. {
  173. int k = ~checksum[i] & 0xff;
  174. checksum[i] = (byte)k;
  175. }
  176.  
  177. return checksum;
  178. }
  179.  
  180. public static byte[] createCheckSumString(byte msg[])
  181. {
  182. byte checksum[] = createCkeckSum(msg);
  183. StringBuffer sb = new StringBuffer();
  184. for (int i = 0; i < 4; i++)
  185. {
  186. String s = Integer.toHexString(checksum[i] & 0xff).toUpperCase();
  187. if (s.length() < 2)
  188. sb.append("0").append(s);
  189. else
  190. sb.append(s);
  191. }
  192.  
  193. return sb.toString().getBytes();
  194. }
  195.  
  196. private String numToHexStr(int numCount, int defaultLen) {
  197.  
  198. // StringBuffer sb = new StringBuffer();
  199. //1.10进制转换为16进制,并且是4位的16进制
  200. StringBuffer hexStr = new StringBuffer(Integer.toHexString(numCount));
  201. for(int i = hexStr.length(); i < defaultLen; ++i) {
  202. hexStr.insert(0, '0');
  203. }
  204.  
  205. return hexStr.toString();
  206. }
  207.  
  208. private String changeToByteStr(String str, int defaultLen) {
  209. StringBuffer sourceSb = new StringBuffer();
  210.  
  211. //判断目标字符串是否满足对应长度要求
  212. int i = 0;
  213. while(i < defaultLen) {
  214.  
  215. if(i < str.length()) {
  216. sourceSb.append(str.charAt(i));
  217. } else {
  218. sourceSb.append(" ");
  219. }
  220.  
  221. ++i;
  222. }
  223. return sourceSb.toString();
  224. }
  225.  
  226. /**
  227. * 消息头=版本号(4B)+终端标识(8B)+服务名(8B)
  228. * 消息头20个字节
  229. */
  230. private byte[] msgHead(String serviceCode) {
  231. StringBuffer sb = new StringBuffer();
  232. //版本号 4个字节
  233.  
  234. sb.append(changeToByteStr(InfAAAMissionConstrant.AAA_MSG_VERSION, InfAAAMissionConstrant.AAA_MSG_VERSION_LEN));
  235.  
  236. //终端标识
  237. sb.append(changeToByteStr(InfAAAMissionConstrant.AAA_MSG_TERMINAL, InfAAAMissionConstrant.AAA_MSG_TERMINAL_LEN));
  238.  
  239. //服务名,不出什么意外,默认就是C280
  240. sb.append(changeToByteStr(serviceCode, InfAAAMissionConstrant.AAA_SERVICE_CODE_LEN));
  241.  
  242. return sb.toString().getBytes();
  243. }
  244.  
  245. /**
  246. * 会话头信息
  247. * 会话头=会话ID(8B)+会话控制字(6B)+会话保留字(4B)
  248. */
  249. private byte[] dlgrsvd(String ip, String workOrderId) {
  250. StringBuffer sb = new StringBuffer();
  251. //我们会话id 工单id
  252. // String plgid = changeToByteStr(workOrderId, InfAAAMissionConstrant.AAA_DLG_ID_LEN);
  253. String plgid = numToHexStr(Integer.valueOf(workOrderId), InfAAAMissionConstrant.AAA_DLG_ID_LEN);
  254. sb.append(plgid);
  255. // 会话控制字包括:DLGLGN,DLGBEG,DLGCON,DLGEND。
  256. // 说明
  257. // 操作员登录MML Server时客户端发DLGLGN,在进行其他操作时,客户端均发DLGCON。
  258. // 操作员退出时,MMLServer给营帐的返回消息中会话控制字为DLGEND,表示会话的结束。
  259. sb.append(changeToByteStr(InfAAAMissionConstrant.AAA_DLG_LGN, InfAAAMissionConstrant.AAA_DLG_CONTROLLER_LEN));
  260. //会话保留字
  261. // 会话保留字与事务保留字共同存储IP地址。
  262. // 说明 长度为4个字节的十六进制字符
  263. // 访问AAA客户端的IP地址,并使用会话保留字和事务保留字传递客户端的IP地址。
  264. // 例如:十进制的IP地址为长度为4个字节的十六进制字符10.10.25.1,转换为十六进制的IP地址为A.A.19.1,前4个字节存储到会话保留字中,后4个字节保留到事务保留字。
  265. // String ip = "127.0.0.1";
  266. String ipNum[] = ip.split("\\.");
  267. StringBuffer ipSb = new StringBuffer();
  268.  
  269. //获取十进制的IP地址为长度为4个字节的十六进制字符10.10.25.1,转换为十六进制的IP地址为A.A.19.1,前4个字节存储到会话保留字中,后4个字节保留到事务保留字。
  270. //1.循环遍历4个数据,转换为16进制字符串,取前4个字节
  271. for(int i = 0; i < ipNum.length; ++i) {
  272. String hexStr = " ";
  273. if(!ipNum[i].equals("")) {
  274. hexStr = Integer.toHexString(Integer.valueOf(ipNum[i]));
  275. }
  276. for(int j = hexStr.length(); j < 2; ++j) {
  277. hexStr = "0" + hexStr;
  278. }
  279. ipSb.append(hexStr);
  280. }
  281. //删除最后一个点
  282. // ipSb.deleteCharAt(ipSb.length() - 1);
  283.  
  284. sb.append(changeToByteStr(ipSb.substring(0, 4), InfAAAMissionConstrant.AAA_DLG_RSVD_LEN_DLGRSVD));
  285.  
  286. return sb.toString().getBytes();
  287. }
  288.  
  289. /**
  290. * 事务头=事务ID(8B)+事务控制字(6B)+事务保留字(4B)
  291. * @return
  292. */
  293. private byte[] txHead(String ip, String workOrderId) {
  294. // 事务ID由客户端产生。如果没有并行的操作,所有的事务ID都可以填1。如果需要使用并行操作,则客户端必须保证当前并行的所有操作中事务ID是不同的。
  295. // 长度为8个字节的整数,用16进制字符表示。
  296. StringBuffer sb = new StringBuffer();
  297.  
  298. //事务id
  299. String txId = numToHexStr(Integer.valueOf(workOrderId), InfAAAMissionConstrant.AAA_TX_ID_LEN);
  300. sb.append(txId);
  301. // 事务控制字包括:TXBEG,TXCON,TXEND。
  302. // 说明
  303. // 由Provision发起的操作,其事务控制字填写TXBEG。
  304. // 当一条MML命令的消息结束时,MML Server返回给Provision的事务控制字为TXEND,表示一条事务结束。
  305. String txControl = InfAAAMissionConstrant.AAA_TX_CON;
  306. sb.append(changeToByteStr(txControl, InfAAAMissionConstrant.AAA_TX_CONTROLLER_LEN));
  307. //事务保留字(4B)
  308. // 访问AAA客户端的IP地址,并使用会话保留字和事务保留字传递客户端的IP地址。
  309. // AAA访问客户端的IP地址,并使用会话保留字和事务保留字传递客户端的IP地址。 例如:十进制的IP地址为长度为4个字节的十六进制字符10.10.25.1,
  310. // 转换为十六进制的IP地址为A.A.19.1,前4个字节存储到会话保留字中,后4个字节保留到事务保留字。 长度为4个字节的十六进制字符
  311. //1.分解ip,取后置的4个字节
  312. String ipNum[] = ip.split("\\.");
  313. //组装16进制值
  314. StringBuffer hexStr = new StringBuffer();
  315. for(int i = 0; i < ipNum.length; ++i) {
  316. hexStr.append(Integer.toHexString(Integer.valueOf(ipNum[i]))).append(".");
  317. }
  318.  
  319. hexStr = hexStr.deleteCharAt(hexStr.length() - 1);
  320. //取最后4个字节
  321. // String ipResult = hexStr.substring(hexStr.length() - 4, hexStr.length());
  322. //事务头=事务ID(8B)+事务控制字(6B)+事务保留字(4B)
  323. sb.append(changeToByteStr(hexStr.substring(hexStr.length() - 4, hexStr.length()), InfAAAMissionConstrant.AAA_TX_RSVD_LEN_DLGRSVD));
  324.  
  325. return sb.toString().getBytes();
  326. }
  327.  
  328. /**
  329. * 校验和IIN模式 4字节
  330. * 校验和=对“消息头+会话头+事务头+操作信息”组成的字符数组按32位分段后异或所得的结果再取反得到的值
  331. * @return
  332. */
  333. private byte[] checkSum(int msgLen, byte msg[]) {
  334. byte res[] = new byte[4];
  335.  
  336. for(int i = 0; i < msgLen; i+=4) {
  337. res[0] ^= msg[i + 0];
  338. res[1] ^= msg[i + 1];
  339. res[2] ^= msg[i + 2];
  340. res[3] ^= msg[i + 3];
  341. }
  342.  
  343. //最后取反
  344. res[0] = (byte) ~res[0];
  345. res[1] = (byte) ~res[1];
  346. res[2] = (byte) ~res[2];
  347. res[3] = (byte) ~res[3];
  348.  
  349. String resStr = new String("");
  350. for (int i = 0; i < 4; i++) {
  351. resStr = resStr + byte2hex(res[i]);
  352. }
  353.  
  354. // String resStr = new String(res);
  355. // for (int i = 0; i < 4; i++) {
  356. // resStr = resStr + byte2hex(res[i]);
  357. // }
  358.  
  359. // 将16进制数扩展为对应字符数组(如0xE8--->"E8")
  360. // for(int i = 7; i >= 0; --i) {
  361. // if(i % 2 == 1) {
  362. // //低4位所代表16进制表字符扩展为一个字节
  363. // res[i] = (byte) (res[i / 2] & 0x0F + '0');
  364. // if(res[i] > '9') {
  365. // res[i] = (byte) (res[i] + 'A' - '0' - 10);
  366. // }
  367. // } else {
  368. // ////高4位所代表16进制表字符扩展为一个字节
  369. // res[i] = (byte) (((res[i / 2] >> 4) & 0x0F) + '0');
  370. // if(res[i] > '9') {
  371. // res[i] = (byte) (res[i] + 'A' - '0' - 10);
  372. // }
  373. // }
  374. // }
  375.  
  376. return resStr.getBytes();
  377. }
  378.  
  379. /**
  380. * 将单字节转成16进制
  381. *
  382. * @param b
  383. * @return
  384. */
  385. private String byte2hex(byte b) {
  386. StringBuffer buf = new StringBuffer();
  387. char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  388. int high = ((b & 0xf0) >> 4);
  389. int low = (b & 0x0f);
  390. buf.append(hexChars[high]);
  391. buf.append(hexChars[low]);
  392. return buf.toString();
  393. }
  394.  
  395. }

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

  1. package cn.cutter.ztesoft.HuWeiMML.Template;
  2.  
  3. import java.io.IOException;
  4. import java.io.OutputStream;
  5.  
  6. /**
  7. * @program:
  8. * @description: 成帧器
  9. * @author: xiaof
  10. * @create: 2018-08-15 10:34
  11. **/
  12. public interface Framer {
  13.  
  14. /**
  15. * 添加成帧信息并将制定消息输出到制定流
  16. * @param message
  17. * @param out
  18. * @throws IOException
  19. */
  20. void frameMsg(byte message[], OutputStream out) throws IOException;
  21.  
  22. /**
  23. * 扫描指定的流,抽取下一条消息
  24. * @return
  25. * @throws IOException
  26. */
  27. byte[] nextMsg() throws IOException;
  28.  
  29. }
  1. package cn.cutter.ztesoft.HuWeiMML.Template;
  2.  
  3. import cn.cutter.ztesoft.HuWeiMML.constrant.InfAAAMissionConstrant;
  4. import org.apache.commons.logging.Log;
  5. import org.apache.commons.logging.LogFactory;
  6.  
  7. import java.io.DataInputStream;
  8. import java.io.IOException;
  9. import java.io.InputStream;
  10. import java.io.OutputStream;
  11. import java.util.Arrays;
  12.  
  13. /**
  14. * @program:
  15. * @description: MML成帧器
  16. * @author: xiaof
  17. * @create: 2018-08-15 10:35
  18. **/
  19. public class MMLFramer implements Framer {
  20.  
  21. private static final Log logger = LogFactory.getLog(MMLFramer.class);
  22.  
  23. private static final int MAX_MESSAGE_LENGTH = 65535;
  24. private static final int BYTE_MASK = 0xff;
  25. private static final int SHORT_MASK = 0xffff;
  26. private static final int BYTE_SHIFT = 8;
  27.  
  28. private DataInputStream in;
  29.  
  30. public MMLFramer(InputStream in) {
  31. this.in = new DataInputStream(in);
  32. }
  33.  
  34. @Override
  35. public void frameMsg(byte[] message, OutputStream out) throws IOException {
  36. //1.判断消息是否超长了
  37. if(message.length > MAX_MESSAGE_LENGTH) {
  38. throw new IOException("消息超长了");
  39. }
  40.  
  41. //输出消息
  42. out.write(message);
  43. out.flush();
  44. }
  45.  
  46. @Override
  47. public byte[] nextMsg() throws IOException {
  48. boolean isMsg = false;
  49. int length = 0;
  50.  
  51. try {
  52. //1.读取2个字节,用来获取信息长度信息
  53. byte beginMark[] = new byte[4];
  54. byte msgLength[] = new byte[4];
  55. in.read(beginMark);
  56. //判断是否,IIN开始标识
  57. if(Arrays.equals(beginMark, InfAAAMissionConstrant.AAA_IIN_MSG_STARTING_BYTE)) {
  58. isMsg = true;
  59. } else {
  60. return new byte[0];
  61. }
  62.  
  63. //读取消息长度 从消息头到操作信息结束(包括填充的空格)的长度,16进制字符(0-F)表示的16位整数(4B),取值范围为56到65000(10进制)
  64. in.read(msgLength);
  65. length = tranByteToInt(msgLength);
  66.  
  67. } catch (IOException e) {
  68. // e.printStackTrace(); //输出报错信息
  69. logger.error(e.getMessage(), e);
  70. return null;
  71. }
  72. //2.创建相应长度的字节数组
  73. byte msg[] = new byte[length + InfAAAMissionConstrant.AAA_IIN_CHK_LEN];
  74. //3.读取相应数据长度字节进入数组
  75. in.readFully(msg); //这个方法会不断读取数据,直到数组填满,否则阻塞
  76.  
  77. return msg;
  78. }
  79.  
  80. /**
  81. * 转换消息长度
  82. * @return
  83. */
  84. private int tranByteToInt(byte byteMsgLength[]) {
  85. //获取数据16进制
  86. String hexStr = new String(byteMsgLength);
  87.  
  88. int result = Integer.parseInt(hexStr, 16);
  89.  
  90. return result;
  91. }
  92.  
  93. }

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

  1. package cn.cutter.ztesoft.HuWeiMML.Template;
  2.  
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5.  
  6. /**
  7. * @program:
  8. * @description: MML操作接口
  9. * @author: xiaof
  10. * @create: 2018-08-15 15:17
  11. **/
  12. public interface MMLOperatorInvoke {
  13.  
  14. void doCammand(InputStream in, OutputStream os);
  15.  
  16. }
  1. package cn.cutter.ztesoft.HuWeiMML.Template;
  2.  
  3. import cn.cutter.ztesoft.HuWeiMML.vo.MsgInfo;
  4. import org.apache.commons.logging.Log;
  5. import org.apache.commons.logging.LogFactory;
  6.  
  7. import java.io.*;
  8. import java.net.Socket;
  9.  
  10. /**
  11. * @program: 湖北移动智慧装维支撑系统
  12. * @description: 华为MML指令IIN类型
  13. * @author: xiaof
  14. * @create: 2018-08-15 10:29
  15. **/
  16. public class AAAMMLIINTemplate {
  17.  
  18. private static final Log logger = LogFactory.getLog(AAAMMLIINTemplate.class);
  19.  
  20. private static final String MML_LOGOUT = "logout:";
  21. private static final String MML_LOGIN_COMMAND = "LOGIN:USER={1},PSWD={2}";
  22.  
  23. public static void sendMML(String ip, int port, MsgInfo msgInfo, MMLOperatorInvoke mmlOperatorInvoke) {
  24.  
  25. //1.声明相应输入输出对象变量
  26. InputStream is = null;
  27. OutputStream os = null;
  28. BufferedReader br = null;
  29.  
  30. try {
  31. //2.创建socket对象
  32. Socket socket = new Socket(ip, port);
  33. socket.setSoTimeout(10 * 1000); //10s超时
  34. //3.建立相应输入输出流
  35. is = socket.getInputStream();
  36. br = new BufferedReader(new InputStreamReader(is));
  37. os = socket.getOutputStream();
  38.  
  39. //先发送登陆指令
  40. MsgCoder msgCoder = new MMLMsgBinCoder();
  41. String loginMsgCommand = MML_LOGIN_COMMAND.replace("{1}", msgInfo.getUserName())
  42. .replace("{2}", msgInfo.getPassWord());
  43. msgInfo.setMsg(loginMsgCommand.getBytes());
  44. msgInfo.setMsgLen(loginMsgCommand.length());
  45. msgInfo.setServiceCode("C280");
  46. byte loginMsg[] = msgCoder.toWire(msgInfo);
  47. int loginTimes = 0;
  48. boolean isOk = false;
  49. os.write(loginMsg);
  50. os.flush();
  51. byte buf[] = new byte[65500];
  52.  
  53. logger.info("发送AAA登陆信息:" + new String(loginMsg));
  54.  
  55. while(loginTimes < 3 && !isOk) {
  56. is.read(buf);
  57. logger.info(new String(buf));
  58. ++loginTimes;
  59. if(buf.length > 0)
  60. isOk = true;
  61. }
  62.  
  63. if(isOk)
  64. mmlOperatorInvoke.doCammand(is, os);
  65.  
  66. } catch (Exception e) {
  67. logger.error(e.getMessage(), e);
  68. } finally {
  69. try {
  70. //退出登陆logut
  71. MsgCoder msgCoder = new MMLMsgBinCoder();
  72. msgInfo.setMsg(MML_LOGOUT.getBytes());
  73. msgInfo.setMsgLen(MML_LOGOUT.length());
  74. byte logoutMsg[] = msgCoder.toWire(msgInfo);
  75. logger.info("发送AAA登出信息:" + new String(logoutMsg));
  76. os.write(logoutMsg);
  77. os.flush();
  78. if(br != null) {
  79. br.close();
  80. }
  81. if(is != null) {
  82. is.close();
  83. }
  84. if(os != null) {
  85. os.close();
  86. }
  87. } catch (IOException e) {
  88. logger.error(e.getMessage(), e);
  89. }
  90. }
  91. }
  92.  
  93. public static void sendMMLHeartBeat(String ip, int port, MsgInfo msgInfo, MMLOperatorInvoke mmlOperatorInvoke) {
  94.  
  95. //1.声明相应输入输出对象变量
  96. InputStream is = null;
  97. OutputStream os = null;
  98. BufferedReader br = null;
  99.  
  100. try {
  101. //2.创建socket对象
  102. Socket socket = new Socket(ip, port);
  103. socket.setSoTimeout(10 * 1000); //10s超时
  104. //3.建立相应输入输出流
  105. is = socket.getInputStream();
  106. br = new BufferedReader(new InputStreamReader(is));
  107. os = socket.getOutputStream();
  108.  
  109. //先发送登陆指令
  110.  
  111. mmlOperatorInvoke.doCammand(is, os);
  112.  
  113. } catch (Exception e) {
  114. logger.error(e.getMessage(), e);
  115. } finally {
  116. try {
  117. //退出登陆logut
  118. if(br != null) {
  119. br.close();
  120. }
  121. if(is != null) {
  122. is.close();
  123. }
  124. if(os != null) {
  125. os.close();
  126. }
  127. } catch (IOException e) {
  128. logger.error(e.getMessage(), e);
  129. }
  130. }
  131. }
  132.  
  133. }

6、发送指令操作

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

  1. AAAMMLIINTemplate.sendMML 方法即可

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. oracle primary key & foreign key

    主键:一个表中只有一个主键约束,但是一个主键约束可以由数据表中的多个列组成:primary key alter table TName add constraints pk_name PRIMARY ...

  2. Linux中硬链接和软链接的区别

    看了这篇文章之后,豁然开朗.直接放链接,感谢作者的分享. https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/#ico ...

  3. 初识STM32中的USMART组件

    今天看了usmart那部分的模块,感觉使我们stm32的学习变更加方便,你可以通过串口查看和检验你所注册过的函数. USMART配步骤1.将USMART包添加到工程中,头文件要包括path2.添加所需 ...

  4. T-1-java语言基础

    一.Linux的由来和发展 Linux是开源的操作系统 Linux是服务器端的操作系统 java主要用于服务器端   二.Linux目录结构(与Windows不同) 文件系统不同:Windows是盘符 ...

  5. P3805 【模板】manacher算法

    #include <bits/stdc++.h> #define up(i,l,r) for(register int i = (l);i <= (r); i++) #define ...

  6. ORM创建多表以及多表的增删改查

    一. 多表的创建 1. 一对一 在哪个表中设置都行,但是添加数据的时候需要现在没有外键的表中添加数据 models.OneToOneField(to="表名",to_field=& ...

  7. UITextField 输入金额,小数点的控制输入

    #pragma mark --- UITextFieldDelegate ---- (BOOL)textField:(UITextField *)textField shouldChangeChara ...

  8. python装饰器同时支持有参数和无参数的练习题

    ''' 预备知识: …… @decorator def f(*args,**kwargs): pass # 此处@decorator  等价于 f = decorator(f) @decorator2 ...

  9. 【CF486E】LIS of Sequence题解

    [CF486E]LIS of Sequence题解 题目链接 题意: 给你一个长度为n的序列a1,a2,...,an,你需要把这n个元素分成三类:1,2,3: 1:所有的最长上升子序列都不包含这个元素 ...

  10. 玩玩微信公众号Java版之七:自定义微信分享

    前面已经学会了微信网页授权,现在微信网页的功能也可以开展起来啦! 首先,我们先来学习一下分享,如何在自己的页面获取分享接口及让小伙伴来分享呢? 今天的主人公: 微信 JS-SDK, 对应官方链接为:微 ...