基于华为smproxy_20040730.jar的CMPP和SMPP的短信构造(支持长短信)

网上有许多关于CMPP和SMPP的介绍,这里不重复说了,直接介绍短信的构造方法。

1、CMPP短信构造方法:

/**
 * 构造消息体
 * msisdns:要发送的手机号码
 * msgContent:消息内容
 */
private CMPPSubmitMessage[] productMsg(String[] msisdns, String msgContent);

如果是短短信(msgContent.length() <= 70),可以采用如下构造方法:

CMPPSubmitMessage msg = new CMPPSubmitMessage(1, 1, 
	config.getRegistered_Delivery(), 
	config.getMsg_level(), 
	config.getService_Id(), 
	config.getFee_UserType(), 
	"", 
	config.getTp_Pid(), 
	config.getTp_Udhi(), 
	config.getMsg_Fmt(), 
	config.getMsg_Src(), 
	config.getFee_Type(), 
	config.getFee_Code(), 
	null, 
	null, 
	config.getSrc_Terminal_Id(), 
	msisdns, 
	msgContent.getBytes(), 
	"");
其中,构造函数的前两个参数设置为1,tp_Udhi值为0 (表示没有协议头),msg_Fmt值为15 (表示GBK编码)。其他使用jar包里例子的默认设置就行。

如果是长短信(msgContent.length() > 70),则采用如下构造方法:

	// 采用UCS2编码格式
	int maxMessageLen = 140;
	byte[] contents = msgContent.getBytes("UnicodeBigUnmarked");
	int messageUCS2Len = contents.length;
	// 计算短信数量
	int count = messageUCS2Len / (maxMessageLen - 6);
	int rest = messageUCS2Len % (maxMessageLen - 6);
	if (rest > 0) {
		count++;
	}
	byte index = 1;
	for (int i = 0; i < count; i++) {
		byte[] smsHead = new byte[6]; // 6位协议头格式:05 00 03 XX MM NN
		smsHead[0] = 0x05; // 表示剩余协议头的长度
		smsHead[1] = 0x00; // 这个值在GSM 03.40规范9.2.3.24.1中规定
		smsHead[2] = 0x03; // 这个值表示剩下短信标识的长度
		smsHead[3] = 0x00; // 这批短信的唯一标志(被拆分的多条短信,此值必需一致)。
		smsHead[4] = (byte) count; // 这批短信的数量
		smsHead[5] = index; // 当前短信是这批短信中第几条
		byte[] msgBytes = null;
		if (i != (count - 1)) {
			msgBytes = new byte[140];
			System.arraycopy(smsHead, 0, msgBytes, 0, 6);
			System.arraycopy(contents, i * (maxMessageLen - 6), msgBytes, 6, maxMessageLen - 6);
		} else {
			msgBytes = new byte[6 + rest];
			System.arraycopy(smsHead, 0, msgBytes, 0, 6);
			System.arraycopy(contents, i * (maxMessageLen - 6), msgBytes, 6, rest);
		}
		// 此处发送短信时,需要将TP_udhi字段设置为1,表示短信内容中含有协议头信息
		CMPPSubmitMessage msg = new CMPPSubmitMessage(1, 1, 
			config.getRegistered_Delivery(), 
			config.getMsg_level(), 
			config.getService_Id(), 
			config.getFee_UserType(), 
			"", 
			config.getTp_Pid(), 
			1, //表示有协议头
			0x08, //UCS2格式编码
			config.getMsg_Src(), 
			config.getFee_Type(), 
			config.getFee_Code(), 
			null, 
			null, 
			config.getSrc_Terminal_Id(), 
			msisdns, 
			msgBytes,
			"");
		index++; 
	}

2、SMPP短信构造方法:

/**
 * 构造消息体
 * msisdns:要发送的手机号码
 * msgContent:消息内容
 */
private FixedSMPPSubmitMessage[] productMsg(String msisdn, String msgContent);

华为Jar包的SMPP协议有bug,会导致长短信无法发送。因此需要对其jar包里的SMPPSubmitMessage.java类进行修正,即FixedSMPPSubmitMessage.java。该修正类的内容附在正文最后。

由于在开发时,短信中心不支持GBK编码,因此短短信和长短信都要通过UCS2编码进行发送。首先要判断要发送的内容是短短信还是长短信:

int maxMessageLen = 126;
byte[] contents = msgContent.getBytes("ISO-10646-UCS-2");
int messageUCS2Len = contents.length;
如果是短短信(messageUCS2Len <= maxMessageLen),和CMPP类似,此处esmClass设置为0,表示没有协议头,dataCoding设置为0x08,表示UCS2编码。其他采用demo的默认值:
FixedSMPPSubmitMessage submitMsg = new FixedSMPPSubmitMessage("", 
	(byte)0, config.getSourceAddrNpi(), 
	config.getSourceAddr(), config.getDestAddrTon(), config.getDestAddrNpi(), 
	"86" + msisdn, config.getEsmClass(), config.getProtocolId(), 
	config.getPriorityFlag(), config.getScheduleDeliveryTime(), config.getValidityPeriod(), 
	config.getRegisteredDelivery(), config.getReplaceIfPresentFlag(), config.getDataCoding(), 
	config.getSmDefaultMsgId(), (byte)(contents.length), contents);

如果是长短信(messageUCS2Len > maxMessageLen),将esmClass的值设置为0x7f,表示有协议头,其他采用demo的默认值,构造方法如下:

int count = messageUCS2Len / (maxMessageLen - 6);
int rest = messageUCS2Len % (maxMessageLen - 6);
if (rest > 0) {
	count++;
}
for (int i = 0; i < count; i++) {
	byte[] head = new byte[6]; // 6位协议头格式:05 00 03 XX MM NN
	head[0] = 0x05; // 表示剩余协议头的长度
	head[1] = 0x00; // 这个值在GSM 03.40规范9.2.3.24.1中规定
	head[2] = 0x03; // 这个值表示剩下短信标识的长度
	head[3] = 0x00; // 这批短信的唯一标志(被拆分的多条短信,此值必需一致)
	head[4] = (byte) count; // 这批短信的数量
	head[5] = (byte)(1 + i); // 当前短信是这批短信中第几条
	byte[] msgBytes = null;
	if (i != (count - 1)) {
		msgBytes = new byte[maxMessageLen];
		System.arraycopy(head, 0, msgBytes, 0, 6);
		System.arraycopy(contents, i * (maxMessageLen - 6), msgBytes, 6, maxMessageLen - 6);
	}else{
		msgBytes = new byte[6 + rest];
		System.arraycopy(head, 0, msgBytes, 0, 6);
		System.arraycopy(contents, i * (maxMessageLen - 6), msgBytes, 6, rest);
	}
	FixedSMPPSubmitMessage submitMsg = new FixedSMPPSubmitMessage("", (byte) 0, config.getSourceAddrNpi(), 
			config.getSourceAddr(), config.getDestAddrTon(),
			config.getDestAddrNpi(), "86" + msisdn, 
			(byte)0x7f, config.getProtocolId(),
			config.getPriorityFlag(), config.getScheduleDeliveryTime(), 
			config.getValidityPeriod(), config.getRegisteredDelivery(),
			config.getReplaceIfPresentFlag(), config.getDataCoding(),
			config.getSmDefaultMsgId(), (byte) msgBytes.length,
			msgBytes);
}

附件:FixedSMPPSubmitMessage.java

import com.huawei.insa2.comm.smpp.SMPPConstant;
import com.huawei.insa2.comm.smpp.message.SMPPMessage;

/**
 * This command is issued by the ESME to submit a short message to the SMSC for
 * transmission to a specified subscriber. When a real source address is
 * provided in a registered submit_sm request, the source address can be used as
 * the destination address for a delivery receipt. It can also be used in
 * identifying the message source in a CDR. This source address must fall in the
 * range of addresses associated with the bind command. Where the originator of
 * messages from the ESME is the ESME itself, or where the ESME does not have a
 * real source address, the source address fields may be defaulted to NULL, and
 * the source address will be taken from the SMSC administration “callback
 * address” for the particular ESME instance. The submit_sm operation can also
 * be used to replace a short message which has previously been submitted. This
 * is achieved by setting the replace_if_present_flag to 0x01 in the Interface.
 * The first message found in the SMSC whose source and destination match those
 * given in the submit_sm will have it’s text replaced by the text in the
 * short_message field of the submit_sm.
 * 
 * 
 */
public class FixedSMPPSubmitMessage extends SMPPMessage {
	private StringBuffer strBuf;

	protected final String serviceType;

	protected final byte sourceAddrTon;

	protected final byte sourceAddrNpi;

	protected final String sourceAddr;

	protected final byte destAddrTon;

	protected final byte destAddrNpi;

	protected final String destinationAddr;

	protected final byte esmClass;

	protected final byte protocolId;

	protected final byte priorityFlag;

	protected final String scheduleDeliveryTime;

	protected final String validityPeriod;

	protected final byte registeredDelivery;

	protected final byte replaceIfPresentFlag;

	protected final byte dataCoding;

	protected final byte smDefaultMsgId;

	protected final byte smLength;

	protected final byte[] shortMessage;

	private String msisdn;

	public String getMsisdn() {
		return msisdn;
	}

	public void setMsisdn(String msisdn) {
		this.msisdn = msisdn;
	}

	/**
	 * 
	 * @param serviceType
	 *            Var. Max 6. Indicates the type of service associated with the
	 *            message. Where not required this should be set to a single
	 *            NULL byte.
	 * @param sourceAddrTon
	 *            Type of number for source. Where not required this should be
	 *            NULL. (See GSM 03.40 [2] 9.1.2.5)
	 * @param sourceAddrNpi
	 *            Numbering Plan Indicator for source Where not required this
	 *            should be NULL. (See GSM 03.40 [2] 9.1.2.5)
	 * @param sourceAddr
	 *            Address of SME which originated this message. This is the
	 *            source address of the short message submitted. This variable
	 *            length field may have leading spaces. Where not required this
	 *            should be a single NULL byte.
	 * @param destAddrTon
	 *            Type of number for destination. Where not required this should
	 *            be NULL (See GSM 03.40 [2] 9.1.2.5)
	 * @param destAddrNpi
	 *            Numbering Plan Indicator for destination Where not required
	 *            this should be NULL. (See GSM 03.40 [2] 9.1.2.5)
	 * @param destinationAddr
	 *            Destination address of this short message. For mobile
	 *            terminated messages, this is the SME address of the target
	 *            subscriber. This variable length field may have leading
	 *            spaces. Where not required this should be a single NULL byte.
	 * @param esmClass
	 *            Indication of message type. For the submit_sm command this
	 *            field is unused, and should be set to NULL. For the deliver_sm
	 *            command however, this field may identify the message as a
	 *            delivery receipt.
	 * @param protocolId
	 *            GSM Protocol ID (See GSM 03.40 [2] 9.2.3.9)
	 * @param priorityFlag
	 *            Designates the message as priority. Setting priority on a
	 *            message moves it to the top of the SMSC message queue for that
	 *            subscriber. 0 = non-priority (default) 1 = priority
	 *            >1=Reserved
	 * @param scheduleDeliveryTime
	 *            The absolute date and time at which delivery of this message
	 *            must be attempted. The format is defined in section 7.5 Where
	 *            not required this should be a single NULL byte.
	 * @param validityPeriod
	 *            The expiration time of this message. This is specified as an
	 *            absolute date and time of expiry. The format is defined in
	 *            section 7.5 Where not required this should be a single NULL
	 *            byte.
	 * @param registeredDelivery
	 *            Flag indicating if the message is a registered short message
	 *            and thus if a Delivery Receipt is required upon the message
	 *            attaining a final state. 0=No receipt required (non-registered
	 *            delivery). 1=Receipt required (registered delivery)
	 *            >1=Reserved
	 * @param replaceIfPresentFlag
	 *            Flag indicating if submitted message should replace an
	 *            existing message between the specified source and destination.
	 *            0=Don’t Replace (default) 1=Replace >1=Reserved
	 * @param dataCoding
	 *            GSM Data-Coding-Scheme ( See GSM 03.40 [2] 9.2.3.10)
	 * @param smDefaultMsgId
	 *            Indicates the default short message to send, by providing an
	 *            index into the table of Predefined Messages set up by the SMSC
	 *            administrator. This should be set to NULL if a text message is
	 *            being sent. Range is 0x01 to 0x64. (See SMPP Applications
	 *            Guide [1] - Default Short Message).
	 * @param smLength
	 *            Length of the text of the message in bytes.
	 * @param shortMessage
	 *            Up to 160 bytes of data. This is the text that is transmitted
	 *            to the mobile station. Note that only ‘sm_length’ bytes will
	 *            be used.
	 * @throws IllegalArgumentException
	 */
	public FixedSMPPSubmitMessage(String serviceType, byte sourceAddrTon,
			byte sourceAddrNpi, String sourceAddr, byte destAddrTon,
			byte destAddrNpi, String destinationAddr, byte esmClass,
			byte protocolId, byte priorityFlag, String scheduleDeliveryTime,
			String validityPeriod, byte registeredDelivery,
			byte replaceIfPresentFlag, byte dataCoding, byte smDefaultMsgId,
			byte smLength, byte[] shortMessage) throws IllegalArgumentException {
		this.msisdn = destinationAddr;
		if (serviceType.length() > 5) {
			throw new IllegalArgumentException(SMPPConstant.SUBMIT_INPUT_ERROR
					+ ":serviceType " + SMPPConstant.STRING_LENGTH_GREAT + "5");
		}

		if (sourceAddr.length() > 20) {
			throw new IllegalArgumentException(SMPPConstant.SUBMIT_INPUT_ERROR
					+ ":sourceAddr " + SMPPConstant.STRING_LENGTH_GREAT + "20");
		}

		if (destinationAddr == null)
			throw new IllegalArgumentException(SMPPConstant.SUBMIT_INPUT_ERROR
					+ ":destinationAddr " + SMPPConstant.STRING_NULL);

		if (destinationAddr.length() > 20) {
			throw new IllegalArgumentException(SMPPConstant.SUBMIT_INPUT_ERROR
					+ ":destinationAddr " + SMPPConstant.STRING_LENGTH_GREAT
					+ "20");
		}

		if (scheduleDeliveryTime.length() != 0
				&& scheduleDeliveryTime.length() != 16) {
			throw new IllegalArgumentException(SMPPConstant.SUBMIT_INPUT_ERROR
					+ ":scheduleDeliveryTime "
					+ SMPPConstant.STRING_LENGTH_NOTEQUAL + "16");
		}

		if (validityPeriod.length() != 0 && validityPeriod.length() != 16) {
			throw new IllegalArgumentException(SMPPConstant.SUBMIT_INPUT_ERROR
					+ ":validityPeriod " + SMPPConstant.STRING_LENGTH_NOTEQUAL
					+ "16");
		}

		if (smLength > 254) {
			throw new IllegalArgumentException(SMPPConstant.SUBMIT_INPUT_ERROR
					+ ":smLength " + SMPPConstant.STRING_LENGTH_GREAT + "254");
		}

		if (shortMessage.length > 254) {
			throw new IllegalArgumentException(SMPPConstant.SUBMIT_INPUT_ERROR
					+ ":shortMessage " + SMPPConstant.STRING_LENGTH_GREAT
					+ "254");
		}

		this.serviceType = serviceType;
		this.sourceAddrTon = sourceAddrTon;
		this.sourceAddrNpi = sourceAddrNpi;
		this.sourceAddr = destinationAddr;
		this.destAddrTon = destAddrTon;
		this.destAddrNpi = destAddrNpi;
		this.destinationAddr = destinationAddr;
		this.esmClass = esmClass;
		this.protocolId = protocolId;
		this.priorityFlag = priorityFlag;
		this.scheduleDeliveryTime = scheduleDeliveryTime;
		this.validityPeriod = validityPeriod;
		this.registeredDelivery = registeredDelivery;
		this.replaceIfPresentFlag = replaceIfPresentFlag;
		this.dataCoding = dataCoding;
		this.smDefaultMsgId = smDefaultMsgId;
		this.smLength = smLength;
		this.shortMessage = shortMessage;

		int len = 33 + serviceType.length() + sourceAddr.length()
				+ destinationAddr.length() + scheduleDeliveryTime.length()
				+ validityPeriod.length() + smLength;
		super.buf = new byte[len];
		setMsgLength(len);
		setCommandId(4);
		setStatus(0);
		int pos = 16;
		System.arraycopy(serviceType.getBytes(), 0, super.buf, pos, serviceType
				.length());
		pos = pos + serviceType.length() + 1;
		super.buf[pos] = sourceAddrTon;
		pos++;
		super.buf[pos] = sourceAddrNpi;
		pos++;
		System.arraycopy(sourceAddr.getBytes(), 0, super.buf, pos, sourceAddr
				.length());
		pos = pos + sourceAddr.length() + 1;
		super.buf[pos] = destAddrTon;
		pos++;
		super.buf[pos] = destAddrNpi;
		pos++;
		System.arraycopy(destinationAddr.getBytes(), 0, super.buf, pos,
				destinationAddr.length());
		pos = pos + destinationAddr.length() + 1;
		super.buf[pos++] = esmClass;
		super.buf[pos++] = protocolId;
		super.buf[pos++] = priorityFlag;
		System.arraycopy(scheduleDeliveryTime.getBytes(), 0, super.buf, pos,
				scheduleDeliveryTime.length());
		pos = pos + scheduleDeliveryTime.length() + 1;
		System.arraycopy(validityPeriod.getBytes(), 0, super.buf, pos,
				validityPeriod.length());
		pos += (validityPeriod.length() + 1);
		super.buf[pos++] = registeredDelivery;
		super.buf[pos++] = replaceIfPresentFlag;
		super.buf[pos++] = dataCoding;
		super.buf[pos++] = smDefaultMsgId;
		super.buf[pos++] = smLength;
		System.arraycopy(shortMessage, 0, super.buf, pos, smLength);

		strBuf = new StringBuffer(600);
		strBuf.append(",serviceType=" + serviceType);
		strBuf.append(",sourceAddrTon=" + sourceAddrTon);
		strBuf.append(",sourceAddrNpi=" + sourceAddrNpi);
		strBuf.append(",sourceAddr=" + sourceAddr);

		strBuf.append(",destAddrTon=" + destAddrTon);
		strBuf.append(",destAddrNpi=" + destAddrNpi);
		strBuf.append(",destinationAddr=" + destinationAddr);
		strBuf.append(",esmClass=" + esmClass);
		strBuf.append(",protocolId=" + protocolId);

		strBuf.append(",priorityFlag=" + priorityFlag);
		strBuf.append(",scheduleDeliveryTime=" + scheduleDeliveryTime);
		strBuf.append(",validityPeriod=" + validityPeriod);
		strBuf.append(",registeredDelivery=" + registeredDelivery);
		strBuf.append(",replaceIfPresentFlag=" + replaceIfPresentFlag);

		strBuf.append(",dataCoding=" + dataCoding);
		strBuf.append(",smDefaultMsgId=" + smDefaultMsgId);
		strBuf.append(",smLength=" + smLength);
		strBuf.append(",shortMessage=" + shortMessage);
	}

	public FixedSMPPSubmitMessage(byte[] buf) throws IllegalArgumentException {
		super.buf = new byte[buf.length];
		System.arraycopy(buf, 0, super.buf, 0, buf.length);

		int pos = 16;
		for (; pos < this.buf.length; ++pos) {
			if (buf[pos] == 0) {
				break;
			}
		}

		int len = pos - 16;

		this.serviceType = new String(buf, 16, len);

		pos++;
		this.sourceAddrTon = buf[pos++];
		this.sourceAddrNpi = buf[pos++];

		int start = pos;

		for (; pos < this.buf.length; ++pos) {
			if (buf[pos] == 0) {
				break;
			}
		}

		this.sourceAddr = new String(buf, start, pos - start);
		pos++;
		this.destAddrTon = buf[pos++];
		this.destAddrNpi = buf[pos++];

		start = pos;

		for (; pos < this.buf.length; ++pos) {
			if (buf[pos] == 0) {
				break;
			}
		}

		this.destinationAddr = new String(buf, start, pos - start);
		pos++;

		this.esmClass = buf[pos++];
		this.protocolId = buf[pos++];
		this.priorityFlag = buf[pos++];

		this.scheduleDeliveryTime = new String(buf, pos, 16);
		pos += 17;

		this.validityPeriod = new String(buf, pos, 16);
		pos += 17;

		this.registeredDelivery = buf[pos++];
		this.replaceIfPresentFlag = buf[pos++];
		this.dataCoding = buf[pos++];
		this.smDefaultMsgId = buf[pos++];
		this.smLength = buf[pos++];
		this.shortMessage = buf;
	}

	public String getServiceType() {
		return serviceType;
	}

	public byte getSourceAddrTon() {
		return this.sourceAddrTon;
	}

	public byte getSourceAddrNpi() {
		return this.sourceAddrNpi;
	}

	public String getSourceAddress() {
		return sourceAddr;
	}

	public byte getDestAddrTon() {
		return this.destAddrTon;
	}

	public byte getDestAddrNpi() {
		return this.destAddrNpi;
	}

	public String getDestAddress() {
		return this.destinationAddr;
	}

	public byte getEsmClass() {
		return this.esmClass;
	}

	public byte getProtocolId() {
		return this.protocolId;
	}

	public String getScheduleDeliveryTime() {
		return this.scheduleDeliveryTime;
	}

	public String getValidityPeriod() {
		return this.validityPeriod;
	}

	public byte[] getShortMessage() {
		return this.shortMessage;
	}

	public String toString() {
		StringBuffer outBuf = new StringBuffer(600);
		outBuf.append("SMPPSubmitMessage: ");
		outBuf.append("PacketLength=" + getMsgLength());
		outBuf.append(",CommandID=" + getCommandId());
		outBuf.append(",Status=" + getStatus());
		outBuf.append(",SequenceID=" + getSequenceId());
		if (strBuf != null) {
			outBuf.append(strBuf.toString());
		}
		return outBuf.toString();
	}

}


  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值