带 COM 支持的 SMPPLIB






4.94/5 (24投票s)
2003年5月16日
9分钟阅读

360751

5460
这是SMPP v3.3 和 v3.4(部分支持)的实现。您可以使用它连接到 SMSC 来发送/接收短信。
引言
我从之前的一份工作中获得了一些 SMPP 知识。辞职后,我在业余时间编写了这个库。基本上,我以一种逻辑方式设计了数据包的继承,到目前为止我认为是这样的,并根据 SMPP 规范对数据包进行编码和解码。希望大家喜欢。
SMPPLIB 是一个用于发送和接收短信(双向短信)的数据包库,基于 SMPP 规范 v3.4(部分支持)。该库依赖于 MFC(但它依赖的只有 CString
和 Winsock 初始化,我可能会在未来的版本中去除依赖)。
SMPPLIB 是开发短消息服务(SMS)增值服务的构建块。它提供了一个简单的解决方案来解决与短消息服务中心(SMSC)的接口问题。
基本上,对于 SMPP 规范中定义的大多数数据包都有相应的类,以及三个 ESME 实体,它们是 EsmeTransmitter
、EsmeReceiver
和 EsmeTransceiver
。通常,在 SMPP 协议中,ESME 向 SMSC 发送一个命令数据包,然后 SMSC 用一个回复数据包进行回复。当 SMSC 向 ESME 发送一个数据包以指示某事时,也是如此,然后 ESME 发送一个回复数据包给 SMSC。
使用 API 发送短信很容易。
EsmeTransmitter trans; CSmppAddress scr(2, 0, "85291234567"); trans.init("212.5.192.111", 5055); trans.bind("t_test", "testpwd", "", scr); trans.submitMessage("Hiya, this is my message", "85291111888", 2, 0);
使用说明
DLL 库在定义了 SMPPLIB_EXPORTING
的情况下构建,因此在使用它时,不应定义此变量。如果未定义符号,则通过 __declspec(dllimport)
导入 SMPPLIB 中的类。
要使用 DLL,只需将 Smpplib.dll 和 Smpplib.lib 放入应用程序的可搜索目录中,然后设置项目选项以链接 Smpplib.lib。您现在可以使用 SMPPLIB 的类了。
SMPPCOM 是 COM 组件的项目,它依赖于为 SMPPLIB 项目构建的 smpplib.dll,因此您可能需要手动将它们放在一起。SMPPCOM 是一个支持 MFC 的 ATL 项目。
TestServer 只是我用于测试数据包发送/接收的项目,以便在完整编码/解码过程后验证数据包中的字段。在 TestServer 中,数据包在 processPacket
例程中处理,而在 SmppLibTest 中,数据包在注册的回调 processPacket
中处理。
SMPPLIB 类介绍
CSmppDate
这是数据包中用于指定日期的表示。它封装了一个 SYSTEMTIME
作为公共成员和一个空指示符作为受保护成员。
void setDate(CString sd); void setDate(SYSTEMTIME sdt); CString toString(); CSmppDate& operator=(CSmppDate &dt); int getLength(); void setNull(); bool isNull();
CSmppAddress
这是 ESME 地址的表示,包含号码类型和编号计划指示器。
CSmppAddress(uint32 adrton, uint32 adrnpi, CString addr); CSmppAddress& operator=(CSmppAddress& addr); int getLength(); void setAddrNpi(uint32 adrnpi); void setAddrTon(uint32 adrton); void setAddr(CString addr);
CPacketBase
它是所有 SMPP 数据包的基类。它负责编码数据包头和加载数据头。在传递参数时,通常将数据包转换为 CPacketBase
,因此要获取 SMPP 数据包,需要根据 command_id
将它们转换为相应的类型。
库中的所有数据包都继承自 CPacketBase
,因此它们都支持以下操作
uint32 getCommandId();
它从数据包中获取命令 ID,如 SMPP 规范所定义。
uint32 getCommandStatus();
它从数据包中获取命令状态。通常对于 SMSC 响应数据包,命令状态为 0 表示没有错误,否则表示错误条件。详细的错误代码可以在 SMPP 3.4 规范中查看。
uint32 getSequenceNumber();
它获取数据包的序列号。序列号是请求数据包和响应数据包的单调递增的数字。响应数据包应设置与相应请求数据包相同的序列号。
void setCommandId(uint32 cmid);
它为数据包设置命令 ID。在大多数情况下,用户不应自行设置。创建数据包时会分配正确的命令 ID。请注意,您不应设置命令 ID,它会根据您创建的数据包进行设置。
void setCommandStatus(uint32 cmst);
它设置数据包的命令状态。命令状态在 SMPP 3.4 规范中定义。
void setSequenceNumber(uint32 seqn);
它设置数据包的序列号。
API 库支持的 SMPP 数据包
SMPPLIB 中的每个响应数据包都可以在构造函数中传递一个命令数据包,以将响应数据包的序列号设置为命令数据包的序列号。
CAlertNotification
警报通知数据包。
CSmppAddress getSource(); CSmppAddress getEsme(); void setSource(CSmppAddress &src); void setEsme(CSmppAddress &esme);
CGenericNack
通用否定确认数据包。
CBindPacketBase
Bind 数据包的基类。
CString getSystemId(); CString getPassword(); CString getSystemType(); CSmppAddress& getSourceRange(); void setSystemId(CString sid); void setPassword(CString pwd); void setSystemType(CString stype); void setSourceRange(CSmppAddress &addr); void setInterfaceVersion(uint32 iver);
CBindRespBase
Bind 响应数据包的基类。
void setSystemId(CString sid);
CString getSystemId();
CBindTransmitter
Bind Transmitter 数据包。继承自 CBindPacketBase
。
CBindTransmitterResp
Bind Transmitter 响应数据包。继承自 CBindRespBase
。
CBindReceiver
Bind Receiver 数据包。继承自 CBindPacketBase
。
CBindReceiverResp
Bind Receiver 响应数据包。继承自 CBindRespBase
。
CBindTransceiver
Bind Transceiver 数据包。继承自 CBindPacketBase
。
CBindTransceiverResp
Bind Transceiver 响应数据包。继承自 CBindRespBase
。
CUnbind
Unbind 数据包。
CUnbindResp
Unbind 响应数据包。
CMessagePacketBase
CSubmitSM
和 CDeliverSM
数据包的基类。
CString getServiceType(); CSmppAddress getSource(); CSmppAddress getDestination(); uint32 getEsmClass(); uint32 getProtocolId(); uint32 getPriorityFlag(); CSmppDate getScheduledDelivery(); CSmppDate getValidityPeriod(); uint32 getRegisteredDelivery(); uint32 getReplaceIfPresent(); uint32 getSmDefaultMsgId(); uint32 getDataCoding(); uint32 getSmLength(); void getMessage(PBYTE &msg, uint32 &nsz); //setter void setServiceType(CString stype); void setSource(CSmppAddress &src); void setDestination(CSmppAddress &dest); void setEsmClass(uint32 esm); void setProtocolId(uint32 pid); void setPriorityFlag(uint32 pflag); void setRegisteredDelivery(uint32 reg); void setReplaceIfPresent(uint32 rip); void setDataCoding(uint32 enc); void setSmLength(uint32 smlen); void setMessage(PBYTE msg, uint32 nsz);
CMessageRespBase
CSubmitSMResp
和 CDeliverSMResp
的基类。
CString getMessageId();
void setMessageId(CString msgid);
CSubmitSM
Submit Short Message 数据包。继承自 CMessagePacketBase
。
CSubmitSMResp
Submit Short Message 响应数据包。继承自 CMessageRespBase
。
CDeliverSM
Deliver Short Message 数据包。继承自 CMessagePacketBase
。
CDeliverSMResp
Deliver Short Message 响应。继承自 CMessageRespBase
。
CEnquireLink
Enquire Link 数据包。
CEnquireLinkResp
Enquire Link 响应。
CQuerySM
Query Short Message 数据包。
CString getMessageId();
void setMessageId(CString msgid);
CQuerySMResp
Query Short Message 响应数据包。
CString getMessageId(); void setMessageId(CString msgid); CSmppDate getFinalDate(); void setFinalDate(CSmppDate &fldate); uint32 getMessageState(); void setMessageState(uint32 msgst); uint32 getErrorCode(); void setErrorCode(uint32 errcode);
CDataPacketBase
CDataSM
的基类。
CString getServiceType(); CSmppAddress getSource(); CSmppAddress getDestination(); uint32 getEsmClass(); uint32 getRegisteredDelivery(); uint32 getDataCoding(); void setServiceType(CString stype); void setSource(CSmppAddress &src); void setDestination(CSmppAddress &dest); void setEsmClass(uint32 esm); void setRegisteredDelivery(uint32 reg); void setDataCoding(uint32 enc);
CDataSM
Data Short Message 数据包。继承自 CDataPacketBase
。
CDataSMResp
Data Short Message 响应数据包。
支持的 ESME 实体
CEsmeTransmitter
用于发送短信的 Transmitter。
CEsmeReceiver
用于接收短信的 Receiver。
CEsmeTransceiver
用于发送和接收短信的 Transceiver。
除了 ESME 实体的 bind
、unbind
、submitMessage
和 enquireLink
操作之外,还可以直接通过以下方式发送数据包
bool sendPacket(CPacketBase &pak);
ESME 实体类被设计为异步模式运行。应该准备一个回调例程来处理从 SMSC 收到的数据包。这是客户端应用程序定义其逻辑的基本位置。要将回调例程注册到 ESME 实体,请调用 registerProcessPacket
并传递回调函数指针。
回调函数的原型应为以下格式
void __stdcall processPacket(CPacketBase *pak, LPVOID param)
该库依赖于 MFC。在任何调用之前,用户应用程序必须通过发出 WSAStartup()
来初始化 Winsock 库。
示例代码
#include "stdafx.h" #include "SMPPAPI.h" #include "winsock2.h" #include "EsmeTransceiver.h" #include "SmppUtil.h" #include "smpppacket.h" #include "smpp.h" void __stdcall processPacket(CPacketBase *pak, LPVOID param) { switch (pak->getCommandId()) { case SMPP_BIND_TRANSMITTER_RESP: { CBindTransmitterResp *pPak; pPak = static_cast<CBindTransmitterResp *>(pak); TRACE("Packet is casted to CBindTransmitterResp\n"); TRACE(pPak->getSystemId()); TRACE("\n"); trans.submitMessage("Hiya, this is my message", "85261110229", 2, 0); } break; case SMPP_BIND_RECEIVER_RESP: { CBindReceiverResp *pPak; pPak = static_cast<CBindReceiverResp *>(pak); TRACE("Packet is casted to CBindReceiverResp\n"); TRACE(pPak->getSystemId()); TRACE("\n"); } break; case SMPP_BIND_TRANSCEIVER_RESP: { CBindTransceiverResp *pPak; pPak = static_cast<CBindTransceiverResp *>(pak); TRACE("Packet is casted to CBindTransceiverResp\n"); TRACE(pPak->getSystemId()); TRACE("\n"); } break; case SMPP_DELIVER_SM: { CDeliverSM *pPak; pPak = static_cast<CDeliverSM *>(pak); TRACE("Packet is casted to CDeliverSM\n"); TRACE("\n"); } break; case SMPP_SUBMIT_SM_RESP: { CSubmitSMResp *pPak; pPak = static_cast<CSubmitSMResp *>(pak); TRACE("Packet is casted to CSubmitSMResp\n"); TRACE(pPak->getMessageId()); TRACE("\n"); trans.unbind(); } break; case SMPP_UNBIND_RESP: { CUnbindResp *pPak; pPak = static_cast<CUnbindResp *>(pak); TRACE("Packet is casted to CUnbindResp\n"); TRACE("\n"); } break; default: break; } } int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData); int nRetCode = 0; // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; } else { CSmppAddress scr(2, 0, "85263243234"); trans.init("212.5.192.111", 5055); trans.registerProcessPacket(processPacket, NULL); trans.bind("t_test", "testpwd", "", scr); getch(); } }
在示例中,准备了一个名为 processPacket(CPacketBase *pak, LPVOID param)
的回调函数来处理来自 SMSC 的通知。要设置该过程,您会注意到,在 main
中,示例调用了 registerProcessPacket(processPacket, NULL)
。当 SMSC 回复 SMPP_BIND_TRANSCEIVER_RESP
时,它会显示远程系统的 System ID,然后向移动用户提交一条消息。
可以在示例文件夹中找到示例项目,即 SmppLibTest 和 TestServer。要尝试,只需运行 TestServer,然后再运行 SmppLibTest。
TestServer 是使用 SMPPLIB 构建的示例测试服务器,而 SmppLibTest 是一个客户端应用程序。
COM 支持
通过出色的 COM 支持,为 SMPPLIB 创建了几个 COM 类。现在,您可以使用任何支持 COM 自动化的语言和系统来发送和接收短信,包括 Visual Basic 和 ASP 网站。安装后,您的系统中应该定义了 SMPPCOMLib。
创建了以下 COM 类
SmppDateCom Method STDMETHOD(isNull)(VARIANT_BOOL *valnull); STDMETHOD(setNull)(void); STDMETHOD(toString)(BSTR *strdate); SmppAddressCom Property TON NPI Address SubmitSMCom / DeliverSMCom Property ServiceType Source Destination esmClass dataCoding protocolID priorityFlag scheduledDelivery validityPeriod registeredDelivery replaceIfPresent smDefaultMsgId Message Method STDMETHOD(compactMessage)(void); STDMETHOD(flipByteOrder)(void); STDMETHOD(setMessage)(VARIANT msgdata); STDMETHOD(getMessage)(VARIANT* pmsgdata); EsmeTransmitterCom / EsmeReceiverCom / EsmeTransceiverCom Method STDMETHOD(bind)(BSTR sysid, BSTR passwd, BSTR systype, ISmppAddressCom* iaddr, VARIANT_BOOL* pret); STDMETHOD(unbind)(VARIANT_BOOL* pret); STDMETHOD(enquireLink)(VARIANT_BOOL* pret); STDMETHOD(init)(BSTR svrip, LONG port); STDMETHOD(close)(void); STDMETHOD(get_Connected)(VARIANT_BOOL* pVal);
除了通用方法外,EsmeTransmitterCom
和 EsmeReceiverCom
还支持提交消息的方法。
STDMETHOD(submitMessage)(ISubmitSMCom* isubmit, BSTR* pMsgid, VARIANT_BOOL* pret);
对于 EsmeTransceiverCom
和 EsmeReceiver
,每当收到 MO 消息时,都会触发 OnDeliverSM
事件。此事件可以在自动化客户端中进行处理。
例如,在 VB.NET 中,它由以下语法声明的子程序处理。
Private Sub receiver_OnDeliverSM(ByVal dsm As SMPPCOMLib.DeliverSMCom) Handles receiver.OnDeliverSM
有关其用法的详细信息,请查看 SmppComTest 项目,这是一个用于发送和接收短信的测试 VB 项目。
为了接收事件,您应该使用 Visual Basic 中的 WithEvent
关键字创建 EsmeReceiverCom
或 EsmeTransceiverCom
的实例。
致谢
据我所知,我部分使用了 Norm Almond 的代码,即 Buffer
类和 TCP 连接的类。谢谢。
常见问题解答
Q1: 此库对 SMPP 规范 v3.3 和 v3.4 的符合程度如何?
A1: 此库部分支持 SMPP 规范 v3.3 和 v3.4。它提供了一些繁琐的数据包编码和解码方法,并将它们放入各个类中。用户只需使用公开的 getter/setter 来操作数据包内容即可。该库还提供了一些 ESME 实体来连接到 SMSC,并提供了与 SMSC 交换数据包的框架。
Q2: 我如何在 VB.NET、VC++.NET 中使用它?
A2: 该库是在 VC6 中开发的,并在 VC++.NET 中重新编译。它们不是托管类。但是,您仍然可以使用 Interop 服务来使用 COM 组件。如果您不想使用 Interop 服务,可以考虑购买我们的商业 .NET 版本。它比这个免费的 C++ 版本支持更多功能。
Q3: 如何在 VC6 中编译此库?
A3: 对于 smpplib,它附带了一个 VC6 项目文件,您只需打开它并编译即可。对于 SMPPCOM 项目,我没有在 VS6 中构建 VC6 COM 项目,所以您必须自己创建 VC6 COM 项目。我认为这很简单,您只需创建一个 ATL 项目并将源代码放入项目中。
Q4: CEsmeReceiver
中的 linkListen
是什么意思?
A4: 它侦听一个端口,以便 SMSC 可以连接到您的接收器。这通常用于 SMSC 发送的出站数据包,要求您绑定到它们以接收消息。
Q5: .NET 商业版本与此免费版本有什么区别?
A5: 这个免费版本是用 C++ 开发的,而商业版本是用 C# 开发的。商业版本在自动重连、自动 enqurelink、自动 deliver_sm
和 enquire_link
响应、可选参数等方面提供更多支持。它还附带了一个具有广播功能的 SMS 发送/接收应用程序。我个人认为 C# 比 C++ 更容易使用。
Q6: 我如何接收发送报告?
A6: 据我所知,这需要在您的 SMSC 中进行一些设置。此外,在 Submit SM 数据包中,有一个称为 Registered Delivery 的属性可以设置。有关需要设置的位模式的详细信息,请咨询 SMPP 规范。
Q7: 如何在我的 VC++ 项目中使用这个库?
A7: 首先,您应该仔细阅读本文档,并理解示例项目。该库以事件(接收数据包)驱动模式运行。您必须为“当收到数据包时要做什么”提供回调处理程序。
Q8: 为什么我的客户端无法创建 COM 组件,但在我的开发平台上却能正常工作?
A8: COM 项目只是 smpplib.dll 的包装器。您应该将 SmppCom.dll 和 Smpplib.dll 放在一起。如果您将文件复制到客户端计算机,则必须使用 'regsrv32 SmppCom.dll' 命令在客户端计算机上注册 COM。
Q9: 它支持可选参数吗?
A9: 目前,此 C++ 版本不支持可选参数,我也不打算花时间支持它们。
Q10: 我可以用它发送免费短信吗?
A10: 这个数据包库用于连接到 SMSC。它不用于直接发送短信。短信是否收费取决于电信公司,但大多数情况下都会收费。这个库不适合业余爱好者使用,尽管您仍然可以研究它。