? 最近要增加短信平臺對移動CMPP3協議的支持,所以就研究了下他的實現。所謂的 CMPP就是 中國移動通信互聯網 短信網關 接口協議 。
CMPP?協議以TCP/IP?作為底層通信承載,所以開發這塊需要對 TCP/IP網絡編程要有一定的了解。
原理:個人理解就是雙方建立以什么方式來通信,就好比信是暗號寫的,只有雙方看的懂。
本文主要針對于長連接形式發送短信為例, 而我們編寫程序也只用編寫 在C / S架構的通訊過程中的C,然后根據服務商提供的帳號、參數經行測試。
一、實現協議
步驟:
? ? ? ? ? ? 1、 建立 SOKCET,啟動一個線程,發送數據。
?
? ? ? ? ? ? 2、進行 鏈路檢查,判斷服務端通信是否正常等等。
? ? ? ? ? ? 3、 啟動接收socket數據的線程。
二、協議代碼實現:
? ? ?1、協議基本類型如下:
Unsigned Integer |
無符號整數 |
Integer |
整數,可為正整數、負整數或零 |
Octet String |
定長字符串,位數不足時,如果左補 ? 0? 則補 ASCII? 表示的零以填充,如果右補 ? 0? 則補二進制的零以表示字符串的結束符 |
? ? ?2、消息結構:
? ? ? ? ? ? 1)消息頭( 所有消息公共包頭 )PS:注意紅色的部分是所有消息的公共頭
? ? ? ? ? ? 2)和消息體
?
三、接下來就是說說如何封裝CMPP3的消息格式了,取幾個談談就行了,原理都差不多。
?
?
?
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import org.apache.log4j.Logger; //totalLength 消息總長度 //commandID命令消息類型 //sequenceID消息流水號碼,順序累加,步長為1,循環使用(一隊請求和應答消息的流水號必須相同) //Unsigned Integer 無符號整型 //Integer 整數,可為正整數、負整數、零 //Octet String 定長字符串,位數不足時,如果左補0則補ASCII表示的0以填充,如果右補0則補二進制的零表示字符串的結束符 public class MsgHead { private Logger logger = Logger.getLogger(MsgHead.class); private int totalLength; // unsigned Integer; private int commandID; // unsigned Integer; private int sequenceID; // unsigned Integer; public byte[] toByteArray() { ByteArrayOutputStream bous = new ByteArrayOutputStream(); DataOutputStream dous = new DataOutputStream(bous); try { dous.writeInt(getTotalLength()); dous.writeInt(this.getCommandID()); dous.writeInt(this.getSequenceID()); dous.close(); return bous.toByteArray(); } catch (Exception e) { if (dous != null) try { dous.close(); } catch (Exception ee) { } logger.error("封裝CMPP消息頭二進制數組失敗!"); return null; } } }
?代碼片段:
?
import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import org.apache.log4j.Logger; import common.msg.util.MsgUtils; /** * sp請求連接到ISMG消息體定義CMPP_CONNECT操作的目的是向ISMG注冊為一個合法SP身份, * 若注冊成功后建立了應用層的連接,此后SP可以通過此ISMG接收和發送短信。 Source_Addr:Octet String * 源地址,此處為SP_id,即SP的企業代碼 AuthenticatorSource:Octet String 用于鑒別源地址,其值通過單向MD5 * hash計算得出,表示如下; * AuthenticatorSource=MD5(source_addr+9個字節的null+secret+timestamp) * Version:unsigned Integer 雙方協商的版本號,對3.0的版本,高4BIT為3,低4位為0 */ public class MsgConnect extends MsgHead { private static Logger logger = Logger.getLogger(MsgConnect.class); private String sourceAddr;// 源地址,此處為spID; private byte[] authenticatorSource;// 用于鑒別源地址 private byte version; private String timeStamp; public byte[] toByteArray() { ByteArrayOutputStream bous = new ByteArrayOutputStream(); DataOutputStream dous = new DataOutputStream(bous); try { dous.writeInt(this.getTotalLength()); dous.writeInt(this.getCommandID()); dous.writeInt(this.getSequenceID()); MsgUtils.writeString(dous, this.getSourceAddr(), 6,"US-ASCII"); dous.write(this.getAuthenticatorSource()); dous.writeByte(this.getVersion()); dous.writeInt(Integer.parseInt(getTimeStamp())); bous.close(); dous.close(); return bous.toByteArray(); } catch (Exception e) { if (bous != null) try { bous.close(); } catch (Exception ee) { } if (dous != null) try { dous.close(); } catch (Exception ee) { } e.printStackTrace(); logger.error("封裝鏈接二進制數組失敗。"); return null; } } }
?
import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import org.apache.log4j.Logger; import common.msg.util.MsgUtils; public class MsgSubmit extends MsgHead { private static Logger logger = Logger.getLogger(MsgSubmit.class); private long msgId = 0;// 信息標識,8個字節的unsigned為空 private byte pkTotal = 0x01;// 相同的msgID信息的總條數,從1開始,本業務填寫1 private byte pkNumber = 0x01;// 系統MSGID的信息序號,從1開始,本業務只填寫1 private byte registeredDelivery = 0x01; // 是否要求返回狀態確認報告0不需要,1需要; private byte msgLevel = 0x01; private String serviceId = ""; // MSC4310508,業務標識是數字、字母、符號的組合 // 計費類型0:對目的終端MSISDN計費,1對源終端MSISDN計費,2對SP計費,3表示本字段無效 private byte feeUserType = 0x01;// 上線的確認此內容 private String feeTerminalId = "";// 被計費的號碼,當feeUserType為3時有效 private byte feeTerminalType = 0x00;// 被計費用戶的號碼類型,0真實,1偽號碼 private byte tpPId = 0x00; private byte tpUdhi = 0x00; // 0ASCII碼字符串,3短信寫卡操作,4二進制信息,8UCS2編碼,15含GB漢字 private byte msgFmt = 0x0F; private String msgSrc = "";// 信息內容來源SPID; // 01:對“計費用戶號碼”免費,02:對“計費用戶號碼”按條計信息費 // 03:對“被計費號碼包月計費,04:對被計費號碼封頂,05:對被計費號碼是由SP實現; private String feeType = "00";// 默認按條 private String feeCode = "000000"; private String validTime = "";// 存或有效期限,17位長度,暫時不支持此功能 private String atTime = "";// 定時發送時間,17位長度,暫時不支持此功能 private String srcId = "";// 源號碼,用戶手機顯示的號碼 private byte destUsrTl = 0x01;// 接受信息的用戶數量(群發) private String destTerminalId = "";// 接受短信的號碼 private byte destTerminalType = 0x00;// 真實號碼 private byte msgLength;// 消息長度,小于或等于140字節 private byte[] msgContent;// 信息內容; private String linkID = "";// 點播業務使用,非點播業務則不使用 public byte[] toByteArray(String code) { ByteArrayOutputStream bous = new ByteArrayOutputStream(); DataOutputStream dous = new DataOutputStream(bous); try { dous.writeInt(this.getTotalLength()); dous.writeInt(this.getCommandID()); dous.writeInt(this.getSequenceID()); dous.writeLong(this.getMsgId());// Msg_Id 信息標識,由SP接入的短信網關本身產生,本處填空 dous.writeByte(this.getPkTotal());// Pk_total 相同Msg_Id的信息總條數 dous.writeByte(this.getPkNumber());// Pk_number 相同Msg_Id的信息序號,從1開始 dous.writeByte(this.getRegisteredDelivery());// Registered_Delivery // 是否要求返回狀態確認報告 dous.writeByte(this.getMsgLevel());// Msg_level 信息級別 MsgUtils.writeString(dous, this.getServiceId(), 10, code);// Service_Id // 業務標識,是數字、字母和符號的組合。 dous.writeByte(this.getFeeUserType());// Fee_UserType 計費用戶類型字段 // 0:對目的終端MSISDN計費;1:對源終端MSISDN計費;2:對SP計費;3:表示本字段無效,對誰計費參見Fee_terminal_Id字段。 MsgUtils.writeString(dous, this.getFeeTerminalId(), 32, code);// Fee_terminal_Id // 被計費用戶的號碼 dous.writeByte(this.getFeeTerminalType());// Fee_terminal_type // 被計費用戶的號碼類型,0:真實號碼;1:偽碼 dous.writeByte(this.getTpPId()); dous.writeByte(this.getTpUdhi()); dous.writeByte(this.getMsgFmt()); MsgUtils.writeString(dous, this.getMsgSrc(), 6, code);// Msg_src // 信息內容來源(SP_Id) MsgUtils.writeString(dous, this.getFeeType(), 2, code);// FeeType // 資費類別 MsgUtils.writeString(dous, this.getFeeCode(), 6, code); MsgUtils.writeString(dous, this.getValidTime(), 17, code);// 存活有效期 MsgUtils.writeString(dous, this.getAtTime(), 17, code);// 定時發送時間 MsgUtils.writeString(dous, this.getSrcId(), 21, code);// Src_Id // spCode dous.writeByte(this.getDestUsrTl()); MsgUtils.writeString(dous, this.getDestTerminalId(), 32, code); dous.writeByte(this.getDestTerminalType());// Dest_terminal_type // 接收短信的用戶的號碼類型,0:真實號碼;1:偽碼 dous.writeByte(this.getMsgLength()); dous.write(this.getMsgContent()); MsgUtils.writeString(dous, this.getLinkID(), 20,code); bous.close(); dous.close(); return bous.toByteArray(); } catch (Exception e) { if (bous != null) try { bous.close(); } catch (Exception ee) { } if (dous != null) try { dous.close(); } catch (Exception ee) { } logger.error("封裝短信發送二進制數組失敗。"); e.printStackTrace(); return null; } }
?
其實這些實現挺簡單的,網上也有很多類似代碼,主要要明確的一點就是,客戶端和服務端的通信流程和消息長度,多看協議文檔。懂了原理,實現起來很簡單。
最后附CMPP3協議文檔。
?
?
?
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
