65.9K
CodeProject 正在变化。 阅读更多。
Home

带 COM 支持的 SMPPLIB

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (24投票s)

2003年5月16日

9分钟阅读

viewsIcon

360751

downloadIcon

5460

这是SMPP v3.3 和 v3.4(部分支持)的实现。您可以使用它连接到 SMSC 来发送/接收短信。

引言

我从之前的一份工作中获得了一些 SMPP 知识。辞职后,我在业余时间编写了这个库。基本上,我以一种逻辑方式设计了数据包的继承,到目前为止我认为是这样的,并根据 SMPP 规范对数据包进行编码和解码。希望大家喜欢。

SMPPLIB 是一个用于发送和接收短信(双向短信)的数据包库,基于 SMPP 规范 v3.4(部分支持)。该库依赖于 MFC(但它依赖的只有 CString 和 Winsock 初始化,我可能会在未来的版本中去除依赖)。

SMPPLIB 是开发短消息服务(SMS)增值服务的构建块。它提供了一个简单的解决方案来解决与短消息服务中心(SMSC)的接口问题。

基本上,对于 SMPP 规范中定义的大多数数据包都有相应的类,以及三个 ESME 实体,它们是 EsmeTransmitterEsmeReceiverEsmeTransceiver。通常,在 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.dllSmpplib.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

CSubmitSMCDeliverSM 数据包的基类。

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

CSubmitSMRespCDeliverSMResp 的基类。

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 实体的 bindunbindsubmitMessageenquireLink 操作之外,还可以直接通过以下方式发送数据包

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,然后向移动用户提交一条消息。

可以在示例文件夹中找到示例项目,即 SmppLibTestTestServer。要尝试,只需运行 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);

除了通用方法外,EsmeTransmitterComEsmeReceiverCom 还支持提交消息的方法。

STDMETHOD(submitMessage)(ISubmitSMCom* isubmit, BSTR* pMsgid, VARIANT_BOOL* pret);

对于 EsmeTransceiverComEsmeReceiver,每当收到 MO 消息时,都会触发 OnDeliverSM 事件。此事件可以在自动化客户端中进行处理。

例如,在 VB.NET 中,它由以下语法声明的子程序处理。

Private Sub receiver_OnDeliverSM(ByVal dsm As SMPPCOMLib.DeliverSMCom) Handles receiver.OnDeliverSM

有关其用法的详细信息,请查看 SmppComTest 项目,这是一个用于发送和接收短信的测试 VB 项目。

为了接收事件,您应该使用 Visual Basic 中的 WithEvent 关键字创建 EsmeReceiverComEsmeTransceiverCom 的实例。

致谢

据我所知,我部分使用了 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_smenquire_link 响应、可选参数等方面提供更多支持。它还附带了一个具有广播功能的 SMS 发送/接收应用程序。我个人认为 C# 比 C++ 更容易使用。

Q6: 我如何接收发送报告?

A6: 据我所知,这需要在您的 SMSC 中进行一些设置。此外,在 Submit SM 数据包中,有一个称为 Registered Delivery 的属性可以设置。有关需要设置的位模式的详细信息,请咨询 SMPP 规范。

Q7: 如何在我的 VC++ 项目中使用这个库?

A7: 首先,您应该仔细阅读本文档,并理解示例项目。该库以事件(接收数据包)驱动模式运行。您必须为“当收到数据包时要做什么”提供回调处理程序。

Q8: 为什么我的客户端无法创建 COM 组件,但在我的开发平台上却能正常工作?

A8: COM 项目只是 smpplib.dll 的包装器。您应该将 SmppCom.dllSmpplib.dll 放在一起。如果您将文件复制到客户端计算机,则必须使用 'regsrv32 SmppCom.dll' 命令在客户端计算机上注册 COM。

Q9: 它支持可选参数吗?

A9: 目前,此 C++ 版本不支持可选参数,我也不打算花时间支持它们。

Q10: 我可以用它发送免费短信吗?

A10: 这个数据包库用于连接到 SMSC。它不用于直接发送短信。短信是否收费取决于电信公司,但大多数情况下都会收费。这个库不适合业余爱好者使用,尽管您仍然可以研究它。

© . All rights reserved.