📄 cmppsocket_bcb.cpp
字号:
/******************************************************************************
FileName : CcmppSocket.cpp
Description : 短消息发送程序
Version : 1.0
Date : 2004年4月6日
Author : 潘昱宇
Other : 只支持CMPP 3.0 for BCB6
编译条件: 单字节对齐,取消循环变量作用域限定,MFC兼容模式
EMail : rebbie@163.com
******************************************************************************/
#include <stdlib.h>
#ifdef _DEBUG
#include <iterator>
#include <list>
#include <string>
#include <iostream>
#include <fstream>
#include <ostream>
#endif
#include <time.h>
#include "md5.h"
#include "cmppsocket_bcb.h"
//将接收的数据dump 到 _revdebug[]
#ifdef _DEBUG
using namespace std;
char _revdebug[1024*1024];
char * _revp=_revdebug;
std::list<std::string> _dbgeventlst;
char _dbgtemp[100];
std::ostream& operator<<(std::ostream& out, const std::list<std::string>& l)
{
std::copy(l.begin(), l.end(),
ostream_iterator<std::string,char> (out,"\n"));
return out;
}
#endif
CcmppSocket::CcmppSocket()
{
#ifdef _DEBUG
_dbgeventlst.push_back("Begin Class CcmppSocket Constructor.");
#endif
// 初始化私有变量
_binitialized = false;
_bexitting = false;
_seqid = 0;
memset( (void *)_window, 0, sizeof( _window));
InitializeCriticalSection( &_csec_wnd);
InitializeCriticalSection( &_csec_snd);
InitializeCriticalSection( &_csec_recv);
InitializeCriticalSection( &_csec_seq);
_hsema_wnd = CreateSemaphore( // 创建计数信号量
NULL,
nCMPP_WINDOW_SIZE,
nCMPP_WINDOW_SIZE,
NULL);
_hevnt_data = CreateEvent( // 创建提示发送的事件
NULL,
false,
false,
NULL);
// 初始化网络
WSADATA wsaData;
WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
#ifdef _DEBUG
_dbgeventlst.push_back("End Class CcmppSocket Constructor.");
#endif
}
CcmppSocket::~CcmppSocket()
{
// 发送CMPP_TERMINATE,从服务器注销
#ifdef _DEBUG
_dbgeventlst.push_back("Begin Class CcmppSocket Desstructor.");
#endif
if( _binitialized)
_exit();
WSACleanup();
// 清除临界区等
DeleteCriticalSection( &_csec_wnd);
DeleteCriticalSection( &_csec_snd);
DeleteCriticalSection( &_csec_recv);
DeleteCriticalSection( &_csec_seq);
CloseHandle( _hsema_wnd);
CloseHandle( _hevnt_data);
#ifdef _DEBUG
_dbgeventlst.push_back("Begin Class CcmppSocket Desstructor.");
#endif
}
/******************************************************************************
spid 企业代码 例如:901001
passwd 登陆口令 例如:test
ismg 短信网关的地址,例如:127.0.0.1
port 短信网关的端口,例如:7890
******************************************************************************/
int CcmppSocket::init( char *spid, char *passwd, char *ismg, unsigned short port)
{
// 如果接口已经初始化,则先删除接口
#ifdef _DEBUG
_dbgeventlst.push_back("Connection Initializtion Begin.");
#endif
if( _binitialized)
_exit();
int err;
#ifdef _DEBUG
_dbgeventlst.push_back("Connect To GateWay Server.");
#endif
err = _connect( ismg, port);
if( err != 0)
{
#ifdef _DEBUG
sprintf(_dbgtemp,"Connect To GateWay Server Fail. Error Code=%d.",WSAGetLastError());
_dbgeventlst.push_back(_dbgtemp);
#endif
return eCMPP_INIT_CONNECT;
}
#ifdef _DEBUG
_dbgeventlst.push_back("Login To GateWay Server Begin.");
#endif
err = _login( spid, passwd);
if( err != 0)
{
#ifdef _DEBUG
sprintf(_dbgtemp," Connect To GateWay Server Fail Return Code=%d Error Code=%d.",err,WSAGetLastError());
_dbgeventlst.push_back(_dbgtemp);
#endif
closesocket( _soc);
return err;
}
#ifdef _DEBUG
_dbgeventlst.push_back("Login To GateWay Server Success.");
#endif
// 保存配置,以备后用
strcpy( _spid, spid);
strcpy( _passwd,passwd);
strcpy( _ismg, ismg);
_port = port;
// 启动发送、接收数据的线程
#ifdef _DEBUG
_dbgeventlst.push_back("Begin Thread Send SMS.");
#endif
_hsend = CreateThread( // 短信发送线程
NULL,
NULL,
thread_send,
(LPVOID)this,
0,
NULL);
#ifdef _DEBUG
_dbgeventlst.push_back("Begin Thread Recieve SMS.");
#endif
_hrecv = CreateThread( // 短信接收线程
NULL,
NULL,
thread_recv,
(LPVOID)this,
0,
NULL);
#ifdef _DEBUG
_dbgeventlst.push_back("Begin Thread Activate.");
#endif
_hactv = CreateThread( // 链路检查
NULL,
NULL,
thread_actv,
(LPVOID)this,
0,
NULL);
// 初始化成功,设置成功标志
_binitialized = true;
// 放弃当前时间片
Sleep( 0);
#ifdef _DEBUG
_dbgeventlst.push_back("Connection Initializtion Success.");
#endif
return eCMPP_INIT_OK;
}
/******************************************************************************
msg 向服务器提交的数据
dwMilliseconds 在成功的将数据插入数据窗口前等待的时间
返回值 描述
=============== ==============================
0 成功
eCMPP_NEED_INIT 接口未初始化
WAIT_TIMEOUT 操作超时
WAIT_ABANDONED 工作线程异常退出,可能是网络故障
******************************************************************************/
int CcmppSocket::Submit( CMPP_SUBMIT &msg, DWORD dwMilliseconds)
{
CMPP_PACKAGE pkg;
CMPP_HEAD &head = (CMPP_HEAD &)pkg.head;
int err, nsize;
if(!_binitialized)
return eCMPP_NEED_INIT;
pkg.n = 3; // 发送失败,则重发两次
pkg.t = time( NULL); // 立即发送
nsize = sizeof( CMPP_HEAD) + sizeof( CMPP_SUBMIT);
// 因为CMPP协议中包的长度是可变的,而我定义的数据结构中
// 包的长度采用的是最大长度,所以这里需要修正
nsize = nsize + msg.msglen - sizeof( msg.msgcontent);
head.size = htonl( nsize);
head.cmdid= htonl( nCMPP_SUBMIT);
head.seqid= htonl( _getseqid());
memcpy( (void *)pkg.data, (void *)&msg, sizeof( msg));
// 将最后8个字节的保留数据拷贝到适当的位置
memcpy(
(void *)(pkg.data + nsize - sizeof( msg.reserve) - sizeof( CMPP_HEAD)),
(void *)msg.reserve,
sizeof( msg.reserve));
// 等候数据发送窗口的空位
err = WaitForSingleObject( _hsema_wnd, dwMilliseconds);
// 等待超时或程序异常
if( err != WAIT_OBJECT_0)
return err;
// 申请数据发送窗口的使用权
EnterCriticalSection( &_csec_wnd);
for( int i=0; i<nCMPP_WINDOW_SIZE; i++)
{
// 找到一个空位
if( _window[i].head.cmdid == 0)
break;
}
memcpy( (void *)&_window[i], (void *)&pkg, sizeof( pkg));
LeaveCriticalSection( &_csec_wnd);
// 唤醒数据发送线程
PulseEvent( _hevnt_data);
return 0;
}
int CcmppSocket::_connect( char *ismg, unsigned short port)
{
int err;
struct sockaddr_in addr;
_soc = socket( AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons( port);
addr.sin_addr.s_addr = inet_addr( ismg);
err = connect( _soc, (struct sockaddr *)&addr, sizeof( addr));
return err;
}
int CcmppSocket::_login( char *spid, char *passwd)
{
CMPP_PACKAGE pkg;
CMPP_CONNECT &msg = *(CMPP_CONNECT *)pkg.data;
int err, nsize;
MD5 ctx;
char authsrc[50], *pos, timestr[20];
memset( (void *)&msg, 0, sizeof( &msg));
nsize = sizeof( CMPP_HEAD) + sizeof( CMPP_CONNECT);
pkg.head.size = htonl( nsize);
pkg.head.cmdid = htonl( nCMPP_CONNECT);
pkg.head.seqid = htonl( _getseqid());
strcpy( (char *)msg.spid, spid);
// 计算单向HASH函数的值
memset( authsrc, 0, sizeof( authsrc));
strcpy( authsrc, spid);
pos = authsrc + strlen( spid) + 9;
strcpy( (char *)pos, passwd);
pos += strlen( passwd);
strcpy( pos, _timestamp( timestr ));
pos += strlen( timestr);
ctx.update( (unsigned char *)authsrc, (int)(pos - authsrc) );
ctx.finalize();
ctx.raw_digest( msg.digest);
msg.ver = nCMPP_VERSION;
msg.timestamp = htonl( atol( timestr));
// 发送身份验证数据
err = _send( (char *)&pkg, nsize);
if( err != nsize)
return eCMPP_INIT_CONNECT;
// 接收返回的数据包
CMPP_CONNECT_RESP &msgresp = *(CMPP_CONNECT_RESP *)pkg.data;
nsize = sizeof( CMPP_HEAD) + sizeof( CMPP_CONNECT_RESP);
err = _recv( (char *)&pkg, nsize);
if( err != nsize )
return eCMPP_INIT_CONNECT;
return ntohl( msgresp.status);
}
void CcmppSocket::_exit()
{
HANDLE threads[] = { _hsend, _hrecv, _hactv};
int i,
nthreads;
// 请求工作线程退出
_bexitting = true;
Sleep( 1000);
// 关闭请求,强制结束所有尚未退出的线程
_bexitting = false;
nthreads = sizeof( threads) / sizeof( HANDLE);
for( i=0; i<nthreads; i++)
{
TerminateThread( threads[i], 0);
CloseHandle( threads[i]);
}
// 注销
_logout();
closesocket( _soc);
}
void CcmppSocket::_logout()
{
CMPP_HEAD msg;
int err, nsize;
nsize = sizeof( msg);
msg.size = htonl( nsize);
msg.cmdid = htonl( nCMPP_TERMINATE);
msg.seqid = htonl( _getseqid());
err = _send( (char *)&msg, sizeof( msg));
if( err != nsize)
return;
err = _recv( (char *)&msg, sizeof( msg));
return;
}
/******************************************************************************
数据发送线程
对于新提交的数据报,立即发送
超过60秒未收到回应,则重发
******************************************************************************/
DWORD WINAPI CcmppSocket::thread_send( LPVOID pdata)
{
CcmppSocket &cmpp = *( CcmppSocket *)pdata;
CMPP_PACKAGE window[nCMPP_WINDOW_SIZE];
int i;
int err;
int nsize;
int dwMilliseconds = 1000; // 轮询间隔为1000毫秒
for( ;;)
{
// 轮询间隔1秒
err = WaitForSingleObject(
cmpp._hevnt_data,
dwMilliseconds);
// 出错了,结束线程
if( err == WAIT_FAILED)
break;
// 申请数据发送窗口的使用权
EnterCriticalSection( &cmpp._csec_wnd);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -