.Net/C# 实现 中国移动 CMPP v3.0 ISMG SP 收发短信的 SP 客户端 (CMPP SP Client)

本文档介绍了如何使用 C# 编程语言实现中国移动 CMPP v3.0 协议,以连接 ISMG 并进行短信收发操作。详细讲解了 CMPP 连接、终止、提交短信和接收短信等关键消息的定义及交互过程,包括消息头、消息体的结构和字段说明。并提供了代码示例,以帮助开发者理解和实现 CMPP 协议的相关功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                           /*
.Net/C# 实现 中国移动 CMPP v3.0 ISMG <-> SP 收发短信的 SP 客户端 (CMPP SP 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/cmpp3pro.rar

本程序以熟悉理解 CMPP 3.0 协议为主要目的,只将 "消息定义" 对象化,其相关协议级交互并未作更深层次的 OO!
也暂无任何错误处理程序!
消息定义的所有字段名称及其数据类型均与上述之 CMPP v3.0.0 文档完全一致!

其间参阅过 shanhe@优快云 or yexiong@cnBlogs 大作(在此谢):
http://blog.youkuaiyun.com/shanhe/archive/2004/07/19/45383.aspx
http://cnblogs.com/yexiong/articles/115330.aspx
但其中有些消息定义字节错位,因此不能正常交互?!且对象化层次较高,不利于理解协议本身!
遂自己动手,丰衣足食,实现部分主要协议(SP 收发短信):

playyuer$at$microshaoft.com Invent.
*/

//CMPP 消息定义
namespace Microshaoft.CMPP.Messages
{
 using System;
 using System.Security.Cryptography;
 using System.Text;
 
 public class CMPP_Request
 {
  
 }
 public class CMPP_Response
 {
  
 } 
 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 Util
 {
  public static string Get_MMDDHHMMSS_String(DateTime dt)
  {
   string s = dt.Month.ToString().PadLeft(2, '0');
   s += dt.Day.ToString().PadLeft(2, '0');
   s += dt.Hour.ToString().PadLeft(2, '0');
   s += dt.Minute.ToString().PadLeft(2, '0');
   s += dt.Second.ToString().PadLeft(2, '0');
   return (s);
  }
  public static string Get_YYYYMMDD_String(DateTime dt)
  {
   string s = dt.Year.ToString().PadLeft(4, '0');
   s += dt.Month.ToString().PadLeft(2, '0');
   s += dt.Day.ToString().PadLeft(2, '0');
   return (s);
  }
 }

 public class MessageHeader //消息头
 {
  public const int Length = 4 + 4 + 4;
  //private byte[] _bytes = new byte[MessageHeader.Length];
  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 CMPP_Command_Id _Command_Id;
  //private uint _Sequence_Id;
  //private uint _Total_Length;

  uint _Total_Length; // 4 Unsigned Integer 消息总长度(含消息头及消息体)
  CMPP_Command_Id _Command_Id; // 4 Unsigned Integer 命令或响应类型
  uint _Sequence_Id; // 4 Unsigned Integer 消息流水号,顺序累加,步长为1,循环使用(一对请求和应答消息的流水号必须相同)

  public MessageHeader(uint Total_Length, CMPP_Command_Id Command_Id, uint Sequence_Id) //发送前
  {
   this._Command_Id = Command_Id;
   this._Sequence_Id = Sequence_Id;
   this._Total_Length = Total_Length;
  }

  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 class CMPP_CONNECT : CMPP_Request
 {
  public const int BodyLength = 6 + 16 + 1 + 4;

  string _Source_Addr; // 6 Octet String 源地址,此处为SP_Id,即SP的企业代码。
  private string _Password;
  byte[] _AuthenticatorSource; // 16 Octet String 用于鉴别源地址。其值通过单向MD5 hash计算得出,表示如下:
  //   AuthenticatorSource =
  //   MD5(Source_Addr+9 字节的0 +shared secret+timestamp)
  //   Shared secret 由中国移动与源地址实体事先商定,timestamp格式为:MMDDHHMMSS,即月日时分秒,10位。
  uint _Version; // 1 Unsigned Integer 双方协商的版本号(高位4bit表示主版本号,低位4bit表示次版本号),对于3.0的版本,高4bit为3,低4位为0
  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)
  {
   this._Header = new MessageHeader(MessageHeader.Length + BodyLength, CMPP_Command_Id.CMPP_CONNECT, 1);

   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()
  {
   byte[] bytes = new byte[MessageHeader.Length + BodyLength];
   int i = 0;

   //header 12
   byte[] buffer = this._Header.ToBytes();
   Buffer.BlockCopy(buffer, 0, bytes, 0, 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 class CMPP_CONNECT_RESP : CMPP_Response
 {
  MessageHeader _Header;
  public const int _FixedBodyLength = 4 + 16 + 1;

  uint _Status; // 4 Unsigned Integer 状态
  //   0:正确
  //   1:消息结构错
  //   2:非法源地址
  //   3:认证错
  //   4:版本太高
  //   5~:其他错误
  byte[] _AuthenticatorISMG; // 16 Octet String ISMG认证码,用于鉴别ISMG。
  //   其值通过单向MD5 hash计算得出,表示如下:
  //   AuthenticatorISMG =MD5(Status+AuthenticatorSource+shared secret),Shared secret 由中国移动与源地址实体事先商定,AuthenticatorSource为源地址实体发送给ISMG的对应消息CMPP_Connect中的值。
  //    认证出错时,此项为空。
  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 class CMPP_SUBMIT : CMPP_Request
 {
  public 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

CMPP模拟器主要是模拟使用中国移动CMPP协议(版本1.x~3.x)的各种网关。此类型网关只使用一个收发连接(短信接收和发送在一个链接上进行)。 模拟器要求使用JDK1.4以上的Java运行环境,请确认相关环境已经安装妥当。如果还没有安装Java环境,请访问java.sun.com下载最新J2SE的SDK。 此模拟器已经使用了全新的核心设计,主要针对应用程序的稳定性、可靠性、效率以及配置、管理和监控方面做了很大的调整。基本界面风格和应用功能上没什么变化。 1.建立Socket连接与登陆 使用自己的CMPP客户端程序,与7890端口建立Socket连接。然后按照CMPP协议发送登陆数据包。模拟器会按照协议处理相关连接和登陆过程。 2.发送短信息 正确建立连接和登陆以后,可以按照CMPP协议的Submit过程提交相关数据并得到应答。模拟器在接收到数据以后,会进行解析并按照协议要求进行应答和回复。相关的处理信息会记录在日志文件中。 3.接收短信息 正确建立连接和登陆以后,在同一连接上等待模拟器的Deliver数据包即可,并且要求客户端按照协议给予应答。模拟器会对相关过程记录在日志信息当中。 4.模拟MT以及状态报告过程 发送submit时,请将registered_delivery设置为1即可。 模拟器收到相关数据包以后,会通过submit_response应答给出message_id;随后模拟出deliver数据包给出状态报告,其中registered_delivery为1。 5.模拟MO过程 发送submit时,请将registered_delivery设置为0即可。 模拟器收到相关数据包以后,会通过deliver请求发送模拟的MO。其中deliver的相关数据全部来自接收到的submit数据。包括来源号码、目标号码、业务代码以及信息内容。 6.模拟压力测试 如果需要进行模拟的完整压力测试过程,只需要以最大速度重复步骤5即可。 模拟器的监控 模拟器有一个基于Web监控后台,系统启动的时候同时启动。缺省端口建立在8081上。监控的URL地址、用户名和密码可以在配置文件中找到。 http://localhost:8081 forest_luo root
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值