/* .Net/C# 实现 中国移动 CMPP v3.0 ISMG <-> SP 收发短信的 SP 客户端 (第2版)(CMPP SP Client) 增加了 CMPP Client 类 本程序严格按 《中国移动通信企业标准》之《中国移动通信互联网短信网关接口协议(China Mobile Point to Point)》(版本号: 3.0.0) 即: CMPP v3.0.0 http://www.spzone.net/protocol/CMPPV3.0.rar 文档,实现了下面消息的定义及其相关协议级交互: 8.4 业务提供商 (SP) 与互联网短信网关 (ISMG) 间的消息定义 8 8.4.1 SP 请求连接到 ISMG(CMPP_CONNECT) 操作 8 8.4.1.1 CMPP_CONNECT 消息定义 (SP -> ISMG) 8 8.4.1.2 CMPP_CONNECT_RESP消息定义 (ISMG -> SP) 9 8.4.2 SP 或 ISMG 请求拆除连接 (CMPP_TERMINATE)操作 9 8.4.2.1 CMPP_TERMINATE 消息定义 (SP -> ISMG 或 ISMG -> SP) 9 8.4.2.2 CMPP_TERMINATE_RESP 消息定义 (SP -> ISMG 或 ISMG -> SP) 10 8.4.3 SP 向 ISMG提交短信 (CMPP_SUBMIT) 操作 10 8.4.3.1 CMPP_SUBMIT 消息定义 (SP -> ISMG) 10 8.4.3.2 CMPP_SUBMIT_RESP 消息定义 (ISMG -> SP) 11 8.4.5 ISMG 向 SP 送交短信 (CMPP_DELIVER) 操作 13 8.4.5.1 CMPP_DELIVER 消息定义 (ISMG -> SP) 13 8.4.5.2 CMPP_DELIVER_RESP 消息定义 (SP -> ISMG) 16 8.4.7 链路检测 (CMPP_ACTIVE_TEST) 操作 17 8.4.7.1 CMPP_ACTIVE_TEST定义 (SP -> ISMG 或 ISMG <- SP) 17 8.4.7.2 CMPP_ACTIVE_TEST_RESP定义 (SP -> ISMG 或 ISMG <- SP) 17 可采用《中国移动通信 CMPP v3.0 短消息网关模拟器 v1.10》进行测试: 下载于: 《北京风起水流软件工作室》 http://www.zealware.com/download/cmpp3smg.rar 《中国移动通信 CMPP v3.0 短消息网关模拟器 v1.10》 http://tech.spforum.net/uploadfile/2006426181749702.rar 本程序以熟悉理解 CMPP 3.0 协议为主要目的,只将 "消息定义" 对象化,其相关协议级交互并未作更深层次的 OO! 也暂无任何错误处理程序! 消息定义的所有字段名称及其数据类型均与上述之 CMPP v3.0.0 文档完全一致! 其间参阅过 shanhe@CSDN or yexiong@cnBlogs 大作(在此鸣谢): http://blog.csdn.net/shanhe/archive/2004/07/19/45383.aspx http://cnblogs.com/yexiong/articles/115330.aspx 但其中有些消息定义字节错位,因此不能正常交互?!且对象化层次较高,不利于理解协议本身! 遂自己动手,丰衣足食,实现部分主要协议(SP 收发短信): playyuer㊣Microshaoft.com Invent. */ namespace Microshaoft { using System; using System.Net.Sockets; public class Utility { public static string BytesArrayToHexString(byte[] data) { return BitConverter.ToString(data).Replace("-", ""); } public static byte[] HexStringToBytesArray(string text) { text = text.Replace(" ", ""); int l = text.Length; byte[] buffer = new byte[l / 2]; for (int i = 0; i < l; i += 2) { buffer[i / 2] = Convert.ToByte(text.Substring(i, 2), 16); } return buffer; } //private static object _SyncLockObject = new object(); public static string Get_MMDDHHMMSS_String(DateTime dt) { return DateTime.Now.ToString("MMddhhmmss"); } public static string Get_YYYYMMDD_String(DateTime dt) { return DateTime.Now.ToString("yyyyMMdd"); } internal static void WriteToStream(byte[] bytes, NetworkStream Stream) { if (Stream.CanWrite) { //lock (_SyncLockObject) { Stream.Write(bytes, 0, bytes.Length); } } } internal static byte[] ReadStreamToBytes(int Length, NetworkStream Stream) { byte[] bytes = null; if (Stream.CanRead) { if (Stream.DataAvailable) { bytes = new byte[Length]; int l = 0; //lock (_SyncLockObject) { while (l < Length) { int r; r = Stream.Read(bytes, l, Length - l); l += r; } } } } return bytes; } } } //CMPP 消息定义 namespace Microshaoft.CMPP.Messages { using System; using System.Text; using System.Security.Cryptography; public enum CMPP_Command_Id : uint { CMPP_CONNECT = 0x00000001 //请求连接 , CMPP_CONNECT_RESP = 0x80000001 //请求连接应答 , CMPP_TERMINATE = 0x00000002 //终止连接 , CMPP_TERMINATE_RESP = 0x80000002 //终止连接应答 , CMPP_SUBMIT = 0x00000004 //提交短信 , CMPP_SUBMIT_RESP = 0x80000004 //提交短信应答 , CMPP_DELIVER = 0x00000005 //短信下发 , CMPP_DELIVER_RESP = 0x80000005 //下发短信应答 , CMPP_QUERY = 0x00000006 //发送短信状态查询 , CMPP_QUERY_RESP = 0x80000006 //发送短信状态查询应答 , CMPP_CANCEL = 0x00000007 //删除短信 , CMPP_CANCEL_RESP = 0x80000007 //删除短信应答 , CMPP_ACTIVE_TEST = 0x00000008 //激活测试 , CMPP_ACTIVE_TEST_RESP = 0x80000008 //激活测试应答 , CMPP_FWD = 0x00000009 //消息前转 , CMPP_FWD_RESP = 0x80000009 //消息前转应答 , CMPP_MT_ROUTE = 0x00000010 //MT路由请求 , CMPP_MT_ROUTE_RESP = 0x80000010 //MT路由请求应答 , CMPP_MO_ROUTE = 0x00000011 //MO路由请求 , CMPP_MO_ROUTE_RESP = 0x80000011 //MO路由请求应答 , CMPP_GET_MT_ROUTE = 0x00000012 //获取MT路由请求 , CMPP_GET_MT_ROUTE_RESP = 0x80000012 //获取MT路由请求应答 , CMPP_MT_ROUTE_UPDATE = 0x00000013 //MT路由更新 , CMPP_MT_ROUTE_UPDATE_RESP = 0x80000013 //MT路由更新应答 , CMPP_MO_ROUTE_UPDATE = 0x00000014 //MO路由更新 , CMPP_MO_ROUTE_UPDATE_RESP = 0x80000014 //MO路由更新应答 , CMPP_PUSH_MT_ROUTE_UPDATE = 0x00000015 //MT路由更新 , CMPP_PUSH_MT_ROUTE_UPDATE_RESP = 0x80000015 //MT路由更新应答 , CMPP_PUSH_MO_ROUTE_UPDATE = 0x00000016 //MO路由更新 , CMPP_PUSH_MO_ROUTE_UPDATE_RESP = 0x80000016 //MO路由更新应答 , CMPP_GET_MO_ROUTE = 0x00000017 //获取MO路由请求 , CMPP_GET_MO_ROUTE_RESP = 0x80000017 //获取MO路由请求应答 } public class MessageHeader //消息头 { public const int Length = 4 + 4 + 4; public CMPP_Command_Id Command_Id { get { return _Command_Id; } } public uint Sequence_Id { get { return _Sequence_Id; } } public uint Total_Length { get { return _Total_Length; } } private uint _Total_Length; // 4 Unsigned Integer 消息总长度(含消息头及消息体) private CMPP_Command_Id _Command_Id; // 4 Unsigned Integer 命令或响应类型 private uint _Sequence_Id; // 4 Unsigned Integer 消息流水号,顺序累加,步长为1,循环使用(一对请求和应答消息的流水号必须相同) public MessageHeader ( uint Total_Length , CMPP_Command_Id Command_Id , uint Sequence_Id ) //发送前 { _Total_Length = Total_Length; _Command_Id = Command_Id; _Sequence_Id = Sequence_Id; } public MessageHeader(byte[] bytes) { byte[] buffer = new byte[4]; Buffer.BlockCopy(bytes, 0, buffer, 0, buffer.Length); Array.Reverse(buffer); _Total_Length = BitConverter.ToUInt32(buffer, 0); Buffer.BlockCopy(bytes, 4, buffer, 0, buffer.Length); Array.Reverse(buffer); _Command_Id = (CMPP_Command_Id)BitConverter.ToUInt32(buffer, 0); Buffer.BlockCopy(bytes, 8, buffer, 0, buffer.Length); Array.Reverse(buffer); _Sequence_Id = BitConverter.ToUInt32(buffer, 0); } public byte[] ToBytes() { byte[] bytes = new byte[MessageHeader.Length]; byte[] buffer = BitConverter.GetBytes(_Total_Length); Array.Reverse(buffer); Buffer.BlockCopy(buffer, 0, bytes, 0, 4); buffer = BitConverter.GetBytes((uint)_Command_Id); Array.Reverse(buffer); Buffer.BlockCopy(buffer, 0, bytes, 4, 4); buffer = BitConverter.GetBytes(_Sequence_Id); Array.Reverse(buffer); Buffer.BlockCopy(buffer, 0, bytes, 8, 4); return bytes; } public override string ToString() { return string.Format ( "\tMessageHeader:{0}Command_Id: {1}{0}Sequence_Id: {2}{0}Total_Length: {2}" , "\r\n\t\t" , _Command_Id , _Sequence_Id , _Total_Length ); } } public class CMPP_CONNECT //: CMPP_Request { public const int BodyLength = 6 + 16 + 1 + 4; private string _Source_Addr; // 6 Octet String 源地址,此处为SP_Id,即SP的企业代码。 private string _Password; private byte[] _AuthenticatorSource; // 16 Octet String 用于鉴别源地址。其值通过单向MD5 hash计算得出,表示如下: // AuthenticatorSource = // MD5(Source_Addr+9 字节的0 +shared secret+timestamp) // Shared secret 由中国移动与源地址实体事先商定,timestamp格式为:MMDDHHMMSS,即月日时分秒,10位。 private uint _Version; // 1 Unsigned Integer 双方协商的版本号(高位4bit表示主版本号,低位4bit表示次版本号),对于3.0的版本,高4bit为3,低4位为0 private uint _Timestamp; // 4 Unsigned Integer 时间戳的明文,由客户端产生,格式为MMDDHHMMSS,即月日时分秒,10位数字的整型,右对齐 。 private MessageHeader _Header; public MessageHeader Header { get { return _Header; } } public byte[] AuthenticatorSource { get { return _AuthenticatorSource; } } public CMPP_CONNECT ( string Source_Addr , string Password , DateTime Timestamp , uint Version , uint Sequence_Id ) { _Header = new MessageHeader ( MessageHeader.Length + BodyLength , CMPP_Command_Id.CMPP_CONNECT , Sequence_Id ); _Source_Addr = Source_Addr; _Password = Password; string s = Utility.Get_MMDDHHMMSS_String(Timestamp); _Timestamp = UInt32.Parse(s); byte[] buffer = new byte[6 + 9 + _Password.Length + 10]; Encoding.ASCII.GetBytes(_Source_Addr).CopyTo(buffer, 0); Encoding.ASCII.GetBytes(_Password).CopyTo(buffer, 6 + 9); Encoding.ASCII.GetBytes(s).CopyTo(buffer, 6 + 9 + _Password.Length); _AuthenticatorSource = new MD5CryptoServiceProvider().ComputeHash(buffer, 0, buffer.Length); _Version = Version; } public byte[] ToBytes() { int i = 0; byte[] bytes = new byte[MessageHeader.Length + BodyLength]; //header 12 byte[] buffer = _Header.ToBytes(); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //Source_Addr 6 i += MessageHeader.Length; buffer = Encoding.ASCII.GetBytes(_Source_Addr); Buffer.BlockCopy(buffer, 0, bytes, i, 6); //AuthenticatorSource 16 i += 6; buffer = _AuthenticatorSource; Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //16 //version 1 i += 16; bytes[i++] = (byte)_Version; //版本 //Timestamp buffer = BitConverter.GetBytes(_Timestamp); Array.Reverse(buffer); buffer.CopyTo(bytes, i); return (bytes); } public override string ToString() { return "[\r\n" + _Header.ToString() + "\r\n" + "\t" + string.Format ( "MessageBody:" + "{0}AuthenticatorSource: {1}" + "{0}Password: {2}" + "{0}Source_Addr: {3}" + "{0}Version: {4}" , "\r\n\t\t" , Utility.BytesArrayToHexString(_AuthenticatorSource) , _Password , _Source_Addr , _Timestamp , _Version ) + "\r\n]"; } } public class CMPP_CONNECT_RESP //: CMPP_Response { private MessageHeader _Header; public const int BodyLength = 4 + 16 + 1; private uint _Status; // 4 Unsigned Integer 状态 // 0:正确 // 1:消息结构错 // 2:非法源地址 // 3:认证错 // 4:版本太高 // 5~:其他错误 private byte[] _AuthenticatorISMG; // 16 Octet String ISMG认证码,用于鉴别ISMG。 // 其值通过单向MD5 hash计算得出,表示如下: // AuthenticatorISMG =MD5(Status+AuthenticatorSource+shared secret),Shared secret 由中国移动与源地址实体事先商定,AuthenticatorSource为源地址实体发送给ISMG的对应消息CMPP_Connect中的值。 // 认证出错时,此项为空。 private uint _Version; // 1 Unsigned Integer 服务器支持的最高版本号,对于3.0的版本,高4bit为3,低4位为0 public byte[] AuthenticatorISMG { get { return _AuthenticatorISMG; } } public uint Status { get { return _Status; } } public uint Version { get { return _Version; } } public MessageHeader Header { get { return _Header; } } public CMPP_CONNECT_RESP(byte[] bytes) { //header 12 int i = 0; byte[] buffer = new byte[MessageHeader.Length]; Buffer.BlockCopy(bytes, 0, buffer, 0, buffer.Length); _Header = new MessageHeader(buffer); //status 4 i += MessageHeader.Length; buffer = new byte[4]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _Status = BitConverter.ToUInt32(buffer, 0); //AuthenticatorISMG 16 i += 4; _AuthenticatorISMG = new byte[16]; Buffer.BlockCopy ( bytes , MessageHeader.Length + 4 , _AuthenticatorISMG , 0 , _AuthenticatorISMG.Length ); //version i += 16; _Version = bytes[i]; } public override string ToString() { return "[\r\n" + _Header.ToString() + "\r\n" + "\t" + string.Format ( "MessageBody:" + "{0}AuthenticatorISMG: {1}" + "{0}BodyLength: {2}" + "{0}Status: {3}" + "{0}Version: {4}" , "\r\n\t\t" , Utility.BytesArrayToHexString(_AuthenticatorISMG) , CMPP_CONNECT_RESP.BodyLength , _Status , _Version ) + "\r\n]"; } } public class CMPP_SUBMIT //: CMPP_Request { private int _BodyLength; //without _Dest_terminal_Id Msg_Content public const int FixedBodyLength = 8 + 1 + 1 + 1 + 1 + 10 + 1 + 32 + 1 + 1 + 1 + 1 + 6 + 2 + 6 + 17 + 17 + 21 + 1 //+ 32*DestUsr_tl + 1 + 1 //+ Msg_length + 20; private ulong _Msg_Id; // 8 Unsigned Integer 信息标识。 private uint _Pk_total; // 1 Unsigned Integer 相同Msg_Id的信息总条数,从1开始。 private uint _Pk_number; // 1 Unsigned Integer 相同Msg_Id的信息序号,从1开始。 private uint _Registered_Delivery; // 1 Unsigned Integer 是否要求返回状态确认报告: // 0:不需要; // 1:需要。 private uint _Msg_level; // 1 Unsigned Integer 信息级别。 private string _Service_Id; // 10 Octet String 业务标识,是数字、字母和符号的组合。 private uint _Fee_UserType; // 1 Unsigned Integer 计费用户类型字段: // 0:对目的终端MSISDN计费; // 1:对源终端MSISDN计费; // 2:对SP计费; // 3:表示本字段无效,对谁计费参见Fee_terminal_Id字段。 private string _Fee_terminal_Id; // 32 Octet String 被计费用户的号码,当Fee_UserType为3时该值有效,当Fee_UserType为0、1、2时该值无意义。 private uint _Fee_terminal_type; // 1 Unsigned Integer 被计费用户的号码类型,0:真实号码;1:伪码。 private uint _TP_pId; // 1 Unsigned Integer GSM协议类型。详细是解释请参考GSM03.40中的9.2.3.9。 private uint _TP_udhi; // 1 Unsigned Integer GSM协议类型。详细是解释请参考GSM03.40中的9.2.3.23,仅使用1位,右对齐。 private uint _Msg_Fmt; // 1 Unsigned Integer 信息格式: // 0:ASCII串; // 3:短信写卡操作; // 4:二进制信息; // 8:UCS2编码; // 15:含GB汉字...... private string _Msg_src; // 6 Octet String 信息内容来源(SP_Id)。 private string _FeeType; // 2 Octet String 资费类别: // 01:对"计费用户号码"免费; // 02:对"计费用户号码"按条计信息费; // 03:对"计费用户号码"按包月收取信息费。 private string _FeeCode; // 6 Octet String 资费代码(以分为单位)。 private string _ValId_Time; // 17 Octet String 存活有效期,格式遵循SMPP3.3协议。 private string _At_Time; // 17 Octet String 定时发送时间,格式遵循SMPP3.3协议。 private string _Src_Id; // 21 Octet String 源号码。SP的服务代码或前缀为服务代码的长号码, 网关将该号码完整的填到SMPP协议Submit_SM消息相应的source_addr字段,该号码最终在用户手机上显示为短消息的主叫号码。 private uint _DestUsr_tl; // 1 Unsigned Integer 接收信息的用户数量(小于100个用户)。 private string[] _Dest_terminal_Id; // 32*DestUsr_tl Octet String 接收短信的MSISDN号码。 private uint _Dest_terminal_type; // 1 Unsigned Integer 接收短信的用户的号码类型,0:真实号码;1:伪码。 private uint _Msg_Length; // 1 Unsigned Integer 信息长度(Msg_Fmt值为0时:<160个字节;其它<=140个字节),取值大于或等于0。 private string _Msg_Content; // Msg_length Octet String 信息内容。 private string _LinkID; // 20 Octet String 点播业务使用的LinkID,非点播类业务的MT流程不使用该字段。 public MessageHeader Header { get { return _Header; } set { _Header = value; } } private MessageHeader _Header; private uint _Sequence_Id; public CMPP_SUBMIT(uint Sequence_Id) { _Sequence_Id = Sequence_Id; } private byte[] _Msg_Content_Bytes; private void SetHeader() { //byte[] buf; switch (_Msg_Fmt) { case 8: _Msg_Content_Bytes = Encoding.BigEndianUnicode.GetBytes(_Msg_Content); break; case 15: //gb2312 _Msg_Content_Bytes = Encoding.GetEncoding("gb2312").GetBytes(_Msg_Content); break; case 0: //ascii case 3: //短信写卡操作 case 4: //二进制信息 default: _Msg_Content_Bytes = Encoding.ASCII.GetBytes(_Msg_Content); break; } _Msg_Length = (uint)_Msg_Content_Bytes.Length; _BodyLength = (int)(FixedBodyLength + 32 * _Dest_terminal_Id.Length + _Msg_Length); _Header = new MessageHeader ( (uint)(MessageHeader.Length + _BodyLength) , CMPP_Command_Id.CMPP_SUBMIT , _Sequence_Id ); } public byte[] ToBytes() { //Msg_Length Msg_Content int i = 0; byte[] bytes = new byte[MessageHeader.Length + _BodyLength]; byte[] buffer = _Header.ToBytes(); Buffer.BlockCopy(buffer, 0, bytes, 0, buffer.Length); i += MessageHeader.Length; //Msg_Id //8 [12,19] buffer = new byte[8]; buffer = BitConverter.GetBytes(_Msg_Id); Array.Reverse(buffer); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //10 //[24,33] //_Pk_total i += 8; bytes[i++] = (byte)_Pk_total; //[20,20] bytes[i++] = (byte)_Pk_number; //[21,21] bytes[i++] = (byte)_Registered_Delivery; //[22,22] bytes[i++] = (byte)_Msg_level; //[23,23] //Service_Id buffer = Encoding.ASCII.GetBytes(_Service_Id); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //10 //[24,33] //Fee_UserType i += 10; bytes[i++] = (byte)_Fee_UserType; //[34,34] //Fee_terminal_Id buffer = Encoding.ASCII.GetBytes(_Fee_terminal_Id); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //32 //[35,66] //Fee_terminal_type i += 32; bytes[i++] = (byte)_Fee_terminal_type; //[67,67] bytes[i++] = (byte)_TP_pId; //[68,68] bytes[i++] = (byte)_TP_udhi; //[69,69] bytes[i++] = (byte)_Msg_Fmt; //[70,70] //Msg_src buffer = Encoding.ASCII.GetBytes(_Msg_src); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //6 //[71,76] //FeeType i += 6; buffer = Encoding.ASCII.GetBytes(_FeeType); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //2 //[77,78] //FeeCode i += 2; buffer = Encoding.ASCII.GetBytes(_FeeCode); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //6 //[79,84] //ValId_Time i += 6; buffer = Encoding.ASCII.GetBytes(_ValId_Time); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //17 //[85,101] //At_Time i += 17; buffer = Encoding.ASCII.GetBytes(_At_Time); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //17 //[102,118] //Src_Id i += 17; buffer = Encoding.ASCII.GetBytes(_Src_Id); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //21 //[119,139] //DestUsr_tl i += 21; _DestUsr_tl = (uint)_Dest_terminal_Id.Length; bytes[i++] = (byte)_DestUsr_tl; //[140,140] //Dest_terminal_Id foreach (string s in _Dest_terminal_Id) { buffer = Encoding.ASCII.GetBytes(s); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); i += 32; } //Dest_terminal_type bytes[i++] = (byte)_Dest_terminal_type; //Msg_Length bytes[i++] = (byte)_Msg_Length; //Msg_Content //buffer = Encoding. Buffer.BlockCopy ( _Msg_Content_Bytes , 0 , bytes , i, _Msg_Content_Bytes.Length ); //LinkID i += (int)_Msg_Length; buffer = Encoding.ASCII.GetBytes(_LinkID); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //20 return bytes; } public ulong Msg_Id { get { return _Msg_Id; } set { _Msg_Id = value; } } public uint Pk_total { get { return _Pk_total; } set { _Pk_total = value; } } public uint Pk_number { get { return _Pk_number; } set { _Pk_number = value; } } public uint Registered_Delivery { get { return _Registered_Delivery; } set { _Registered_Delivery = value; } } public uint Msg_level { get { return _Msg_level; } set { _Msg_level = value; } } public string Service_Id { get { return _Service_Id; } set { _Service_Id = value; } } public uint Fee_UserType { get { return _Fee_UserType; } set { _Fee_UserType = value; } } public string Fee_terminal_Id { get { return _Fee_terminal_Id; } set { _Fee_terminal_Id = value; } } public uint Fee_terminal_type { get { return _Fee_terminal_type; } set { _Fee_terminal_type = value; } } public uint TP_pId { get { return _TP_pId; } set { _TP_pId = value; } } public uint TP_udhi { get { return _TP_udhi; } set { _TP_udhi = value; } } public uint Msg_Fmt { get { return _Msg_Fmt; } set { _Msg_Fmt = value; if (_Msg_Content != null) { SetHeader(); } } } public string Msg_src { get { return _Msg_src; } set { _Msg_src = value; } } public string FeeType { get { return _FeeType; } set { _FeeType = value; } } public string FeeCode { get { return _FeeCode; } set { _FeeCode = value; } } public string ValId_Time { get { return _ValId_Time; } set { _ValId_Time = value; } } public string At_Time { get { return _At_Time; } set { _At_Time = value; } } public string Src_Id { get { return _Src_Id; } set { _Src_Id = value; } } public uint DestUsr_tl { get { return _DestUsr_tl; } set { _DestUsr_tl = value; } } public string[] Dest_terminal_Id { get { return _Dest_terminal_Id; } set { _Dest_terminal_Id = value; } } public uint Dest_terminal_type { get { return _Dest_terminal_type; } set { _Dest_terminal_type = value; } } public uint Msg_Length { get { return _Msg_Length; } set { _Msg_Length = value; } } public string Msg_Content { get { return _Msg_Content; } set { _Msg_Content = value; SetHeader(); } } public string LinkId { get { return _LinkID; } set { _LinkID = value; } } public override string ToString() { return "[\r\n" + _Header.ToString() + "\r\n" + "\t" + string.Format ( "MessageBody:" + "{0}At_Time: {1}" + "{0}BodyLength: {2}" + "{0}Dest_terminal_Id: {3}" + "{0}Dest_terminal_type: {4}" + "{0}DestUsr_tl: {5}" + "{0}Fee_terminal_Id: {6}" + "{0}Fee_terminal_type: {7}" + "{0}Fee_UserType: {8}" + "{0}FeeCode: {9}" + "{0}FeeType: {10}" + "{0}LinkID: {11}" + "{0}Msg_Content: {12}" + "{0}Msg_Fmt: {13}" + "{0}Msg_Id: {14}" + "{0}Msg_Length: {15}" + "{0}Msg_level: {16}" + "{0}Msg_src: {17}" + "{0}Pk_number: {18}" + "{0}Pk_total: {19}" + "{0}Registered_Delivery: {20}" + "{0}Sequence_Id: {21}" + "{0}Service_Id: {22}" + "{0}Src_Id: {23}" + "{0}TP_pId: {24}" + "{0}TP_udhi: {25}" + "{0}ValId_Time: {26}" , "\r\n\t\t" , _At_Time , _BodyLength , String.Join(",", _Dest_terminal_Id) , _Dest_terminal_type , _DestUsr_tl , _Fee_terminal_Id , _Fee_terminal_type , _Fee_UserType , _FeeCode , _FeeType , _LinkID , _Msg_Content , _Msg_Fmt , _Msg_Id , _Msg_Length , _Msg_level , _Msg_src , _Pk_number , _Pk_total , _Registered_Delivery , _Sequence_Id , _Service_Id , _Src_Id , _TP_pId , _TP_udhi , _ValId_Time ) + "\r\n]"; } } public class CMPP_SUBMIT_RESP //: CMPP_Response { private MessageHeader _Header; private uint _Msg_Id; private uint _Result; public const int BodyLength = 8 + 4; public uint Msg_Id { get { return _Msg_Id; } } public uint Result { get { return _Result; } } public MessageHeader Header { get { return _Header; } } public CMPP_SUBMIT_RESP(byte[] bytes) { int i = 0; byte[] buffer = new byte[MessageHeader.Length]; Buffer.BlockCopy(bytes, 0, buffer, 0, buffer.Length); _Header = new MessageHeader(buffer); //Msg_Id i += MessageHeader.Length; buffer = new byte[8]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _Msg_Id = BitConverter.ToUInt32(buffer, 0); //Result i += 8; buffer = new byte[4]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _Result = BitConverter.ToUInt32(buffer, 0); } public override string ToString() { return "[\r\n" + _Header.ToString() + "\r\n" + "\t" + string.Format ( "MessageBody:" + "{0}Msg_Id: {1}" + "{0}Result: {2}" , "\r\n\t\t" , _Msg_Id , _Result ) + "\r\n]"; } } public class CMPP_DELIVER //: CMPP_Request { public ulong Msg_Id { get { return _Msg_Id; } } public string Dest_Id { get { return _Dest_Id; } } public string Service_Id { get { return _Service_Id; } } public uint TP_pid { get { return _TP_pid; } } public uint TP_udhi { get { return _TP_udhi; } } public uint Msg_Fmt { get { return _Msg_Fmt; } } public string Src_terminal_Id { get { return _Src_terminal_Id; } } public uint Src_terminal_type { get { return _Src_terminal_type; } } public uint Registered_Delivery { get { return _Registered_Delivery; } } public uint Msg_Length { get { return _Msg_Length; } } public string Msg_Content { get { return _Msg_Content; } } public string LinkId { get { return _LinkID; } } private ulong _Msg_Id; // 8 Unsigned Integer 信息标识。 // 生成算法如下: // 采用64位(8字节)的整数: // (1)????????? 时间(格式为MMDDHHMMSS,即月日时分秒):bit64~bit39,其中 // bit64~bit61:月份的二进制表示; // bit60~bit56:日的二进制表示; // bit55~bit51:小时的二进制表示; // bit50~bit45:分的二进制表示; // bit44~bit39:秒的二进制表示; // (2)????????? 短信网关代码:bit38~bit17,把短信网关的代码转换为整数填写到该字段中; // (3)????????? 序列号:bit16~bit1,顺序增加,步长为1,循环使用。 // 各部分如不能填满,左补零,右对齐。 private string _Dest_Id; // 21 Octet String 目的号码。 // SP的服务代码,一般4--6位,或者是前缀为服务代码的长号码;该号码是手机用户短消息的被叫号码。 private string _Service_Id; // 10 Octet String 业务标识,是数字、字母和符号的组合。 private uint _TP_pid; // 1 Unsigned Integer GSM协议类型。详细解释请参考GSM03.40中的9.2.3.9。 private uint _TP_udhi; // 1 Unsigned Integer GSM协议类型。详细解释请参考GSM03.40中的9.2.3.23,仅使用1位,右对齐。 private uint _Msg_Fmt; // 1 Unsigned Integer 信息格式: // 0:ASCII串; // 3:短信写卡操作; // 4:二进制信息; // 8:UCS2编码; // 15:含GB汉字。 private string _Src_terminal_Id; // 32 Octet String 源终端MSISDN号码(状态报告时填为CMPP_SUBMIT消息的目的终端号码)。 private uint _Src_terminal_type; // 1 Unsigned Integer 源终端号码类型,0:真实号码;1:伪码。 private uint _Registered_Delivery; // 1 Unsigned Integer 是否为状态报告: // 0:非状态报告; // 1:状态报告。 private uint _Msg_Length; // 1 Unsigned Integer 消息长度,取值大于或等于0。 private string _Msg_Content; // Msg_length Octet String 消息内容。 private string _LinkID; // 20 Octet String 点播业务使用的LinkID,非点播类业务的MT流程不使用该字段。 private MessageHeader _Header; public MessageHeader Header { get { return _Header; } } public const int FixedBodyLength = 8 // Msg_Id Unsigned Integer 信息标识。 // 生成算法如下: // 采用64位(8字节)的整数: // (1)????????? 时间(格式为MMDDHHMMSS,即月日时分秒):bit64~bit39,其中 // bit64~bit61:月份的二进制表示; // bit60~bit56:日的二进制表示; // bit55~bit51:小时的二进制表示; // bit50~bit45:分的二进制表示; // bit44~bit39:秒的二进制表示; // (2)????????? 短信网关代码:bit38~bit17,把短信网关的代码转换为整数填写到该字段中; // (3)????????? 序列号:bit16~bit1,顺序增加,步长为1,循环使用。 // 各部分如不能填满,左补零,右对齐。 + 21 // Dest_Id Octet String 目的号码。 // SP的服务代码,一般4--6位,或者是前缀为服务代码的长号码;该号码是手机用户短消息的被叫号码。 + 10 // Service_Id Octet String 业务标识,是数字、字母和符号的组合。 + 1 // TP_pid Unsigned Integer GSM协议类型。详细解释请参考GSM03.40中的9.2.3.9。 + 1 // TP_udhi Unsigned Integer GSM协议类型。详细解释请参考GSM03.40中的9.2.3.23,仅使用1位,右对齐。 + 1 // Msg_Fmt Unsigned Integer 信息格式: // 0:ASCII串; // 3:短信写卡操作; // 4:二进制信息; // 8:UCS2编码; // 15:含GB汉字。 + 32 // Src_terminal_Id Octet String 源终端MSISDN号码(状态报告时填为CMPP_SUBMIT消息的目的终端号码)。 + 1 // Src_terminal_type Unsigned Integer 源终端号码类型,0:真实号码;1:伪码。 + 1 // Registered_Delivery Unsigned Integer 是否为状态报告: // 0:非状态报告; // 1:状态报告。 + 1 // Msg_Length Unsigned Integer 消息长度,取值大于或等于0。 //Msg_length // Msg_Content Octet String 消息内容。 + 20; // LinkID Octet String 点播业务使用的LinkID,非点播类业务的MT流程不使用该字段。 private int _BodyLength; public int BodyLength { get { return _BodyLength; } } public CMPP_DELIVER(byte[] bytes) { int i = 0; byte[] buffer = new byte[MessageHeader.Length]; Buffer.BlockCopy(bytes, 0, buffer, 0, MessageHeader.Length); _Header = new MessageHeader(buffer); //Msg_Id 8 i += MessageHeader.Length; buffer = new byte[8]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _Msg_Id = BitConverter.ToUInt64(buffer, 0); string s = null; //Dest_Id 21 i += 8; buffer = new byte[21]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); s = Encoding.ASCII.GetString(buffer).Trim(); s = s.Substring(0, s.IndexOf('\0')); _Dest_Id = s; //Service_Id 20 i += 21; buffer = new byte[10]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); s = Encoding.ASCII.GetString(buffer).Trim(); s = s.Substring(0, s.IndexOf('\0')); _Service_Id = s; //TP_pid 1 i += 10; _TP_pid = (uint)bytes[i++]; _TP_udhi = (uint)bytes[i++]; _Msg_Fmt = (uint)bytes[i++]; //Src_terminal_Id 32 buffer = new byte[32]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); s = Encoding.ASCII.GetString(buffer).Trim(); s = s.Substring(0, s.IndexOf('\0')); _Src_terminal_Id = s; //Src_terminal_type 1 i += 32; _Src_terminal_type = (uint)bytes[i++]; _Registered_Delivery = (uint)bytes[i++]; _Msg_Length = (uint)bytes[i++]; //Msg_Content buffer = new byte[_Msg_Length]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); switch (_Msg_Fmt) { case 8: _Msg_Content = Encoding.BigEndianUnicode.GetString(buffer).Trim(); break; case 15: //gb2312 _Msg_Content = Encoding.GetEncoding("gb2312").GetString(buffer).Trim(); break; case 0: //ascii case 3: //短信写卡操作 case 4: //二进制信息 default: _Msg_Content = Encoding.ASCII.GetString(buffer).ToString(); break; } //Linkid 20 i += (int)_Msg_Length; buffer = new byte[20]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); s = Encoding.ASCII.GetString(buffer).Trim(); s = s.Substring(0, s.IndexOf('\0')); _LinkID = s; } public byte[] ToBytes() { //Msg_Length Msg_Content byte[] buf; switch (_Msg_Fmt) { case 8: buf = Encoding.BigEndianUnicode.GetBytes(_Msg_Content); break; case 15: //gb2312 buf = Encoding.GetEncoding("gb2312").GetBytes(_Msg_Content); break; case 0: //ascii case 3: //短信写卡操作 case 4: //二进制信息 default: buf = Encoding.ASCII.GetBytes(_Msg_Content); break; } _Msg_Length = (uint)buf.Length; _BodyLength = FixedBodyLength + (int)_Msg_Length; byte[] bytes = new byte[MessageHeader.Length + _BodyLength]; int i = 0; byte[] buffer = null; //header 12 _Header = new MessageHeader ( (uint)(MessageHeader.Length + _BodyLength) , CMPP_Command_Id.CMPP_DELIVER , 0 ); //Msg_Id 8 i += MessageHeader.Length; buffer = new Byte[8]; buffer = BitConverter.GetBytes(_Msg_Id); Array.Reverse(buffer); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //Dest_Id 21 i += 8; buffer = new byte[21]; buffer = Encoding.ASCII.GetBytes(_Dest_Id); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //Service_Id 10 i += 21; buffer = new byte[10]; buffer = Encoding.ASCII.GetBytes(_Service_Id); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //TP_pid 1 i += 10; bytes[i++] = (byte)_TP_pid; bytes[i++] = (byte)_TP_udhi; bytes[i++] = (byte)_Msg_Fmt; //Src_terminal_Id 32 buffer = new byte[32]; buffer = Encoding.ASCII.GetBytes(_Src_terminal_Id); Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //Src_terminal_type 1 i += 32; bytes[i++] = (byte)_Src_terminal_type; bytes[i++] = (byte)_Registered_Delivery; bytes[i++] = (byte)_Msg_Length; //Msg_Content Buffer.BlockCopy(buf, 0, bytes, i, buf.Length); //LinkID i += (int)_Msg_Length; return bytes; } public override string ToString() { return "[\r\n" + _Header.ToString() + "\r\n" + "\t" + string.Format ( "MessageBody:" + "{0}BodyLength: {1}" + "{0}Dest_Id: {2}" + "{0}LinkID: {3}" + "{0}Msg_Content: {4}" + "{0}Msg_Fmt: {5}" + "{0}Msg_Id: {6}" + "{0}Msg_Length: {7}" + "{0}Registered_Delivery: {8}" + "{0}Service_Id: {9}" + "{0}Src_terminal_Id: {10}" + "{0}Src_terminal_type: {11}" + "{0}TP_pid: {12}" + "{0}TP_udhi: {13}" , "\r\n\t\t" , _BodyLength , _Dest_Id , _LinkID , _Msg_Content , _Msg_Fmt , _Msg_Id , _Msg_Length , _Registered_Delivery , _Service_Id , _Src_terminal_Id , _Src_terminal_type , _TP_pid , _TP_udhi ) + "\r\n]"; } } public class CMPP_DELIVER_RESP //: CMPP_Response { private MessageHeader _Header; private ulong _Msg_Id; private uint _Result; public const int Bodylength = 8 + 4; public CMPP_DELIVER_RESP(ulong Msg_Id, uint Result) { _Msg_Id = Msg_Id; _Result = Result; } public byte[] ToBytes() { int i = 0; byte[] bytes = new byte[MessageHeader.Length + Bodylength]; byte[] buffer = new byte[MessageHeader.Length]; //header _Header = new MessageHeader ( MessageHeader.Length + Bodylength , CMPP_Command_Id.CMPP_DELIVER_RESP , 0 ); buffer = _Header.ToBytes(); Buffer.BlockCopy(buffer, 0, bytes, 0, buffer.Length); i += MessageHeader.Length; //msg_id 8 buffer = BitConverter.GetBytes(_Msg_Id); Array.Reverse(buffer); buffer.CopyTo(bytes, i); //result 4 i += 8; buffer = BitConverter.GetBytes(_Result); Array.Reverse(buffer); buffer.CopyTo(bytes, i); return bytes; } public override string ToString() { return _Header.ToString() + "\r\n" + string.Format ( "[\r\nMessageBody:" + "\r\n\tMsg_Id: {0}" + "\r\n\tResult: {1}" + "\r\n]" , _Msg_Id , _Result ); } } public class CMPP_Msg_Content //状态报告 { public const int BodyLength = 8 + 7 + 10 + 10 + 32 + 4; private uint _Msg_Id; // 8 Unsigned Integer 信息标识。SP提交短信(CMPP_SUBMIT)操作时,与SP相连的ISMG产生的Msg_Id。 private string _Stat; // 7 Octet String 发送短信的应答结果,含义详见表一。SP根据该字段确定CMPP_SUBMIT消息的处理状态。 private string _Submit_time; // 10 Octet String YYMMDDHHMM(YY为年的后两位00-99,MM:01-12,DD:01-31,HH:00-23,MM:00-59)。 private string _Done_time; // 10 Octet String YYMMDDHHMM。 public CMPP_Msg_Content(byte[] bytes) { if (bytes.Length == BodyLength) { int i = 0; //_Msg_Id 8 byte[] buffer = new byte[8]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _Msg_Id = BitConverter.ToUInt32(buffer, 0); //_Stat 7 i += 8; buffer = new byte[7]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); _Stat = Encoding.ASCII.GetString(buffer); //_Submit_time 10 i += 7; buffer = new byte[10]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); _Submit_time = Encoding.ASCII.GetString(buffer); //_Done_time 10 i += 10; buffer = new byte[10]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); _Submit_time = Encoding.ASCII.GetString(buffer); //Dest_terminal_Id 32 i += 10; buffer = new byte[32]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); _Dest_terminal_Id = Encoding.ASCII.GetString(buffer); //SMSC_sequence 4 i += 32; buffer = new byte[4]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _SMSC_sequence = BitConverter.ToUInt32(buffer, 0); } } public uint Msg_Id { get { return _Msg_Id; } set { _Msg_Id = value; } } public string Stat { get { return _Stat; } set { _Stat = value; } } public string Submit_time { get { return _Submit_time; } set { _Submit_time = value; } } public string Done_time { get { return _Done_time; } set { _Done_time = value; } } public string Dest_terminal_Id { get { return _Dest_terminal_Id; } set { _Dest_terminal_Id = value; } } public uint SMSC_sequence { get { return _SMSC_sequence; } set { _SMSC_sequence = value; } } private string _Dest_terminal_Id; // 32 Octet String 目的终端MSISDN号码(SP发送CMPP_SUBMIT消息的目标终端)。 private uint _SMSC_sequence; // 4 Unsigned Integer 取自SMSC发送状态报告的消息体中的消息标识。 public override string ToString() { return string.Format ( "[\r\nMessageBody:" + "\r\n\tBodyLength: {0}" + "\r\n\tDest_terminal_Id: {1}" + "\r\n\tDone_time: {2}" + "\r\n\tMsg_Id: {3}" + "\r\n\tSMSC_sequence: {4}" + "\r\n\tStat: {5}" + "\r\n\tSubmit_time: {6}" + "\r\n]" , CMPP_Msg_Content.BodyLength , _Dest_terminal_Id , _Done_time , _Msg_Id , _SMSC_sequence , _Stat , _Submit_time ); } } public class CMPP_QUERY //: CMPP_Request { private MessageHeader _Header; private string _Time; // 8 Octet String 时间YYYYMMDD(精确至日)。 private uint _Query_Type; // 1 Unsigned Integer 查询类别: // 0:总数查询; // 1:按业务类型查询。 private string _Query_Code; // 10 Octet String 查询码。 // 当Query_Type为0时,此项无效;当Query_Type为1时,此项填写业务类型Service_Id.。 private string _Reserve; // 8 Octet String 保留。 public MessageHeader Header { get { return _Header; } } public string Time { get { return _Time; } } public uint Query_Type { get { return _Query_Type; } } public string Query_Code { get { return _Query_Code; } } public string Reserve { get { return _Reserve; } } public const int BodyLength = 8 + 1 + 10 + 8; public CMPP_QUERY ( DateTime Time , uint Query_Type , string Query_Code , string Reserve , uint Sequence_Id ) { _Time = Utility.Get_YYYYMMDD_String(Time); _Query_Type = Query_Type; _Query_Code = Query_Code; _Reserve = Reserve; _Header = new MessageHeader ( (uint)(MessageHeader.Length + BodyLength) , CMPP_Command_Id.CMPP_QUERY , Sequence_Id ); } public byte[] ToBytes() { int i = 0; byte[] bytes = new byte[MessageHeader.Length + BodyLength]; //header byte[] buffer = new byte[MessageHeader.Length]; buffer = _Header.ToBytes(); buffer.CopyTo(bytes, 0); //Time 8 i += MessageHeader.Length; buffer = new byte[10]; buffer = Encoding.ASCII.GetBytes(_Time); buffer.CopyTo(bytes, i); //Query_Type 1 i += 8; bytes[i++] = (byte)_Query_Type; //Query_Code 10 buffer = new byte[10]; buffer = Encoding.ASCII.GetBytes(_Query_Code); buffer.CopyTo(bytes, i); //Reserve 8 i += 10; buffer = new byte[8]; buffer = Encoding.ASCII.GetBytes(_Reserve); buffer.CopyTo(bytes, i); return bytes; } public override string ToString() { return "[\r\n" + _Header.ToString() + "\r\n" + "\t" + string.Format ( "MessageBody:" + "{0}Query_Code: {1}" + "{0}Query_Type: {2}" + "{0}Reserve: {3}" + "{0}Time: {4}" , "\r\n\t\t" , _Query_Code , _Query_Type , _Reserve , _Time ) + "\r\n]"; } } public class CMPP_QUERY_RESP { public MessageHeader Header { get { return _Header; } } public string Time { get { return _Time; } } public uint Query_Type { get { return _Query_Type; } } public string Query_Code { get { return _Query_Code; } } public uint Mt_TlMsg { get { return _MT_TLMsg; } } public uint Mt_Tlusr { get { return _MT_Tlusr; } } public uint Mt_Scs { get { return _MT_Scs; } } public uint MT_WT { get { return _MT_WT; } } public uint MT_FL { get { return _MT_FL; } } public uint MO_Scs { get { return _MO_Scs; } } public uint MO_WT { get { return _MO_WT; } } public uint MO_FL { get { return _MO_FL; } } private MessageHeader _Header; private string _Time; // 8 Octet String 时间(精确至日)。 private uint _Query_Type; // 1 Unsigned Integer 查询类别: // 0:总数查询; // 1:按业务类型查询。 private string _Query_Code; // 10 Octet String 查询码。 private uint _MT_TLMsg; // 4 Unsigned Integer 从SP接收信息总数。 private uint _MT_Tlusr; // 4 Unsigned Integer 从SP接收用户总数。 private uint _MT_Scs; // 4 Unsigned Integer 成功转发数量。 private uint _MT_WT; // 4 Unsigned Integer 待转发数量。 private uint _MT_FL; // 4 Unsigned Integer 转发失败数量。 private uint _MO_Scs; // 4 Unsigned Integer 向SP成功送达数量。 private uint _MO_WT; // 4 Unsigned Integer 向SP待送达数量。 private uint _MO_FL; // 4 Unsigned Integer 向SP送达失败数量。 public const int BodyLength = 8 // Octet String 时间(精确至日)。 + 1 // Unsigned Integer 查询类别: // 0:总数查询; // 1:按业务类型查询。 + 10 // Octet String 查询码。 + 4 // Unsigned Integer 从SP接收信息总数。 + 4 // Unsigned Integer 从SP接收用户总数。 + 4 // Unsigned Integer 成功转发数量。 + 4 // Unsigned Integer 待转发数量。 + 4 // Unsigned Integer 转发失败数量。 + 4 // Unsigned Integer 向SP成功送达数量。 + 4 // Unsigned Integer 向SP待送达数量。 + 4; // Unsigned Integer 向SP送达失败数量。 public CMPP_QUERY_RESP(byte[] bytes) { int i = 0; //header 12 byte[] buffer = new byte[MessageHeader.Length]; Buffer.BlockCopy(bytes, 0, buffer, 0, buffer.Length); _Header = new MessageHeader(buffer); //Time 8 i += MessageHeader.Length; buffer = new byte[8]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); _Time = Encoding.ASCII.GetString(buffer); //Query_Type 1 i += 8; _Query_Type = (uint)bytes[i++]; //Query_Code 10 buffer = new byte[10]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); _Query_Code = Encoding.ASCII.GetString(buffer); //MT_TLMsg 4 i += 10; buffer = new byte[4]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _MT_TLMsg = BitConverter.ToUInt32(buffer, 0); //MT_Tlusr 4 i += 4; buffer = new byte[4]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _MT_Tlusr = BitConverter.ToUInt32(buffer, 0); //MT_Scs 4 i += 4; buffer = new byte[4]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _MT_Scs = BitConverter.ToUInt32(buffer, 0); //MT_WT 4 i += 4; buffer = new byte[4]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _MT_WT = BitConverter.ToUInt32(buffer, 0); //MT_FL 4 i += 4; buffer = new byte[4]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _MT_FL = BitConverter.ToUInt32(buffer, 0); //MO_Scs 4 i += 4; buffer = new byte[4]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _MO_Scs = BitConverter.ToUInt32(buffer, 0); //MO_WT 4 i += 4; buffer = new byte[4]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _MO_WT = BitConverter.ToUInt32(buffer, 0); //MO_FL 4 i += 4; buffer = new byte[4]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); Array.Reverse(buffer); _MO_FL = BitConverter.ToUInt32(buffer, 0); } public override string ToString() { return "[\r\n" + _Header.ToString() + "\r\n" + "\t" + string.Format ( "MessageBody:" + "{0}BodyLength: {1}" + "{0}MO_FL: {2}" + "{0}MO_Scs: {3}" + "{0}MO_WT: {4}" + "{0}MT_FL: {5}" + "{0}MT_Scs: {6}" + "{0}MT_TLMsg: {7}" + "{0}MT_Tlusr: {8}" + "{0}MT_WT: {9}" + "{0}Query_Code: {10}" + "{0}Query_Type: {11}" + "{0}Time: {12}" , "\r\n\t\t" , CMPP_QUERY_RESP.BodyLength , _MO_FL , _MO_Scs , _MO_WT , _MT_FL , _MT_Scs , _MT_TLMsg , _MT_Tlusr , _MT_WT , _Query_Code , _Query_Type , _Time ) + "\r\n]"; } } public class CMPP_ACTIVE_TEST { private MessageHeader _Header; public MessageHeader Header { get { return _Header; } } public CMPP_ACTIVE_TEST(uint Sequence_Id) { _Header = new MessageHeader ( MessageHeader.Length , CMPP_Command_Id.CMPP_ACTIVE_TEST , Sequence_Id ); } public byte[] ToBytes() { return _Header.ToBytes(); } public override string ToString() { return _Header.ToString(); } } public class CMPP_ACTIVE_TEST_RESP { private MessageHeader _Header; private byte _Reserved; public byte Reserved { get { return _Reserved; } } public MessageHeader Header { get { return _Header; } } public CMPP_ACTIVE_TEST_RESP(byte[] bytes) { int i = 0; //header byte[] buffer = new byte[MessageHeader.Length]; Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length); _Header = new MessageHeader(buffer); //Reserved 1 i += MessageHeader.Length; _Reserved = bytes[i]; } public byte[] ToBytes() { return _Header.ToBytes(); } public override string ToString() { return "[\r\n" + _Header.ToString() + "\r\n" + "]"; } } } //CMPP Client namespace Microshaoft.CMPP { using System; using System.Net.Sockets; using System.Threading; using Microshaoft.CMPP.Messages; public class MessageEventArgs : EventArgs { private byte[] _HeaderData; private MessageHeader _Header; private byte[] _BodyData; public byte[] MessageBodyData { get { return _BodyData; } } public MessageHeader Header { get { return _Header; } } public byte[] MessageHeaderData { get { return _HeaderData; } } public MessageEventArgs(byte[] bytes) { _HeaderData = new byte[MessageHeader.Length]; Buffer.BlockCopy(bytes, 0, _HeaderData, 0, MessageHeader.Length); _Header = new MessageHeader(_HeaderData); _BodyData = new byte[_Header.Total_Length - MessageHeader.Length]; Buffer.BlockCopy(bytes, MessageHeader.Length, _BodyData, 0, _BodyData.Length); } } public class Client { private string _Host; private int _Port; private string _Source_Addr; private string _Password; private TcpClient tc; private bool _IsConnected; public bool IsConnected { get { return _IsConnected; } } private NetworkStream _NetworkStream; private static object _SyncLockObject = new object(); public delegate void MessageEventHandler(Client Sender, MessageEventArgs e); //public delegate void RequestEventHandler(Client Sender, ResponseEventArgs e); public event MessageEventHandler ResponseMessageReceive; public event MessageEventHandler BeforeRequestMessageSend; public event MessageEventHandler AfterRequestMessageSended; private Thread _ReadResponseThread; private void OnBeforeRequestMessageSend(byte[] data) { if (BeforeRequestMessageSend != null) { BeforeRequestMessageSend(this, new MessageEventArgs(data)); } } private void OnAfterRequestMessageSended(byte[] data) { if (AfterRequestMessageSended != null) { AfterRequestMessageSended(this, new MessageEventArgs(data)); } } private void WriteToStreamWithLock(byte[] data, NetworkStream Stream) { OnBeforeRequestMessageSend(data); lock (_SyncLockObject) { Utility.WriteToStream(data, Stream); } OnAfterRequestMessageSended(data); } private byte[] ReadStreamToBytesWithLock(int Length, NetworkStream Stream) { lock (_SyncLockObject) { return Utility.ReadStreamToBytes(Length, Stream); } } public void Terminate(uint SequenceId) { MessageHeader terminate = new MessageHeader ( MessageHeader.Length , CMPP_Command_Id.CMPP_TERMINATE , SequenceId ); Console.WriteLine("Request:\r\n{0}", terminate.ToString()); WriteToStreamWithLock(terminate.ToBytes(), _NetworkStream); StartRun(); } public void ActiveTest(uint SequenceId) { MessageHeader activeTest = new MessageHeader ( MessageHeader.Length , CMPP_Command_Id.CMPP_ACTIVE_TEST , SequenceId ); //byte[] activeTest = activeTest.ToBytes(); Console.WriteLine("Request:\r\n{0}", activeTest.ToString()); WriteToStreamWithLock(activeTest.ToBytes(), _NetworkStream); StartRun(); } public bool Connect ( string Host , int Port , string UserID , string Password , uint SequenceId ) { _Host = Host; _Port = Port; _Source_Addr = UserID; _Password = Password; DateTime ts = DateTime.Now; CMPP_CONNECT connect = new CMPP_CONNECT ( _Source_Addr , _Password , ts , 1 , SequenceId ); Console.WriteLine("Request:\r\n{0}", connect.ToString()); tc = new TcpClient(); tc.Connect(_Host, _Port); _NetworkStream = tc.GetStream(); WriteToStreamWithLock(connect.ToBytes(), _NetworkStream); Run(ref _IsConnected); return _IsConnected; } public void Query ( DateTime Time , uint QueryType , string QueryCode , string Reserve , uint SequenceId ) { CMPP_QUERY query = new CMPP_QUERY ( Time , QueryType , QueryCode , Reserve , SequenceId ); Console.WriteLine("Request:\r\n{0}", query.ToString()); WriteToStreamWithLock(query.ToBytes(), _NetworkStream); StartRun(); } public void Submit ( ulong MsgId , uint RegisteredDelivery , string FeeTerminalId , string[] DestTerminalId , string MsgContent , uint SequenceId ) { //这里的字段根据需要设定 CMPP_SUBMIT submit = new CMPP_SUBMIT(SequenceId); submit.Msg_Id = MsgId; // uint _Msg_Id; // 8 Unsigned Integer 信息标识。 submit.Pk_total = 1; // uint _Pk_total; // 1 Unsigned Integer 相同Msg_Id的信息总条数,从1开始。 submit.Pk_number = 0; // uint _Pk_number; // 1 Unsigned Integer 相同Msg_Id的信息序号,从1开始。 submit.Registered_Delivery = RegisteredDelivery; // uint _Registered_Delivery; // 1 Unsigned Integer 是否要求返回状态确认报告: // // 0:不需要; // // 1:需要。 submit.Msg_level = 1; // uint _Msg_level; // 1 Unsigned Integer 信息级别。 submit.Service_Id = "abcdefghij"; // string _Service_Id; // 10 Octet String 业务标识,是数字、字母和符号的组合。 submit.Fee_UserType = 3; // uint _Fee_UserType; // 1 Unsigned Integer 计费用户类型字段: // // 0:对目的终端MSISDN计费; // // 1:对源终端MSISDN计费; // // 2:对SP计费; // // 3:表示本字段无效,对谁计费参见Fee_terminal_Id字段。 submit.Fee_terminal_Id = FeeTerminalId; // string _Fee_terminal_Id; // 32 Octet String 被计费用户的号码,当Fee_UserType为3时该值有效,当Fee_UserType为0、1、2时该值无意义。 submit.Fee_terminal_type = 0; // uint _Fee_terminal_type; // 1 Unsigned Integer 被计费用户的号码类型,0:真实号码;1:伪码。 submit.TP_pId = 0; // uint _TP_pId; // 1 Unsigned Integer GSM协议类型。详细是解释请参考GSM03.40中的9.2.3.9。 submit.TP_udhi = 0; // uint _TP_udhi; // 1 Unsigned Integer GSM协议类型。详细是解释请参考GSM03.40中的9.2.3.23,仅使用1位,右对齐。 submit.Msg_Fmt = 15; // uint _Msg_Fmt; // 1 Unsigned Integer 信息格式: // // 0:ASCII串; // // 3:短信写卡操作; // // 4:二进制信息; // // 8:UCS2编码; // // 15:含GB汉字...... submit.Msg_src = _Source_Addr; // string _Msg_src; // 6 Octet String 信息内容来源(SP_Id)。 submit.FeeType = "02"; // string _FeeType; // 2 Octet String 资费类别: // // 01:对"计费用户号码"免费; // // 02:对"计费用户号码"按条计信息费; // // 03:对"计费用户号码"按包月收取信息费。 submit.FeeCode = "100"; // string _FeeCode; // 6 Octet String 资费代码(以分为单位)。 submit.ValId_Time = Utility.Get_MMDDHHMMSS_String(DateTime.Now.AddHours(2)) + "032+"; // string _ValId_Time; // 17 Octet String 存活有效期,格式遵循SMPP3.3协议。 submit.At_Time = Utility.Get_MMDDHHMMSS_String(DateTime.Now) + "032+"; // string _At_Time; // 17 Octet String 定时发送时间,格式遵循SMPP3.3协议。 //spnum submit.Src_Id = ""; // string _Src_Id; // 21 Octet String 源号码。SP的服务代码或前缀为服务代码的长号码, 网关将该号码完整的填到SMPP协议Submit_SM消息相应的source_addr字段,该号码最终在用户手机上显示为短消息的主叫号码。 submit.Dest_terminal_Id = DestTerminalId; //new string[] {"1391xxx1138", "1391xxx1137"}; // string[] _Dest_terminal_Id; // 32*DestUsr_tl Octet String 接收短信的MSISDN号码。 submit.DestUsr_tl = (uint)submit.Dest_terminal_Id.Length; // uint _DestUsr_tl; // 1 Unsigned Integer 接收信息的用户数量(小于100个用户)。 submit.Dest_terminal_type = 0; // uint _Dest_terminal_type; // 1 Unsigned Integer 接收短信的用户的号码类型,0:真实号码;1:伪码。 submit.Msg_Fmt = 15; // uint _Msg_Length; // 1 Unsigned Integer 信息长度(Msg_Fmt值为0时:<160个字节;其它<=140个字节),取值大于或等于0。 submit.Msg_Content = MsgContent; //"大家好!这是一个短信群发测试!"; // string _Msg_Content; // Msg_length Octet String 信息内容。 submit.LinkId = ""; // string _LinkID; // 20 Octet String 点播业务使用的LinkID,非点播类业务的MT流程不使用该字段。 Console.WriteLine("Request:\r\n{0}", submit.ToString()); WriteToStreamWithLock(submit.ToBytes(), _NetworkStream); StartRun(); } bool _b = false; private bool _IsStarted = false; public void StartRun() { if (!_IsStarted) { _IsStarted = true; if (_ReadResponseThread == null) { _ReadResponseThread = new Thread(new ThreadStart(Run)); } if (_ReadResponseThread.ThreadState == ThreadState.Unstarted) { _ReadResponseThread.Start(); } } } private void Run() { Run(ref _b); } private void Run(ref bool BreakFlag) { while (!BreakFlag) { if (_NetworkStream.CanRead) { if (_NetworkStream.DataAvailable) { byte[] buffer = new byte[MessageHeader.Length]; //Header buffer = ReadStreamToBytesWithLock(MessageHeader.Length, _NetworkStream); MessageHeader header = new MessageHeader(buffer); byte[] bytes = new byte[header.Total_Length]; Buffer.BlockCopy(buffer, 0, bytes, 0, buffer.Length); int l = (int)header.Total_Length - MessageHeader.Length; if (l > 0) { buffer = ReadStreamToBytesWithLock(l, _NetworkStream); Buffer.BlockCopy ( buffer , 0 , bytes , MessageHeader.Length , buffer.Length ); } if (header.Command_Id == CMPP_Command_Id.CMPP_CONNECT_RESP) { CMPP_CONNECT_RESP connect_resp = new CMPP_CONNECT_RESP(bytes); _IsConnected = connect_resp.Status == 0; } else if (header.Command_Id == CMPP_Command_Id.CMPP_TERMINATE_RESP) { _b = true; } if (ResponseMessageReceive != null) { ResponseMessageReceive(this, new MessageEventArgs(bytes)); } } } Thread.Sleep(100); } if (_b) { _NetworkStream.Close(); _NetworkStream.Dispose(); _NetworkStream = null; } } } } //测试程序 namespace Test { using System; using System.Text; using Microshaoft.CMPP; using Microshaoft.CMPP.Messages; class ConsoleApplication { static void Main() { ConsoleApplication a = new ConsoleApplication(); Client c = new Client(); c.ResponseMessageReceive += new Client.MessageEventHandler(a.c_ResponseMessageReceive); c.AfterRequestMessageSended += new Client.MessageEventHandler(a.c_AfterRequestMessageSended); Console.WriteLine("press 'q' to exit this programe!"); uint i = 0; //Sequence_Id header ulong l = 0; //Msg_Id body if (c.Connect("localhost", 7891, "901234", "1234", ++i)) { c.Submit ( ++l , 1 , "1391xxx1138" , new string[] { "13911234567", "13911234567" } , "卧鼠藏虫 身披七彩祥云 脚踏金甲圣衣" , ++i ); c.Query(DateTime.Parse("2005-1-1"), 1, "001", "", ++i); c.Query(DateTime.Parse("2005-1-1"), 1, "001", "", ++i); c.ActiveTest(++i); c.Submit ( ++l , 1 , "1391xxx1138" , new string[] { "13911234567" } , "欲穷千里目 粒粒皆辛苦" , ++i ); } string s; while ((s = Console.ReadLine()) != "q") { if (c.IsConnected) { if (s.Length > 0) { c.Submit ( ++l , 1 , "1391xxx1138" , new string[] { "13911234567", "13911234567" } , s , ++i ); //Console.WriteLine("Request: Sequence_Id: {0},Msg_Id: {1}, Content: \"{2}\"", i, l, s); } else { Console.WriteLine("you can submit your SMS at here,or press 'q' to exit this programe!"); } } } if (c.IsConnected) { c.Terminate(++i); } Console.ReadLine(); } private void c_ResponseMessageReceive(Client Sender, MessageEventArgs e) { MessageHeader header = e.Header; //PrintHeader(header); byte[] bytes = new byte[header.Total_Length]; header.ToBytes().CopyTo(bytes, 0); e.MessageBodyData.CopyTo(bytes, MessageHeader.Length); string s = ""; if (header.Command_Id == CMPP_Command_Id.CMPP_ACTIVE_TEST_RESP) { CMPP_ACTIVE_TEST_RESP response = new CMPP_ACTIVE_TEST_RESP(bytes); //Console.WriteLine(response.Reserved); s = response.ToString(); } else if (header.Command_Id == CMPP_Command_Id.CMPP_CONNECT_RESP) { CMPP_CONNECT_RESP response = new CMPP_CONNECT_RESP(bytes); s = response.ToString(); } else if (header.Command_Id == CMPP_Command_Id.CMPP_QUERY_RESP) { CMPP_QUERY_RESP response = new CMPP_QUERY_RESP(bytes); s = response.ToString(); } else if (header.Command_Id == CMPP_Command_Id.CMPP_SUBMIT_RESP) { CMPP_SUBMIT_RESP response = new CMPP_SUBMIT_RESP(bytes); s = response.ToString(); } else if (header.Command_Id == CMPP_Command_Id.CMPP_TERMINATE_RESP) { s = String.Format("good bye"); } //上行短信 else if (header.Command_Id == CMPP_Command_Id.CMPP_DELIVER) { CMPP_DELIVER response = new CMPP_DELIVER(bytes); if (response.Registered_Delivery == 0) //普通短信 { s = String.Format("收到普通短信: \n{0}", response.ToString()); } else //该模拟器不能自动生成状态报告再下发!请自行键入下面短信内容后,发送 //状态报告短信: 00000001DELIVRD031213505003121350501391xxx11381391xxx11381391xx11380001 { CMPP_Msg_Content x = new CMPP_Msg_Content(Encoding.ASCII.GetBytes(response.Msg_Content)); s = x.ToString(); } } Console.WriteLine("Response Received:\r\n" + s + "\n"); } public void PrintHeader(MessageHeader Header) { Console.WriteLine("Response: Sequence_Id: {0}!", Header.Sequence_Id); Console.WriteLine("Total_Length: {0}!", Header.Total_Length); Console.WriteLine("Command_Id: {0}!", Header.Command_Id); } private void c_AfterRequestMessageSended(Client Sender, MessageEventArgs e) { Console.WriteLine("Sended {0} Request!", e.Header.Command_Id); //Console.ReadLine(); } } }