基于华为SMProxy开发cmpp2.0(跳坑版)

万俟沛
2023-12-01

说明:

1. 利用华为提供的jar包(smproxy.jar)进行开发。

2. 根据华为提供的“短消息代理(CMPP20 SMProxy)使用说明书.doc”文档说明。

3. 短信的开发:上行,下行,下行短信回执。

 

一、首先必须继承SMProxy的抽象类,代码如下:

import com.huawei.insa2.comm.cmpp.message.CMPPDeliverMessage;
import com.huawei.insa2.comm.cmpp.message.CMPPMessage;
import com.huawei.insa2.util.Args;
import com.huawei.smproxy.SMProxy;
import lombok.extern.slf4j.Slf4j;


/**
 * 重写华为方法
 */
@Slf4j
public class SmsSendProxy extends SMProxy {

    private GatewaySmsServiceImpl gatewaySmsService;

    public SmsSendProxy(Args args, GatewaySmsServiceImpl gatewaySmsService) {
        super(args);
        this.gatewaySmsService = gatewaySmsService;
    }

    @Override
    public CMPPMessage onDeliver(CMPPDeliverMessage msg) {
        log.debug("启动接收短信");
        gatewaySmsService.processRecvDeliverMsg(msg);
        return super.onDeliver(msg);
    }

    @Override
    public void onTerminate() {
        gatewaySmsService.onTerminate();
    }

}

二、基于该类,进行短信的接收和发送的编写

import com.huawei.insa2.comm.cmpp.message.CMPPDeliverMessage;
import com.huawei.insa2.comm.cmpp.message.CMPPSubmitMessage;
import com.huawei.insa2.comm.cmpp.message.CMPPSubmitRepMessage;
import com.huawei.insa2.util.Args;
import com.huawei.insa2.util.Cfg;
import com.huawei.insa2.util.TypeConvert;
import com.demo.common.model.OHResult;
import com.demo.order.service.GatewaySmsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.*;


/**
 * 网关短信
 */
@Slf4j
@Service
public class GatewaySmsServiceImpl implements GatewaySmsService {

    SmsSendProxy smProxy;

    private static String RETURN_UNICODE_SC = "UnicodeBigUnmarked";
    private static String RETURN_UNICODE_TEST = "GB2312";


    @Override
    public void init() {
        try {
            File file = new File("/home/weihu/cmpp.xml");
            if (!file.exists()) {
                return;
            }

            Args args = new Cfg("/home/weihu/cmpp.xml").getArgs("ismg");
            smProxy = new SmsSendProxy(args, this);
        } catch (Exception e) {
            log.error("建立网关连接失败", e);
        }
    }


//发送短信
    @Override
    public OHResult sendMessage(String[] phones, String message) {
        int rstNum = -1;
        long msgId = -1;

        List<byte[]> list = this.getLongByte(message);
        if (CollectionUtils.isEmpty(list)) {
            return OHResult.Fail("发送失败");
        }

        try {
            for (String phone : phones) {
                log.debug("发送号码:{}", phone);

                for (int i = 0; i < list.size(); i++) {
                    CMPPSubmitMessage cmppSubmitMessage = new CMPPSubmitMessage(
                            list.size(),//@pk_Total 相同msg_Id消息总条数,短短信这里是1
                            i + 1,//@pk_Number 相同msg_Id的消息序号
                            1,//@registered_Delivery 是否要求返回状态报告
                            1,//@msg_Level  信息级别
                            "",// @service_Id 业务类型 用户自定义 用来分类查询
                            2,//@fee_UserType 0对目的终端计费;1对源终端计费;2对SP计费;
                            "",//@fee_Terminal_Id 被计费用户的号码
                            0,//@tp_Pid GSM协议类型 一般文本的时候设0,铃声图片设1
                            1,//@tp_Udhi GSM协议类型 0不包含头部信息 1包含头部信息
                            8,//@msg_Fmt 消息格式
                            "123456",//@msg_Src 消息内容来源 6位的企业代码,这里需修改
                            "02",// @fee_Type 资费类别 一般为02:按条计信息费
                            "0",//@fee_Code 资费代码(以分为单位)
                            null,//@valid_Time 存活有效期
                            null,//@at_Time 定时发送时间
                            "10085",//@src_Terminal_Id 移动所提供的服务代码  此处需修改
                            phones,//@dest_Terminal_Id 接收业务的MSISDN号码,就是接收短信的手机号,String数组
                            list.get(i),//@msg_Content 消息内容 byte[],发送的消息内容,需要转化为byte[]
                            "" //预留
                    );
                    CMPPSubmitRepMessage sub = (CMPPSubmitRepMessage) smProxy.send(cmppSubmitMessage);

                    //发送结果
                    rstNum = sub.getResult();
                    //网关生成的唯一标识
                    msgId = TypeConvert.byte2long(sub.getMsgId());
                    log.debug("发送号码:{} result:{} msgId:{}", phone, rstNum, msgId);
                }

                if (rstNum == 0) {
                    log.debug("{} 发送成功", phone);
                    return OHResult.Success("发送成功", String.valueOf(msgId));
                } else {
                    log.debug("{} 发送失败", phone);
                    return OHResult.Fail("1", "发送失败", String.valueOf(msgId));
                }
            }
        } catch (Exception e) {
            log.error("发送异常:", e);

            //查询 SMGPSMProxy 与网关的 TCP连接状态 正常等于null
            String stateDesc = smProxy.getConnState();
            log.debug("网关TCP状态:" + stateDesc);

            if (stateDesc != null) {
                log.debug("网关重新连接");
                smProxy.close();
                init();
            }
            return OHResult.Fail("发送失败");
        }
    }


//接收短信
    @Override
    public void processRecvDeliverMsg(CMPPDeliverMessage deliverMsg) {
        if (deliverMsg.getRegisteredDeliver() == 0) {
            //上行短信
            try {
                //字节
                int msgFmt = deliverMsg.getMsgFmt();
                //上行号码
                String srcTerminalId = esc0(deliverMsg.getSrcterminalId());
                //上行短信内容
                String msgContent;
                if (deliverMsg.getMsgFmt() == 8) {
                    msgContent = esc0(new String(deliverMsg.getMsgContent(), RETURN_UNICODE_SC));
                } else {
                    msgContent = esc0(new String(deliverMsg.getMsgContent(), RETURN_UNICODE_TEST));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            //发送短信后的回执状态
            try {
                //发送号码
                String srcTerminalId = esc0(deliverMsg.getSrcterminalId());
                //回执状态
                String stat = esc0(deliverMsg.getStat());
                //statusMsgId 和 sendMessage类回调的msgId 一致
                long statusMsgId = TypeConvert.byte2long(deliverMsg.getStatusMsgId());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    @Override
    public void onTerminate() {
        log.debug("网关重新连接");
        smProxy.close();
        init();
    }


    public List<byte[]> getLongByte(String message) {
        List<byte[]> list = new ArrayList<byte[]>();
        try {
            //转换为byte[]
            byte[] messageUCS2 = message.getBytes("UnicodeBigUnmarked");
            // 长短信长度
            int messageUCS2Len = messageUCS2.length;
            int maxMessageLen = 140;
            // 长短信发送
            if (messageUCS2Len > maxMessageLen) {
                //int tpUdhi = 1;
                //长消息是1.短消息是0
                //int msgFmt = 0x08;//长消息不能用GBK // 长短信分为多少条发送
                int messageUCS2Count = messageUCS2Len / (maxMessageLen - 6) + 1;
                byte[] tp_udhiHead = new byte[6];
                Random random = new Random();
                //随机填充tp_udhiHead[3] 标识这批短信
                random.nextBytes(tp_udhiHead);
                tp_udhiHead[0] = 0x05;
                tp_udhiHead[1] = 0x00;
                tp_udhiHead[2] = 0x03;
//				tp_udhiHead[3] = 0x0A;
                tp_udhiHead[4] = (byte) messageUCS2Count;
                // 默认为第一条
                tp_udhiHead[5] = 0x01;
                for (int i = 0; i < messageUCS2Count; i++) {
                    tp_udhiHead[5] = (byte) (i + 1);
                    byte[] msgContent;
                    // 不为最后一条
                    if (i != messageUCS2Count - 1) {
                        msgContent = byteAdd(tp_udhiHead, messageUCS2, i * (maxMessageLen - 6), (i + 1) * (maxMessageLen - 6));
                        list.add(msgContent);
                    } else {
                        msgContent = byteAdd(tp_udhiHead, messageUCS2, i * (maxMessageLen - 6), messageUCS2Len);
                        list.add(msgContent);
                    }
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }


    public byte[] byteAdd(byte[] tpUdhiHead, byte[] messageUCS2, int i, int j) {
        byte[] msgb = new byte[j - i + 6];
        System.arraycopy(tpUdhiHead, 0, msgb, 0, 6);
        System.arraycopy(messageUCS2, i, msgb, 6, j - i);
        return msgb;
    }


    public String esc0(String s) {
        if (s == null || s.length() == 0) {
            s = "";
            return s;
        } else {
            int i = s.indexOf('\0');
            if (i > 0)
                s = s.substring(0, i);
            else
                s = s.replaceAll("\0", "");
        }
        return s;
    }

}

三、cmpp.xml网关配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <!--与InfoX建立连接所需参数-->
    <ismg>
        <!-- InfoX主机地址,与移动签合同时移动所提供的地址 需修改-->
        <host>127.0.0.1</host>
        <!-- InfoX主机端口号 cmpp2.0默认为7890,cmpp3.0为7891-->
        <port>7890</port>
        <!--(登录帐号SP…ID)与移动签合同时所提供的企业代码 6位  需修改-->
        <source-addr>123456</source-addr>
        <!--登录密码 默认为空 如有需修改-->
        <shared-secret>654321</shared-secret>
        <!-- 心跳信息发送间隔时间(单位:秒) -->
        <heartbeat-interval>10</heartbeat-interval>
        <!-- 连接中断时重连间隔时间(单位:秒)-->
        <reconnect-interval>10</reconnect-interval>
        <!-- 需要重连时,连续发出心跳而没有接收到响应的个数(单位:个)-->
        <heartbeat-noresponseout>5</heartbeat-noresponseout>
        <!-- 操作超时时间(单位:秒) -->
        <transaction-timeout>10</transaction-timeout>
        <!--双方协商的版本号(大于0,小于256)-->
        <version>1</version>
        <!--是否属于调试状态,true表示属于调试状态,所有的消息被打印输出到屏幕,false表示不属于调试状态,所有的消息不被输出-->
        <debug>true</debug>
    </ismg>    
</config>

 

PS:

1、接收短信中的号码会有86,+86开头出现,需要人工处理。

2、接收短信中的号码和内容会有特殊字符,此处代码已经进行了处理。

3、发送短信后回调的msgId和接收短信中statusMsgId是一致,从而确定是哪条短信发送的回执。

4、SMProxy说明书:http://www.docin.com/p-43812154.html

 类似资料: