?? cmppsocket_vc.cpp
字號:
/******************************************************************************
FileName : CcmppSocket.cpp
Description : 短消息發送程序
Version : 1.0
Date : 2004年4月6日
Author : 潘昱宇
Other : 只支持CMPP 3.0
編譯開關 單字節對齊 ,連接Ws2_32.lib.
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_VC.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( pkg.head) + sizeof( msg);
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 + -