.Net/C# 實現 中國移動 CMPP v3.0 ISMG SP 收發短信的 SP 客戶端 (第2版) (CMPP SP Client)

/*
.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
   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')); 

   this._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')); 
   this._Service_Id = s;

   //TP_pid 1
   i += 10;
   this._TP_pid = (uint) bytes[i++];
   this._TP_udhi = (uint) bytes[i++];
   this._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')); 
   this._Src_terminal_Id = s;

   //Src_terminal_type 1
   i += 32;
   this._Src_terminal_type = (uint) bytes[i++];
   this._Registered_Delivery = (uint) bytes[i++];
   this._Msg_Length = (uint) bytes[i++];

   //Msg_Content
   buffer = new byte[this._Msg_Length];
   Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length);
   switch (this._Msg_Fmt)
   {
    case 8:
     this._Msg_Content = Encoding.BigEndianUnicode.GetString(buffer).Trim();
     break;
    case 15: //gb2312
     this._Msg_Content = Encoding.GetEncoding("gb2312").GetString(buffer).Trim();
     break;
    case 0: //ascii
    case 3: //短信寫卡操作
    case 4: //二進制信息
    default:
     this._Msg_Content = Encoding.ASCII.GetString(buffer).ToString();
     break;
   }

   //Linkid 20
   i += (int) this._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')); 
   this._LinkID = s;

  }

  public byte[] ToBytes()
  { //Msg_Length Msg_Content
   byte[] buf;
   switch (this._Msg_Fmt)
   {
    case 8:
     buf = Encoding.BigEndianUnicode.GetBytes(this._Msg_Content);
     break;
    case 15: //gb2312
     buf = Encoding.GetEncoding("gb2312").GetBytes(this._Msg_Content);
     break;
    case 0: //ascii
    case 3: //短信寫卡操作
    case 4: //二進制信息
    default:
     buf = Encoding.ASCII.GetBytes(this._Msg_Content);
     break;
   }

   this._Msg_Length = (uint) buf.Length;
   this._BodyLength = FixedBodyLength + (int) this._Msg_Length;

   byte[] bytes = new byte[MessageHeader.Length + this._BodyLength];

   int i = 0;

   byte[] buffer = null;
   //header 12
   this._Header = new MessageHeader((uint) (MessageHeader.Length + this._BodyLength), CMPP_Command_Id.CMPP_DELIVER, 0);

   //Msg_Id 8
   i += MessageHeader.Length;
   buffer = new Byte[8];
   buffer = BitConverter.GetBytes(this._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(this._Dest_Id);
   Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length);

   //Service_Id 10
   i += 21;
   buffer = new byte[10];
   buffer = Encoding.ASCII.GetBytes(this._Service_Id);
   Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length);

   //TP_pid 1
   i += 10;
   bytes[i++] = (byte) this._TP_pid;
   bytes[i++] = (byte) this._TP_udhi;
   bytes[i++] = (byte) this._Msg_Fmt;

   //Src_terminal_Id 32
   buffer = new byte[32];
   buffer = Encoding.ASCII.GetBytes(this._Src_terminal_Id);
   Buffer.BlockCopy(buffer, 0, bytes, i, buffer.Length);

   //Src_terminal_type 1
   i += 32;
   bytes[i++] = (byte) this._Src_terminal_type;
   bytes[i++] = (byte) this._Registered_Delivery;
   bytes[i++] = (byte) this._Msg_Length;

   //Msg_Content
   Buffer.BlockCopy(buf, 0, bytes, i, buf.Length);

   //LinkID
   i += (int) this._Msg_Length;

   return bytes;
  }
  public override string ToString()
  {
   return "[/r/n"
    + this._Header.ToString() + "/r/n"
    + string.Format
     (
      "/tMessageBody:"
      + "/r/n/t/tBodyLength: {0}"
      + "/r/n/t/tDest_Id: {1}"
      + "/r/n/t/tLinkID: {2}"
      + "/r/n/t/tMsg_Content: {3}"
      + "/r/n/t/tMsg_Fmt: {4}"
      + "/r/n/t/tMsg_Id: {5}"
      + "/r/n/t/tMsg_Length: {6}"
      + "/r/n/t/tRegistered_Delivery: {7}"
      + "/r/n/t/tService_Id: {8}"
      + "/r/n/t/tSrc_terminal_Id: {9}"
      + "/r/n/t/tSrc_terminal_type: {10}"
      + "/r/n/t/tTP_pid: {11}"
      + "/r/n/t/tTP_udhi: {12}"
      ,this._BodyLength
      ,this._Dest_Id
      ,this._LinkID
      ,this._Msg_Content
      ,this._Msg_Fmt
      ,this._Msg_Id
      ,this._Msg_Length
      ,this._Registered_Delivery
      ,this._Service_Id
      ,this._Src_terminal_Id
      ,this._Src_terminal_type
      ,this._TP_pid
      ,this._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)
  {
   this._Msg_Id = Msg_Id;
   this._Result = Result;
  }

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

   byte[] buffer = new byte[MessageHeader.Length];
   //header
   this._Header = new MessageHeader(MessageHeader.Length + Bodylength, CMPP_Command_Id.CMPP_DELIVER_RESP, 0);
   buffer = this._Header.ToBytes();
   Buffer.BlockCopy(buffer, 0, bytes, 0, buffer.Length);
   i += MessageHeader.Length;

   //msg_id 8
   buffer = BitConverter.GetBytes(this._Msg_Id);
   Array.Reverse(buffer);
   buffer.CopyTo(bytes, i);

   //result 4
   i += 8;
   buffer = BitConverter.GetBytes(this._Result);
   Array.Reverse(buffer);
   buffer.CopyTo(bytes, i);
   return bytes;

  }
  public override string ToString()
  {
   return this._Header.ToString() + "/r/n"
    + string.Format
     (
      "[/r/nMessageBody:"
      + "/r/n/tMsg_Id: {0}"
      + "/r/n/tResult: {1}"
      + "/r/n]"
      ,this._Msg_Id
      ,this._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);
    this._Msg_Id = BitConverter.ToUInt32(buffer, 0);

    //_Stat 7
    i += 8;
    buffer = new byte[7];
    Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length);
    this._Stat = Encoding.ASCII.GetString(buffer);

    //_Submit_time 10
    i += 7;
    buffer = new byte[10];
    Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length);
    this._Submit_time = Encoding.ASCII.GetString(buffer);

    //_Done_time 10
    i += 10;
    buffer = new byte[10];
    Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length);
    this._Submit_time = Encoding.ASCII.GetString(buffer);

    //Dest_terminal_Id 32
    i += 10;
    buffer = new byte[32];
    Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length);
    this._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);
    this._SMSC_sequence = BitConverter.ToUInt32(buffer, 0);
   }
  }

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

  public string Stat
  {
   get
   {
    return this._Stat;
   }
   set
   {
    this._Stat = value;
   }
  }

  public string Submit_time
  {
   get
   {
    return this._Submit_time;
   }
   set
   {
    this._Submit_time = value;
   }
  }

  public string Done_time
  {
   get
   {
    return this._Done_time;
   }
   set
   {
    this._Done_time = value;
   }
  }

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

  public uint SMSC_sequence
  {
   get
   {
    return this._SMSC_sequence;
   }
   set
   {
    this._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
      ,this._Dest_terminal_Id
      ,this._Done_time
      ,this._Msg_Id
      ,this._SMSC_sequence
      ,this._Stat
      ,this._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 this._Header;
   }
  }

  public string Time
  {
   get
   {
    return this._Time;
   }
  }

  public uint Query_Type
  {
   get
   {
    return this._Query_Type;
   }
  }

  public string Query_Code
  {
   get
   {
    return this._Query_Code;
   }
  }

  public string Reserve
  {
   get
   {
    return this._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
   )
  {
   this._Time = Util.Get_YYYYMMDD_String(Time);
   this._Query_Type = Query_Type;
   this._Query_Code = Query_Code;
   this._Reserve = Reserve;
   this._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 = this._Header.ToBytes();
   buffer.CopyTo(bytes, 0);

   //Time 8
   i += MessageHeader.Length;
   buffer = new byte[10];
   buffer = Encoding.ASCII.GetBytes(this._Time);
   buffer.CopyTo(bytes, i);

   //Query_Type 1
   i += 8;
   bytes[i++] = (byte) this._Query_Type;

   //Query_Code 10
   buffer = new byte[10];
   buffer = Encoding.ASCII.GetBytes(this._Query_Code);
   buffer.CopyTo(bytes, i);

   //Reserve 8
   i += 10;
   buffer = new byte[8];
   buffer = Encoding.ASCII.GetBytes(this._Reserve);
   buffer.CopyTo(bytes, i);

   return bytes;
  }
  public override string ToString()
  {
   return "[/r/n"
    + this._Header.ToString() + "/r/n"
    + string.Format
     (
      "/tMessageBody:"
      + "/r/n/t/tQuery_Code: {0}"
      + "/r/n/t/tQuery_Type: {1}"
      + "/r/n/t/tReserve: {2}"
      + "/r/n/t/tTime: {3}"
      + "/r/n]"
      ,this._Query_Code
      ,this._Query_Type
      ,this._Reserve
      ,this._Time
     )
    + "/r/n]";
  }
 }

 public class CMPP_QUERY_RESP
 {
  public MessageHeader Header
  {
   get
   {
    return this._Header;
   }
  }

  public string Time
  {
   get
   {
    return this._Time;
   }
  }

  public uint Query_Type
  {
   get
   {
    return this._Query_Type;
   }
  }

  public string Query_Code
  {
   get
   {
    return this._Query_Code;
   }
  }

  public uint Mt_TlMsg
  {
   get
   {
    return this._MT_TLMsg;
   }
  }

  public uint Mt_Tlusr
  {
   get
   {
    return this._MT_Tlusr;
   }
  }

  public uint Mt_Scs
  {
   get
   {
    return this._MT_Scs;
   }
  }

  public uint MT_WT
  {
   get
   {
    return this._MT_WT;
   }
  }

  public uint MT_FL
  {
   get
   {
    return this._MT_FL;
   }
  }

  public uint MO_Scs
  {
   get
   {
    return this._MO_Scs;
   }
  }

  public uint MO_WT
  {
   get
   {
    return this._MO_WT;
   }
  }

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

   //Time 8
   i += MessageHeader.Length;
   buffer = new byte[8];
   Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length);
   this._Time = Encoding.ASCII.GetString(buffer);

   //Query_Type 1
   i += 8;
   this._Query_Type = (uint) bytes[i++];

   //Query_Code 10
   buffer = new byte[10];
   Buffer.BlockCopy(bytes, i, buffer, 0, buffer.Length);
   this._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);
   this._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);
   this._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);
   this._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);
   this._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);
   this._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);
   this._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);
   this._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);
   this._MO_FL = BitConverter.ToUInt32(buffer, 0);
  }

  public override string ToString()
  {
   return "[/r/n"
    + this._Header.ToString() + "/r/n"
    + string.Format
     (
      "/tMessageBody:"
      + "/r/n/t/tBodyLength: {0}"
      + "/r/n/t/tMO_FL: {1}"
      + "/r/n/t/tMO_Scs: {2}"
      + "/r/n/t/tMO_WT: {3}"
      + "/r/n/t/tMT_FL: {4}"
      + "/r/n/t/tMT_Scs: {5}"
      + "/r/n/t/tMT_TLMsg: {6}"
      + "/r/n/t/tMT_Tlusr: {7}"
      + "/r/n/t/tMT_WT: {8}"
      + "/r/n/t/tQuery_Code: {9}"
      + "/r/n/t/tQuery_Type: {10}"
      + "/r/n/t/tTime: {11}"
 
      ,CMPP_QUERY_RESP.BodyLength
      ,this._MO_FL
      ,this._MO_Scs
      ,this._MO_WT
      ,this._MT_FL
      ,this._MT_Scs
      ,this._MT_TLMsg
      ,this._MT_Tlusr
      ,this._MT_WT
      ,this._Query_Code
      ,this._Query_Type
      ,this._Time
     )
         + "/r/n]";
  }
 }

 public class CMPP_ACTIVE_TEST
 {
  private MessageHeader _Header;

  public MessageHeader Header
  {
   get
   {
    return this._Header;
   }
  }

  public CMPP_ACTIVE_TEST(uint Sequence_Id)
  {
   this._Header = new MessageHeader(MessageHeader.Length, CMPP_Command_Id.CMPP_ACTIVE_TEST, Sequence_Id);
  }

  public byte[] ToBytes()
  {
   return this._Header.ToBytes();
  }

  public override string ToString()
  {
   return this._Header.ToString() ;
   
  }
 }

 public class CMPP_ACTIVE_TEST_RESP
 {
  private MessageHeader _Header;
  private byte _Reserved;

  public byte Reserved
  {
   get
   {
    return this._Reserved;
   }
  }

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

   //Reserved 1
   i += MessageHeader.Length;
   this._Reserved = bytes[i];
  }

  public byte[] ToBytes()
  {
   return this._Header.ToBytes();
  }
  public override string ToString()
  {
   return this._Header.ToString();
  }

 }

}

//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[] BodyData
  {
   get
   {
    return this._BodyData;
   }
  }

  public MessageHeader Header
  {
   get
   {
    return this._Header;
   }
  }

  public byte[] HeaderData
  {
   get
   {
    return this._HeaderData;
   }
  }

  public MessageEventArgs(byte[] bytes)
  {
   this._HeaderData = new byte[MessageHeader.Length];
   Buffer.BlockCopy(bytes, 0, this._HeaderData, 0, MessageHeader.Length);
   this._Header = new MessageHeader(this._HeaderData);
   this._BodyData = new byte[this._Header.Total_Length - MessageHeader.Length];
   Buffer.BlockCopy(bytes, MessageHeader.Length, this._BodyData, 0, this._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 this._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 RequestMessageSend;

  private Thread _ReadResponseThread;


  private void OnRequestMessageSend(byte[] data)
  {
   if (RequestMessageSend != null)
   {
    RequestMessageSend(this, new MessageEventArgs(data));
   }
  }


  private void WriteToStreamWithLock(byte[] data, NetworkStream Stream)
  {
   this.OnRequestMessageSend(data);
   lock (_SyncLockObject)
   {
    Util.WriteToStream(data, Stream);
   }
  }

  private byte[] ReadFromStreamWithLock(int Length, NetworkStream Stream)
  {
   lock (_SyncLockObject)
   {
    return Util.ReadFromStream(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());
   this.WriteToStreamWithLock(terminate.ToBytes(), this._NetworkStream);
   this.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());
   this.WriteToStreamWithLock(activeTest.ToBytes(), this._NetworkStream);
   this.StartRun();
  }

  public bool Connect(string Host, int Port, string UserID, string Password, uint SequenceId)
  {
   this._Host = Host;
   this._Port = Port;
   this._Source_Addr = UserID;
   this._Password = Password;

   DateTime ts = DateTime.Now;
   CMPP_CONNECT connect = new CMPP_CONNECT(this._Source_Addr, this._Password, ts, 1, SequenceId);

   Console.WriteLine("Request:/r/n{0}",connect.ToString());

   tc = new TcpClient();
   tc.Connect(this._Host, this._Port);
   this._NetworkStream = tc.GetStream();

   this.WriteToStreamWithLock(connect.ToBytes(), this._NetworkStream);

   this.Run(ref this._IsConnected);
   return this._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());

   this.WriteToStreamWithLock(query.ToBytes(), this._NetworkStream);
   this.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 = this._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 = Util.Get_MMDDHHMMSS_String(DateTime.Now.AddHours(2)) + "032+"; //      string _ValId_Time;     // 17 Octet String 存活有效期,格式遵循SMPP3.3協議。
   submit.At_Time = Util.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());
   this.WriteToStreamWithLock(submit.ToBytes(), this._NetworkStream);

   this.StartRun();
  }

  bool _b = false;
  private bool _IsStarted = false;

  public void StartRun()
  {
   if (!this._IsStarted)
   {
    this._IsStarted = true;
    if (this._ReadResponseThread == null)
    {
     this._ReadResponseThread = new Thread(new ThreadStart(this.Run));
    }
    if (this._ReadResponseThread.ThreadState == ThreadState.Unstarted)
    {
     this._ReadResponseThread.Start();
    }
   }
  }

  private void Run()
  {
   this.Run(ref this._b);
  }

  private void Run(ref bool BreakFlag)
  {
   while (!BreakFlag)
   {
    if (this._NetworkStream.CanRead)
    {
     if (this._NetworkStream.DataAvailable)
     {
      byte[] buffer = new byte[MessageHeader.Length]; //Header
      buffer = this.ReadFromStreamWithLock(MessageHeader.Length, this._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 = this.ReadFromStreamWithLock(l, this._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);
       this._IsConnected = connect_resp.Status == 0;
      }
      else if (header.Command_Id == CMPP_Command_Id.CMPP_TERMINATE_RESP)
      {
       this._b = true;
      }
      if (ResponseMessageReceive != null)
      {
       ResponseMessageReceive(this, new MessageEventArgs(bytes));
      }
     }
    }
    Thread.Sleep(100);
   }
   if (this._b)
   {
    this._NetworkStream.Close();
    this._NetworkStream = null;
   }
  }
 }
}

//測試程序

namespace Test
{
 using System;
 using System.Text;
 using Microshaoft.CMPP.Messages;
 using Microshaoft.CMPP;

 class ConsoleApplication
 {
  static void Main()
  {
   
   ConsoleApplication a = new ConsoleApplication();
   Client c = new Client();
   c.ResponseMessageReceive += new Microshaoft.CMPP.Client.MessageEventHandler(a.c_ResponseMessageReceive);
   c.RequestMessageSend += new Microshaoft.CMPP.Client.MessageEventHandler(a.c_RequestMessageSend);

   Console.WriteLine("press 'q' to exit this programe!");
   uint i = 0; //Sequence_Id header
   ulong l = 0; //Msg_Id body
   if (c.Connect("localhost", 7890, "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;
   //this.PrintHeader(header);
   byte[] bytes = new byte[header.Total_Length];
   header.ToBytes().CopyTo(bytes, 0);
   e.BodyData.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_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();
    }
   }
   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");
   }
   Console.WriteLine(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_RequestMessageSend(Client Sender, MessageEventArgs e)
  {
   Console.WriteLine("Send {0} Request!" ,e.Header.Command_Id);
   //Console.ReadLine();
  }
 }
}

posted on 2005-03-20 23:28 Microshaoft 閱讀(6502) 評論(18)  編輯 收藏 引用 收藏至365Key

Feedback

代碼真好 不過太長了 先收藏起來 以後慢慢看  
  

我發誓 我一輩子永遠離CMPP,CMPP離的遠遠的  
  

我也只有先收藏起來了,正是我要用的。  
  

呵呵,這好像在playuer在csdn.net的博客上看過:),如果能提供打包下載就更好了,我記得有一個嚴重的錯誤的,就是測試例子中有一個bool類型的函數與一類名重複了,導致.BodyLength屬性非法。  
  

難逃法眼 火眼金睛 還是 用了照妖鏡  
  

希望能提供打包下載,謝謝!  
  

to:Microshaoft
我有自己的一套東西來挖掘我感興趣的東西的:)  
  

也許讓我從此放棄編程了。!·  
  

什麼也別說了,Blog真是不斷給人驚喜的地方
謝謝!太感謝了!  
  

  
  

謝謝樓主!
最近測試.正好用到.
希望繼續努力!  
  

程序寫的太差了  
  

標準協議裏很多內容都沒有實現,哪裏實現了滑動窗口了,基本上沒有出錯檢查,連日誌都沒有寫.  
  

的確如此,把滑動窗口加進去程序要複雜的多,還有回執、連接保持等我用Java寫的有需要的來吧[email protected]  
  

在做連接測試時,返回什麼代表沒有連接成功?  
  

這也算程序啊,如果這是程序的話我早就是軟件高手呢  
  



//這個地方,你也要來做廣告!
想錢想病了!  
  

好文章!!!  
  

# re: .Net/C# 實現 中國移動 CMPP v3.0 ISMG SP 收發短信的 SP 客戶端 (第2版) (CMPP SP Client) 2005-03-21 08:46 QuitGame
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章