怎么样通过.NetC#C#实现中国移动CMPP v3.0 ISMG SP收发短信的客户端?

怎么样通过.NetC#C#实现中国移动CMPP v3.0 ISMG SP收发短信的客户端?

 

/*
.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 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.
*/

/*
.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 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 Util
{
//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[] ReadFromStream(int Length, NetworkStream Stream)
{
byte[] bytes = null;
if (Stream.CanRead)
{
if (Stream.DataAvailable)
{
bytes = new byte[Length];
int r = 0;
int l = 0;
//lock (_SyncLockObject)
{
while (l < Length)
{
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 this._Command_Id;
}
}

public uint Sequence_Id
{
get
{
return this._Sequence_Id;
}
}

public uint Total_Length
{
get
{
return this._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
) //发送前
{
this._Total_Length = Total_Length;
this._Command_Id = Command_Id;
this._Sequence_Id = Sequence_Id;
}

public MessageHeader(byte[] bytes)
{
byte[] buffer = new byte[4];
Buffer.BlockCopy(bytes, 0, buffer, 0, buffer.Length);
Array.Reverse(buffer);
this._Total_Length = BitConverter.ToUInt32(buffer, 0);

Buffer.BlockCopy(bytes, 4, buffer, 0, buffer.Length);
Array.Reverse(buffer);
this._Command_Id = (CMPP_Command_Id) BitConverter.ToUInt32(buffer, 0);

Buffer.BlockCopy(bytes, 8, buffer, 0, buffer.Length);
Array.Reverse(buffer);
this._Sequence_Id = BitConverter.ToUInt32(buffer, 0);
}


public byte[] ToBytes()
{
byte[] bytes = new byte[MessageHeader.Length];

byte[] buffer = BitConverter.GetBytes(this._Total_Length);
Array.Reverse(buffer);
Buffer.BlockCopy(buffer, 0, bytes, 0, 4);

buffer = BitConverter.GetBytes((uint) this._Command_Id);
Array.Reverse(buffer);
Buffer.BlockCopy(buffer, 0, bytes, 4, 4);

buffer = BitConverter.GetBytes(this._Sequence_Id);
Array.Reverse(buffer);
Buffer.BlockCopy(buffer, 0, bytes, 8, 4);

return bytes;
}
public override string ToString()
{
return string.Format
(
"/tMessageHeader:/r/n/t/tCommand_Id: {0}/r/n/t/tSequence_Id: {1}/r/n/t/tTotal_Length: {2}"
,this._Command_Id
,this._Sequence_Id
,this._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 this._Header;
}
}

public byte[] AuthenticatorSource
{
get
{
return this._AuthenticatorSource;
}
}

public CMPP_CONNECT
(
string Source_Addr
, string Password
, DateTime Timestamp
, uint Version
, uint Sequence_Id
)
{
this._Header = new MessageHeader(MessageHeader.Length + BodyLength, CMPP_Command_Id.CMPP_CONNECT, Sequence_Id);

this._Source_Addr = Source_Addr;
this._Password = Password;

string s = Util.Get_MMDDHHMMSS_String(Timestamp);
this._Timestamp = UInt32.Parse(s);

byte[] buffer = new byte[6 + 9 + this._Password.Length + 10];
Encoding.ASCII.GetBytes(this._Source_Addr).CopyTo(buffer, 0);
Encoding.ASCII.GetBytes(this._Password).CopyTo(buffer, 6 + 9);
Encoding.ASCII.GetBytes(s).CopyTo(buffer, 6 + 9 + this._Password.Length);
this._AuthenticatorSource = new MD5CryptoServiceProvider().ComputeHash(buffer, 0, buffer.Length);

this._Version = Version;
}

public byte[] ToBytes()
{
int i = 0;
byte[] bytes = new byte[MessageHeader.Length + BodyLength];

//header 12
byte[] buffer = this._Header.ToBytes();
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length);

//Source_Addr 6
i += MessageHeader.Length;
buffer = Encoding.ASCII.GetBytes(this._Source_Addr);
Buffer.BlockCopy(buffer, 0, bytes, i, 6);

//AuthenticatorSource 16
i += 6;
buffer = this._AuthenticatorSource;
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //16

//version 1
i += 16;
bytes[i++] = (byte) this._Version; //版本

//Timestamp
buffer = BitConverter.GetBytes(this._Timestamp);
Array.Reverse(buffer);
buffer.CopyTo(bytes, i);
return (bytes);
}

public override string ToString()
{
return "[/r/n"
+ this._Header.ToString() + "/r/n"
+ string.Format
(
"/tMessageBody:"
+ "/r/n/t/tAuthenticatorSource: {0}"
+ "/r/n/t/tPassword: {1}"
+ "/r/n/t/tSource_Addr: {3}"
+ "/r/n/t/tVersion: {4}"
,this._AuthenticatorSource
,this._Password
,this._Source_Addr
,this._Timestamp
,this._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 this._AuthenticatorISMG;
}
}

public uint Status
{
get
{
return this._Status;
}
}

public uint Version
{
get
{
return this._Version;
}
}

public MessageHeader Header
{
get
{
return this._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);
this._Header = new MessageHeader(buffer);

//status 4
i += MessageHeader.Length;
buffer = new byte[4];
Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length);
Array.Reverse(buffer);
this._Status = BitConverter.ToUInt32(buffer, 0);

//AuthenticatorISMG 16
i += 4;
this._AuthenticatorISMG = new byte[16];
Buffer.BlockCopy(bytes, MessageHeader.Length + 4, this._AuthenticatorISMG, 0, this._AuthenticatorISMG.Length);

//version
i += 16;
this._Version = bytes[i];
}

public override string ToString()
{
return "[/r/n"
+ this._Header.ToString() + "/r/n"
+ string.Format
(
"/tMessageBody:"
+ "/r/n/t/tAuthenticatorISMG: {0}"
+ "/r/n/t/tBodyLength: {1}"
+ "/r/n/t/tStatus: {2}"
+ "/r/n/t/tVersion: {3}"
,this._AuthenticatorISMG
,CMPP_CONNECT_RESP.BodyLength
,this._Status
,this._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)
{
this._Sequence_Id = Sequence_Id;
}


private byte[] _Msg_Content_Bytes;
private void SetHeader()
{
//byte[] buf;
switch (this._Msg_Fmt)
{
case 8:
_Msg_Content_Bytes = Encoding.BigEndianUnicode.GetBytes(this._Msg_Content);
break;
case 15: //gb2312
_Msg_Content_Bytes = Encoding.GetEncoding("gb2312").GetBytes(this._Msg_Content);
break;
case 0: //ascii
case 3: //短信写卡操作
case 4: //二进制信息
default:
_Msg_Content_Bytes = Encoding.ASCII.GetBytes(this._Msg_Content);
break;
}

this._Msg_Length = (uint) _Msg_Content_Bytes.Length;
this._BodyLength = (int) (FixedBodyLength + 32 * this._Dest_terminal_Id.Length + this._Msg_Length);
this._Header = new MessageHeader((uint) (MessageHeader.Length + this._BodyLength), CMPP_Command_Id.CMPP_SUBMIT, this._Sequence_Id);
}

public byte[] ToBytes()
{
//Msg_Length Msg_Content

int i = 0;
byte[] bytes = new byte[MessageHeader.Length + this._BodyLength];
byte[] buffer = this._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(this._Msg_Id);
Array.Reverse(buffer);
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //10 //[24,33]

//_Pk_total
i += 8;
bytes[i++] = (byte) this._Pk_total; //[20,20]
bytes[i++] = (byte) this._Pk_number; //[21,21]
bytes[i++] = (byte) this._Registered_Delivery; //[22,22]
bytes[i++] = (byte) this._Msg_level; //[23,23]

//Service_Id
buffer = Encoding.ASCII.GetBytes(this._Service_Id);
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //10 //[24,33]

//Fee_UserType
i += 10;
bytes[i++] = (byte) this._Fee_UserType; //[34,34]

//Fee_terminal_Id
buffer = Encoding.ASCII.GetBytes(this._Fee_terminal_Id);
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //32 //[35,66]

//Fee_terminal_type
i += 32;
bytes[i++] = (byte) this._Fee_terminal_type; //[67,67]
bytes[i++] = (byte) this._TP_pId; //[68,68]
bytes[i++] = (byte) this._TP_udhi; //[69,69]
bytes[i++] = (byte) this._Msg_Fmt; //[70,70]

//Msg_src
buffer = Encoding.ASCII.GetBytes(this._Msg_src);
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //6 //[71,76]

//FeeType
i += 6;
buffer = Encoding.ASCII.GetBytes(this._FeeType);
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //2 //[77,78]

//FeeCode
i += 2;
buffer = Encoding.ASCII.GetBytes(this._FeeCode);
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //6 //[79,84]

//ValId_Time
i += 6;
buffer = Encoding.ASCII.GetBytes(this._ValId_Time);
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //17 //[85,101]

//At_Time
i += 17;
buffer = Encoding.ASCII.GetBytes(this._At_Time);
Buffer.BlockCopy(buffer , 0, bytes, i, buffer.Length); //17 //[102,118]

//Src_Id
i += 17;
buffer = Encoding.ASCII.GetBytes(this._Src_Id);
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //21 //[119,139]

//DestUsr_tl
i += 21;
this._DestUsr_tl = (uint) this._Dest_terminal_Id.Length;
bytes[i++] = (byte) this._DestUsr_tl; //[140,140]

//Dest_terminal_Id
foreach (string s in this._Dest_terminal_Id)
{
buffer = Encoding.ASCII.GetBytes(s);
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length);
i += 32;
}

//Dest_terminal_type
bytes[i++] = (byte) this._Dest_terminal_type;
//Msg_Length
bytes[i++] = (byte) this._Msg_Length;

//Msg_Content
//buffer = Encoding.
Buffer.BlockCopy(this._Msg_Content_Bytes, 0, bytes, i, this._Msg_Content_Bytes.Length);

//LinkID
i += (int) this._Msg_Length;
buffer = Encoding.ASCII.GetBytes(this._LinkID);
Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length); //20
return bytes;
}

public ulong Msg_Id
{
get
{
return this._Msg_Id;
}
set
{
this._Msg_Id = value;
}
}

public uint Pk_total
{
get
{
return this._Pk_total;
}
set
{
this._Pk_total = value;
}
}

public uint Pk_number
{
get
{
return this._Pk_number;
}
set
{
this._Pk_number = value;
}
}

public uint Registered_Delivery
{
get
{
return this._Registered_Delivery;
}
set
{
this._Registered_Delivery = value;
}
}

public uint Msg_level
{
get
{
return this._Msg_level;
}
set
{
this._Msg_level = value;
}
}

public string Service_Id
{
get
{
return this._Service_Id;
}
set
{
this._Service_Id = value;
}
}

public uint Fee_UserType
{
get
{
return this._Fee_UserType;
}
set
{
this._Fee_UserType = value;
}
}

public string Fee_terminal_Id
{
get
{
return this._Fee_terminal_Id;
}
set
{
this._Fee_terminal_Id = value;
}
}

public uint Fee_terminal_type
{
get
{
return this._Fee_terminal_type;
}
set
{
this._Fee_terminal_type = value;
}
}

public uint TP_pId
{
get
{
return this._TP_pId;
}
set
{
this._TP_pId = value;
}
}

public uint TP_udhi
{
get
{
return this._TP_udhi;
}
set
{
this._TP_udhi = value;
}
}

public uint Msg_Fmt
{
get
{
return this._Msg_Fmt;
}
set
{
this._Msg_Fmt = value;
if (this._Msg_Content != null)
{
this.SetHeader();
}
}
}

public string Msg_src
{
get
{
return this._Msg_src;
}
set
{
_Msg_src = value;
}
}

public string FeeType
{
get
{
return this._FeeType;
}
set
{
this._FeeType = value;
}
}

public string FeeCode
{
get
{
return this._FeeCode;
}
set
{
this._FeeCode = value;
}
}

public string ValId_Time
{
get
{
return this._ValId_Time;
}
set
{
this._ValId_Time = value;
}
}

public string At_Time
{
get
{
return this._At_Time;
}
set
{
this._At_Time = value;
}
}

public string Src_Id
{
get
{
return this._Src_Id;
}
set
{
this._Src_Id = value;
}
}

public uint DestUsr_tl
{
get
{
return this._DestUsr_tl;
}
set
{
this._DestUsr_tl = value;
}
}

public string[] Dest_terminal_Id
{
get
{
return this._Dest_terminal_Id;
}
set
{
this._Dest_terminal_Id = value;
}
}

public uint Dest_terminal_type
{
get
{
return this._Dest_terminal_type;
}
set
{
this._Dest_terminal_type = value;
}
}

public uint Msg_Length
{
get
{
return this._Msg_Length;
}
set
{
this._Msg_Length = value;
}
}

public string Msg_Content
{
get
{
return this._Msg_Content;
}
set
{
this._Msg_Content = value;
this.SetHeader();
}
}

public string LinkId
{
get
{
return this._LinkID;
}
set
{
this._LinkID = value;
}
}
public override string ToString()
{
return "[/r/n"
+ this._Header.ToString() + "/r/n"
+ string.Format
(
"/tMessageBody:"
+ "/r/n/t/tAt_Time: {0}"
+ "/r/n/t/tBodyLength: {1}"
+ "/r/n/t/tDest_terminal_Id: {2}"
+ "/r/n/t/tDest_terminal_type: {3}"
+ "/r/n/t/tDestUsr_tl: {4}"
+ "/r/n/t/tFee_terminal_Id: {5}"
+ "/r/n/t/tFee_terminal_type: {6}"
+ "/r/n/t/tFee_UserType: {7}"
+ "/r/n/t/tFeeCode: {8}"
+ "/r/n/t/tFeeType: {9}"
+ "/r/n/t/tLinkID: {10}"
+ "/r/n/t/tMsg_Content: {11}"
+ "/r/n/t/tMsg_Fmt: {12}"
+ "/r/n/t/tMsg_Id: {13}"
+ "/r/n/t/tMsg_Length: {14}"
+ "/r/n/t/tMsg_level: {15}"
+ "/r/n/t/tMsg_src: {16}"
+ "/r/n/t/tPk_number: {17}"
+ "/r/n/t/tPk_total: {18}"
+ "/r/n/t/tRegistered_Delivery: {19}"
+ "/r/n/t/tSequence_Id: {20}"
+ "/r/n/t/tService_Id: {21}"
+ "/r/n/t/tSrc_Id: {22}"
+ "/r/n/t/tTP_pId: {23}"
+ "/r/n/t/tTP_udhi: {24}"
+ "/r/n/t/tValId_Time: {25}"
,this._At_Time
,this._BodyLength
,String.Join(",",this._Dest_terminal_Id)
,this._Dest_terminal_type
,this._DestUsr_tl
,this._Fee_terminal_Id
,this._Fee_terminal_type
,this._Fee_UserType
,this._FeeCode
,this._FeeType
,this._LinkID
,this._Msg_Content
,this._Msg_Fmt
,this._Msg_Id
,this._Msg_Length
,this._Msg_level
,this._Msg_src
,this._Pk_number
,this._Pk_total
,this._Registered_Delivery
,this._Sequence_Id
,this._Service_Id
,this._Src_Id
,this._TP_pId
,this._TP_udhi
,this._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 this._Msg_Id;
}
}

public uint Result
{
get
{
return this._Result;
}
}

public MessageHeader Header
{
get
{

return this._Header;
}
}

public CMPP_SUBMIT_RESP(byte[] bytes)
{
int i = 0;
byte[] buffer = new byte[MessageHeader.Length];
Buffer.BlockCopy(bytes, 0, buffer, 0, buffer.Length);
this._Header = new MessageHeader(buffer);

//Msg_Id
i += MessageHeader.Length;
buffer = new byte[8];
Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length);
Array.Reverse(buffer);
this._Msg_Id = BitConverter.ToUInt32(buffer, 0);

//Result
i += 8;
buffer = new byte[4];
Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length);
Array.Reverse(buffer);
this._Result = BitConverter.ToUInt32(buffer, 0);
}
public override string ToString()
{
return "[/r/n"
+ this._Header.ToString() + "/r/n"
+ string.Format
(
"/tMessageBody:"
+ "/r/n/t/tMsg_Id: {0}"
+ "/r/n/t/tResult: {1}"

,this._Msg_Id
,this._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 this._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 this._BodyLength;
}
}

public CMPP_DELIVER(byte[] bytes)
{
int i = 0;

byte[] buffer = new byte[MessageHeader.Length];
Buffer.BlockCopy(bytes, 0, buffer, 0, MessageHeader.Length);
this._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);
this._Msg_Id = BitConverter.ToUInt64(buffer, 0);

string s = null;
//Dest_Id 21