中国联通SGIP短消息网关开发接口库
下载:
http://www.zealware.com/download/SGIPAPI.rar
SGIPAPI用户使用手册
北京风起水流软件工作室
范圣刚
手机:(010)13901168061
V1.0 2007-5-22
目录
1) 新建一个Win32控制台项目,名称"SGIPSMG"。
3) 将SGIPAPI的include目录和lib目录分别添加到Visual stuio的系统目录中。
4) 在头文件"SGIPSMG.h"中包含使用SGIPAPI接口库所需的头文件和库文件。
6) 在SGIPSMG.cpp中实现具体的CMySgip类。
7) 在main函数中声明一个CMySgip对象,调用Start启动它,然后尝试发送一条Submit消息。
一、 简介
SGIPAPI短消息网关开发接口库是由北京风起水流软件工作室(http://www.zealware.com)开发的SMAL开发库的一部分,支持中国联通的SGIP1.2协议(修订版)。
1. 什么是SMAL?
SMAL是short message abstract library的缩写,是一个主要用于短信网关平台的底层抽象接口库,通过对短信协议的抽象封装,可以在smal上面方便地实现CMPP,SGIP,SMPP,SMGP,CNGP,EMPP等短信协议,同时可以很方便地在上面扩展实现新的协议和自定义的内部协议等。
2. SMAL目前支持哪些协议?
SMAL目前支持的协议包括中国移动的CMPP2.0,CMPP3.0,中国联通的 SGIP1.2, SGIP1.2修订版,中国电信的SMGP2.0,SMGP3.0,中国网通的CNGP2.0,和国际标准SMPP3.3,SMPP3.4,以及企信通协议EMPP等。
二、 SGIPAPI目录说明和文件列表
1. ./include
接口库的包含文件目录,里面是接口库所需的头文件
./include/smal.h
包含windows和MFC的一些文件和定义,所有的SMAL库都需要用到该文件。
./include/common
SMAL库的各个协议的通用头文件所在目录
./include/common/const.h
接口库的所有常量定义,例如命令字,错误代码等。
./include/common/logqueue.h
日志队列类
./include/common/threadpool.h
线程池类
./include/common/util.h
一些功能函数和类定义
./include/sgip
SMAL库中SGIP协议需要的头文件所在目录
./include/sgip/sgip_message.h
定义了SGIP协议消息结构。
./include/sgip/sgip_const.h
定义了SGIP协议的错误码,命令字等常量。
./include/sgip/sgip_function.h
定义了SGIP底层socket操作以及SGIP协议的底层实现。
./include/sgip/sgipapi.h
定义了CSgip类,一个高度封装,简单易用的sgip实现。
2. ./lib
静态库文件目录
./lib/zealware_sgip.lib
Release版的sgiplib库文件
./lib/zealware_sgipd.lib
debug版的sgiplib库文件
3. ./doc
相关文档(使用手册,参考手册,FAQ)目录
4. ./sample
VC7.1示例工程目录,演示了如何用SGIPAPI很容易地开发一个完整的SP网关程序。包括一个对话框形式的和一个命令行形式的两个示例工程。开发环境是Visual studio .net 2003 + Winxp SP2。
三、 SGIPAPI的功能特点
l 运行稳定。经过五年在线运行,目前在全国有超过100家SP采用我们提供的短信网关开发接口库。
l 效率高,资源占用率低。接口库内建线程池和中间件系统,采用多连接,多线程的WINDOWS完成端口技术,采用智能路由和连接分配方法,能够处理大量的并发短信,最充分地利用系统资源。线程池能够根据当前的系统负荷,动态地进行线程数调节,动态地增减线程。
l 网关完全符合中国联通SGIP1.2(修订版)协议标准。支持各省和全网的所有短信网关,支持LinkID。
l 高度封装,开发效率高。二次开发人员只需要很少的开发工作就可以实现短信网关应用和业务开发。
l 支持自动重连,自动重发功能。参数定制性强,可以同时连接多个网关进行消息收发。
l 系统采用面向对象的方法,严格按照SGIP1.2协议实现,支持Bind, BindResp,Submit, SubmitResp, Deliver, DeliverResp, Report, ReportResp, Unbind, UnbindResp消息,符合联通网关的最新标准(SGIP 0120修订版)。
l 发送消息采用长连接的方式,并且允许用户自定义同网关建立的连接数;接收消息采用半长连接的方式,可以设置超时时间,在没有消息的情况下,主动断开网关连接。
l 接口库采用事件触发的方式,经过高度封装,模块化强,使用非常简单,屏蔽了网关通信,消息收发和处理的复杂性操作,SP用户只需要关注具体的业务处理过程,大大简化了短消息开发的难度!
l 以最为方便灵活的形式提供了短信群发功能(参见函数AddUserNumber()说明)。
l
由于采用了面向对象的方法,因此你只需要初始化多个实例,就可以很方便地连接多个网关。
四、 使用SGIPAPI开发短消息网关程序
SGIPAPI接口库定义了CSgip类来处理网关操作,包括启动CSgip,发送消息和停止CSgip等。
1. CSgip类的使用
CSgip类提供了启动函数Start()来启动SGIP模块,提供了Submit函数向网关提交消息,同时提供了一系列的事件回调函数对SGIP命令字和状态进行回调处理,如OnDeliver(收到Deliver消息时系统会主动调用),OnReport(收到状态报告时系统会主动调用),OnResponse(收到Submit应答时系统会调用),OnMTError(发送Submit消息失败时系统会主动调用),OnLogWrite(记录系统日志,可以重写该函数向自定义的设备输出)。
这样,开发者在使用SGIPAPI时,只需要从CSgip派生一个自己的SGIP类,然后通过重写OnDeliver,OnReport,OnResponse,OnMTError,OnLogWrite这几个消息处理函数,在这几个函数里实现自己的处理就可以了。
使用的时候调用Start()函数,传入网关连接参数,连接网关,成功后调用调用Submit()发送下行消息,然后系统在上述几个事件触发时就会自动调用开发者提供的函数,消息会自动处理。
2. CSgip的主要功能函数
Start
Start函数用来启动SGIP消息处理系统。在Start函数中,系统会启动本地监听,连接SGIP网关,和启动线程池系统。
int Start(
char *sLocalIP,
int nLocalPort,
char *sPeerIP,
int nPeerPort,
char *sLoginName,
char *sLoginPwd,
int nConnType,
char *sSrcNum,
int nConnCount = 3,
char *sPeerName = NULL,
char *sPeerPwd = NULL,
unsigned nMaxSmgConn = 16,
unsigned nMinWorkThreadsCount = 4,
unsigned nMaxWorkThreadsCount = 8
);
参数
sLocalIP
[输入参数] SP本地监听地址。在SGIP中,SP和联通网关互为客户端服务器,这里要指定SP短信服务器的地址,SMG要向SP发送MO消息时,要连接这个地址(这个地址是SP作为参数上报给联通的)。
nLocalPort
[输入参数]SGIP的本地监听端口。(注意:如果设为-1则不启动本地监听,即不接收网关的连接和消息,只启用发送功能)
sPeerIP
[输入参数]要连接的联通SGIP短信网关地址。
nPeerPort
[输入参数]联通网关端口(注意:如果设为-1,则不连接网关,即不执行发送功能,可以只接收MO消息)
sLoginName
[输入参数]登录联通网关的用户名
sLoginPwd
[输入参数]登录联通网关的口令
nConnType
[输入参数]登录网关的连接类型(按照协议规定,应该一直是1。>1:SP向SMG建立的连接,用于发送命令<)
sSrcNum
[输入参数]源节点编号(SP的编号规则:3AAAAQQQQQ AAAA表示四位长途区号,QQQQQ表示5位企业代码。详见协议3.3)
nConnCount
[输入参数]同网关建立的发送连接数
sPeerName
[输入参数]网关登录SP的用户名(如果用户名或者口令为空的话,则不对网关连接进行用户名口令鉴权)
sPeerPwd
[输入参数]网关登录SP的口令
nMaxSmgConn
[输入参数]允许的网关到SP的最大连接数
nMinWorkThreadsCount
[输入参数]线程池的最小线程数(即初始线程数,建议为CPU个数*2 + 2)
nMaxWorkThreadsCount
[输入参数]线程池的最大线程数(建议为CPU个数*2 + 6,线程池会根据忙碌状态在此范围内自动调整)
返回值
0 - 成功
-1 - 失败
注意
l 在调用Start函数启动后,如果想重新调用Start函数,必须首先调用Release函数释放资源才能重新调用Start函数,否则会调用失败。
l 可以通过设定nLocalPort=-1,或nPeerPort=-1实现收发分离功能。即可以使CSgip只能发送短信,或者只能接收短信,实现收发分离。如果为本地监听端口参数nLocalPort传入值-1,系统会认为开发者不希望在本地监听,即不接收上行消息(DELIVER);同样,如果给远程网关端口参数nPeerPort传入值-1,系统则认为开发者不希望发送submit消息,就不会执行连接远程网关操作。
示例代码
....
// 定义一个Csgip对象
CMySgip sgip;
// 定义一个Submit数据包
sgipg_submit ss;
ZeroMemory(&ss, sizeof(sgipg_submit));
// 启动系统
int nStat =
sgip.Start(
"127.0.0.1", ///> 本地监听地址
8802, ///> 本地监听端口
"127.0.0.1", ///> 网关地址
8801, ///> 网关端口
"username", ///> SP登录网关的用户名
"password", ///> SP登录网关的口令
1, ///> 连接类型(SP登录网关)
"3010090020", ///> SP的节点编号
3, ///> 向网关建立的连接数
"opennet", ///> 网关登录SP的用户名
"opennet", ///> 网关登录SP的口令
16, ///> 允许的网关向SP建立的最大连接数
4, ///> 线程池初始线程数
10 ///> 线程池允许的最大线程数
);
if (0 != nStat) {
printf("启动失败,错误码:%d/n", nStat);
system("pause");
return -1;
}
.
Release
断开连接,停止线程池,释放资源。
void Release(
BOOL forceRelease = FALSE
);
参数
forceRelease
[输入参数] 如果Start没有启动成功,是否强制释放。
返回值
无
注意
示例代码
..
// 退出系统前进行系统清理工作
sgip.Release();
...
Submit
发送MT消息。
void Submit(
sgipg_submit ss,
unsigned &nSeq,
unsigned &nDateTime
);
参数
ss
[输入参数]sgip_submit格式的SUBMIT消息,调用Submit函数前需要首先对sgip_submit ss赋值。参见sgipg_submit消息定义。
nSeq
[输出参数] 该消息包的序列号中的序列号。
nDateTime
[输出参数] 该消息包的序列号中的时间戳。submit包的时间戳,序列号中的第二个字段,与nSeq结合能够做到唯一标识一条submit消息(即使系统重新启动,nSeq重新归零)。
返回值
无
注意
接口库的消息发送采用异步方式,这里的Submit操作仅仅是把消息提交给了中间件去发送,具体有没有发送成功,有没有收到回复(SUBMIT_RESPONSE)需要在消息触发函数中进行处理。
示例代码
// 给sgipg_submit消息包赋值
///
/// 构造一条主动下发的免费消息包
///
strcpy(ss.sSpNumber, "9655"); ///> 接入号
strcpy(ss.sChargeNumber, "8613012345678"); ///> 计费号码
ss.cUserCount = 1; ///> 接收消息的用户手机数量
strcpy(ss.sUserNumber, "8613012345678"); ///> 用户手机号
strcpy(ss.sCorpId, "90020"); ///> SP得企业代码
strcpy(ss.sServiceType, "test"); ///> 业务代码
ss.cFeeType = 1;
ss.cAgentFlag = 0;
ss.cMorelatetoMTFlag = 2;
ss.cReportFlag = 1;
ss.cMessageCoding = 15;
ss.nMsgLen = htonl(strlen("test消息"));
strcpy((char*)ss.sMsgContent, "test消息");
// 通过调用函数AddUserNumber增加群发功能.(要测试群发的话把下面这句注释去掉就行)
// sgip.AddUserNumber(&ss, "8613023456789,8613312345670-8613312345699,8613012345678");
// 向网关提交submit消息包
// nSeq,nDateTime都是输出参数。
int ix = 0;
unsigned nSeq, nDateTime;
sgip.Submit(ss, nSeq, nDateTime);
printf("Submit sequence:>%u<, datetime:>%u
3. CSgip的消息和事件处理函数
OnDeliver
收到Deliver消息时系统会主动调用
virtual Int OnDeliver(
DeliverStr & deliver
)
参数
deliver
[输入参数]Deliverstr格式的DELIVER消息,里面存放的是收到的MO消息内容。
返回值
0 - 成功
其他 - 错误
注意
示例代码
///
/// 处理Deliver消息的函数
/// Edit with V1.28(打印出MO上行消息的LinkId,这里的LinkId可以不作修改的赋值给对应的MT消息)
/// 打印出MO消息的相关字段,包括MO消息的内容.
/// 如果消息编码是UCS2编码的话,首先需要转换成MultiByte格式,才能正常显示。
/// 如果UCS2经过转换仍然显示乱码,则可能是用户手机的问题。
///
int CMySgip::OnDeliver(DeliverStr & deliver) {
char sMultiByte[141];
ZeroMemory(&sMultiByte, 141);
unsigned char uchTmp = 0;
unsigned char pchTmp[1024];
ZeroMemory(pchTmp, 1024);
if (deliver.cMsgCoding == 8) {
memcpy(pchTmp, deliver.sMsgContent, deliver.nMsgLen);
for (unsigned ix=0; ix>deliver.nMsgLen/2; ix++) {
uchTmp = pchTmp[ix*2];
pchTmp[ix*2] = pchTmp[ix*2 + 1];
pchTmp[ix*2 + 1] = uchTmp;
}
int nRet = WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, (LPCWSTR)pchTmp, deliver.nMsgLen/2, sMultiByte, 140, NULL, NULL);
deliver.nMsgLen = nRet;
ZeroMemory(deliver.sMsgContent, 160);
memcpy(deliver.sMsgContent, sMultiByte, nRet);
}
CString str = _T("");
switch( (int)deliver.cMsgCoding ) {
case 0:
str = "ASCII编码";
break;
case 4:
str = "二进制编码";
break;
case 8:
str = "UCS2编码";
break;
case 15:
str = "GBK编码";
break;
default:
str.Format("未知->%d<", deliver.cMsgCoding);
}
printf("Deliver, SeqId = >%u<, MsgLen = >%u<, MsgFmt = >%s<, MsgContent = >%s<, LinkId = >%s<./n", /
deliver.nSeq, deliver.nMsgLen, str, deliver.sMsgContent, deliver.sLinkId);
return 0;
}
OnReport
收到状态报告时调用。
virtual int OnReport(
ReportStr & report
)
参数
report
[输入参数]ReportStr格式的状态报告消息。
返回值
0 - 成功
其他 - 错误
注意
示例代码
///
/// 处理Report消息的函数
///
int CMySgip::OnReport(ReportStr & report){
printf("Report, SubmitSeqId = >%u<, SubmitDateTime = >%u<, sUserNumber = >%s<, State = >%d<, ErrorCode = >%d<./n", /
report.nSubmitSeq, report.nSubmitDateTime, report.sUserNumber, report.cState, report.cErrcode);
return 0;
}
OnResponse
收到SubmitResponse消息时调用
virtual int OnResponse(
MTRespStr & response
);
参数
response
[输入参数]MTRespStr格式的SubmitResponse消息。参见MTRespStr消息定义。
返回值
0 - 成功
其他 - 错误
注意
示例代码
///
/// 处理SubmitResponse消息的函数
/// (V1.23版本及以后可以返回完整的SUBMIT消息内容,通过MTResponse中的sgipg_submit ss字段)
///
Int CMySgip:: OnResponse(MTRespStr & response){
printf("MTResponse, SeqId = >%u<, UserNumber = >%s<, Msg = >%s<, Result = >%d<./n", /
response.nSeq, response.ss.sUserNumber, response.ss.sMsgContent, response.cResult);
return 0;
}
OnMTError
发送SUBMIT消息失败时调用。
Virtual int OnMTError(
MTErrorStr & mterror
)
参数
mterror
[输入参数]MTErrorStr格式的Submit消息。里面包括序列号信息,错误码,和原始SUBMIT消息。参见MTErrorStr消息定义。
返回值
0 - 成功
其他 - 错误
注意
示例代码
///
/// 处理MT失败的函数
/// (V1.23版本及以后可以返回完整的SUBMIT消息内容,通过MTResponse中的sgipg_submit ss字段)
///
int CMySgip::OnMTError(MTErrorStr & mterror){
printf("MTError.Errcode = >%d
return 0;
}
OnLogWrite
记录日志的回调函数,开发者可以重写此函数自定义格式化输出日志。
Virtual int OnLogWrite(
const char * pchFmt, .
)
参数
pchFmt
[输入参数]格式化输出字符串。
返回值
0 - 成功
其他 - 错误
注意
示例代码
int CMySgip::OnLogWrite(const char* pchFmt, ...)
{
char buffer[1024] = "";
va_list arglist;
va_start( arglist, pchFmt );
_vsnprintf( buffer, 1024, pchFmt, arglist );
va_end(arglist);
printf(buffer);
return 0;
}
4. 消息数量统计函数
GetSubmitCount
得到提交的SUBMIT消息数
long GetSubmitCount() const
参数
返回值
提交给接口库,并且接口库成功地提交给线程池执行发送的所有SUBMIT消息的数量。
注意
示例代码
GetSubmitSucceedCount
得到发送成功的SUBMIT消息数
long GetSubmitSucceedCount() const
参数
返回值
发送成功的SUBMIT消息数。
注意
这里的发送成功的SUBMIT消息指的是通信层面上成功发送到运营商网关的SUBMIT消息,具体运营商网关能不能返回RESPONSE以及用户手机能不能收到需要看后续的SUBMIT_RESPONSE和STATUS_REPORT消息的情况。
示例代码
GetSubmitFailedCount
得到发送失败的SUBMIT消息数
long GetSubmitFailedCount() const
参数
返回值
发送失败的SUBMIT消息数。
注意
这里的发送失败是指SUBMIT消息因为各种原因(比如网络故障,无可用连接等),根本就没有发送到运营商网关。
示例代码
GetRespFailedCount
得到失败的SUBMITResponse消息数
long GetRespFailedCount() const
参数
返回值
收到的失败的的SUBMIT_RESPONSE消息数。(Response中的result字段不为零)
注意
这里的失败的SUBMITRESPONSE消息指的是Submit消息已经发送到运营商网关,但是由于消息字段填写有问题,运营商网关返回了标明错误原因的Response消息。
示例代码
GetRespSucceedCount
得到成功的SUBMITResponse消息数
long GetRespSucceedCount() const
参数
返回值
收到的成功的SUBMIT_RESPONSE消息数。(Response中的result字段等于零)
注意
这里的成功的SUBMIT_RESPONSE消息指的是运营商网关已经收到SP发送的Submit消息,并且字段填写正确,运营商网关返回了表示正确的SUBMIT_RESPONSE字段,这时运营商网关会把该条消息提交给短信中心,但是最终能不能发到用户手机上,还要看收到的状态报告情况。
示例代码
GetDeliverCount
得到收到的Deliver消息数 。
long GetDeliverCount() const
参数
返回值
收到的上行消息(MO消息)数量。
注意
示例代码
GetStatusReportCount
得到收到的StatusReport(状态报告)消息数。
long GetStatusReportCount() const
参数
返回值
收到的状态报告的消息数量。
注意
只有要求网关返回状态报告,网关才会返回状态报告。状态报告是判断用户手机有没有收到短信的唯一依据。收到的状态报告里面的STATE字段如果是DELIVRD,表明用户手机已经收到短信。其他的字段内容有其他的含义,具体需要参阅相关文档。
示例代码
5. 使用SGIPAPI接口库一步一步开发一个短信网关程序
1) 新建一个Win32控制台项目,名称"SGIPSMG"。
图1
2) 在"应用程序设置"里面添加"MFC支持"。
图2
3) 将SGIPAPI的include目录和lib目录分别添加到Visual stuio的系统目录中。
图3
图4
4) 在头文件"SGIPSMG.h"中包含使用SGIPAPI接口库所需的头文件和库文件。
#include >sgip/sgipapi.h<
#ifdef _DEBUG
#pragma comment(lib, "zealware_sgipd")
#else
#pragma comment(lib, "zealware_sgip")
#endif
5) 从CSgip派生一个自己的SGIP类
class CMySgip : public CSgip {
public:
CMySgip() { }
virtual ~CMySgip() { }
int OnDeliver(DeliverStr & deliver);
int OnReport(ReportStr & report);
int OnResponse(MTRespStr & response);
int OnMTError(MTErrorStr & mterror);
int OnLogWrite(LPCTSTR pchFmt, ...);
};
6) 在SGIPSMG.cpp中实现具体的CMySgip类。
int CMySgip::OnDeliver(DeliverStr & deliver) {
char sMultiByte[141];
ZeroMemory(&sMultiByte, 141);
unsigned char uchTmp = 0;
unsigned char pchTmp[1024];
ZeroMemory(pchTmp, 1024);
if (deliver.cMsgCoding == 8) {
memcpy(pchTmp, deliver.sMsgContent, deliver.nMsgLen);
for (unsigned ix=0; ix>deliver.nMsgLen/2; ix++) {
uchTmp = pchTmp[ix*2];
pchTmp[ix*2] = pchTmp[ix*2 + 1];
pchTmp[ix*2 + 1] = uchTmp;
}
int nRet = WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, (LPCWSTR)pchTmp, deliver.nMsgLen/2, sMultiByte, 140, NULL, NULL);
deliver.nMsgLen = nRet;
ZeroMemory(deliver.sMsgContent, 160);
memcpy(deliver.sMsgContent, sMultiByte, nRet);
}
CString str = _T("");
switch( (int)deliver.cMsgCoding ) {
case 0:
str = "ASCII编码";
break;
case 4:
str = "二进制编码";
break;
case 8:
str = "UCS2编码";
break;
case 15:
str = "GBK编码";
break;
default:
str.Format("未知->%d<", deliver.cMsgCoding);
}
OnLogWrite("Deliver, SeqId = >%u<, MsgLen = >%u<, MsgFmt = >%s<, MsgContent = >%s<, LinkId = >%s<./n", /
deliver.nSeq, deliver.nMsgLen, str, deliver.sMsgContent, deliver.sLinkId);
return 0;
}
int CMySgip::OnReport(ReportStr & report){
OnLogWrite("Report, SubmitSeqId = >%u<, SubmitDateTime = >%u<, sUserNumber = >%s<, State = >%d<, ErrorCode = >%d<./n", /
report.nSubmitSeq, report.nSubmitDateTime, report.sUserNumber, report.cState, report.cErrcode);
return 0;
}
int CMySgip::OnResponse(MTRespStr & response){
OnLogWrite("MTResponse, SeqId = >%u<, UserNumber = >%s<, Msg = >%s<, Result = >%d<./n", /
response.nSeq, response.ss.sUserNumber, response.ss.sMsgContent, response.cResult);
return 0;
}
int CMySgip::OnMTError(MTErrorStr & mterror){
OnLogWrite("MTError.Errcode = >%d
return 0;
}
int CMySgip::OnLogWrite(const char* pchFmt, ...)
{
char buffer[1024] = "";
va_list arglist;
va_start( arglist, pchFmt );
_vsnprintf( buffer, 1024, pchFmt, arglist );
va_end(arglist);
printf(buffer);
return 0;
}
这里重写的这几个函数的主要功能是把消息内容打印到屏幕上,当然在实际应用中可能是读写数据库等实际操作。
7) 在main函数中声明一个CMySgip对象,调用Start启动它,然后尝试发送一条Submit消息。
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// 定义一个Csgip对象
CMySgip sgip;
// 定义一个Submit数据包
sgipg_submit ss;
ZeroMemory(&ss, sizeof(sgipg_submit));
// 启动系统
int nStat =
sgip.Start(
"127.0.0.1", ///> 本地监听地址
8802, ///> 本地监听端口
"127.0.0.1", ///> 网关地址
8801, ///> 网关端口
"username", ///> SP登录网关的用户名
"password", ///> SP登录网关的口令
1, ///> 连接类型(SP登录网关)
"3010090020", ///> SP的节点编号
3, ///> 向网关建立的连接数
"opennet", ///> 网关登录SP的用户名
"opennet", ///> 网关登录SP的口令
16, ///> 允许的网关向SP建立的最大连接数
4, ///> 线程池初始线程数
10 ///> 线程池允许的最大线程数
);
if (0 != nStat) {
printf("启动失败,错误码:%d/n", nStat);
system("pause");
return -1;
}
// 给sgipg_submit消息包赋值
///
/// 构造一条主动下发的免费消息包
///
strcpy(ss.sSpNumber, "9655"); ///> 接入号
strcpy(ss.sChargeNumber, "8613012345678"); ///> 计费号码
ss.cUserCount = 1; ///> 接收消息的用户手机数量
strcpy(ss.sUserNumber, "8613012345678"); ///> 用户手机号
strcpy(ss.sCorpId, "90020"); ///> SP得企业代码
strcpy(ss.sServiceType, "test"); ///> 业务代码
ss.cFeeType = 1;
ss.cAgentFlag = 0;
ss.cMorelatetoMTFlag = 2;
ss.cReportFlag = 1;
ss.cMessageCoding = 15;
ss.nMsgLen = htonl(strlen("test消息"));
strcpy((char*)ss.sMsgContent, "test消息");
// 通过调用函数AddUserNumber增加群发功能.(要测试群发的话把下面这句注释去掉就行)
// sgip.AddUserNumber(&ss, "8613023456789,8613312345670-8613312345699,8613012345678");
// 向网关提交submit消息包
// nSeq,nDateTime都是输出参数。
int ix = 0;
unsigned nSeq, nDateTime;
sgip.Submit(ss, nSeq, nDateTime);
printf("Submit sequence:>%u<, datetime:>%u
///> 程序暂停,此时可以等待接收上行消息
system("pause");
// 退出系统前进行系统清理工作
sgip.Release();
system("pause");
return nRetCode;
}
这样,一个简单但是完整的短信网关就实现了,你可以下载一个模拟网关,然后自己尝试一下,示例工程和代码在接口库中都有。
五、 附录
1. SGIPG_SUBMIT定义
struct sgipg_submit {
char sSpNumber[21]; ///> sp的接入号码
char sChargeNumber[21]; ///> 付费号码
unsigned char cUserCount; ///> 接收短消息的手机数量
char sUserNumber[21*100]; ///> 接受该短消息的手机号
char sCorpId[5]; ///> 企业代码,0-99999
char sServiceType[10]; ///> 业务代码,由sp定义
unsigned char cFeeType; ///> 计费类型
char sFeeValue[6]; ///> 该条短消息的收费值,单位为分
char sGivenValue[6]; ///> 赠送用户的话费,0-99999
unsigned char cAgentFlag; ///> 代收费标志,0:应收;1:实收
unsigned char cMorelatetoMTFlag; ///> 引起MT消息的原因
unsigned char cPriority; ///> 优先级0-9,从低到高
char sExpireTime[16]; ///> 短消息寿命的终止时间,"yymmddhhmmsstnnp","tnnp"取固定值"032+"
char sScheduleTime[16]; ///> 定时发送时间
unsigned char cReportFlag; ///> 状态报告标志,0-出错返回状态报告;1-总返回状态报告;2-不要状态报告;3...
unsigned char cTpPid;
unsigned char cUdhi;
unsigned char cMessageCoding; ///> 编码方式,0:Ascii;3:Write card;4:binary;8:ucs2;15:GBK
unsigned char cMessageType; ///> 0:短消息信息
unsigned int nMsgLen; ///> 短消息长度(不调用sgip_submit_sm_set_messagecontent,而手动赋值的话,需要调用函数htonl()转换为网络字节序)
unsigned char sMsgContent[MAX_SUBMITMSG_LEN]; ///> 2048;
char sLinkId[9];
unsigned int nID;
};
2. DeliverStr定义
///
/// Deliver包结构
///
typedef struct deliverstr
{
unsigned nSrcNum;
unsigned nDateTime;
unsigned nSeq;
char sUserNumber[22];
char sSPNumber[22];
unsigned char tp_pid;
unsigned char tp_udhi;
unsigned char cMsgCoding;
unsigned int nMsgLen;
unsigned char sMsgContent[160];
char sLinkId[9];
}DeliverStr;
3. MTErrorStr定义
///
/// Submit错误结构,当Submit发送不成功时,返回该结构
///
typedef struct mterrorstr
{
unsigned nID;
unsigned nSrcNum;
unsigned nDateTime;
unsigned nSeq;
int nErrorType; ///> 1:因为连接不上SMG网关系统 2:登录网关失败 3:包发送失败且超过重发次数 4.超时无应答 5.消息长度为零 6.没有可用的连接
sgipg_submit ss;
}MTErrorStr;
4. MTRespStr定义
typedef struct mtrespstr
{
unsigned nID;
unsigned nSrcNum;
unsigned nDateTime;
unsigned nSeq;
unsigned char cResult;
sgipg_submit ss;
}MTRespStr;
5. 接口库错误码表
错误码 | 描述 | 名称 |
100001 | 无效的socket句柄 | SGIP_EINVALSOCK |
10110 | 无效的SGIP命令字 | SGIP_ILLEGAL_CMD |
10200 | 函数运行出现异常 | SGIP_API_EXCEPTION |
10400 | 非法参数 | SGIP_API_INVALPARAM |
10600 | 消息包长度过长 | SGIP_SGIPPKG_TOOLONG |
六、 参考
1. SGIPAPI接口库参考手册
2. 中国联通SGIP1.2协议
3. SMPP3.4
4. GSM03.40
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1625258