給妳壹些信息供妳參考:
其實說到API的開發,只是封裝了壹個通信協議。以便上層用戶可以專註於自己的業務模塊,而無需了解詳細的協議解釋。
對於CMPP2.0協議來說,只需要使用8條指令,相對來說不是很復雜。
CMPP _連接
= 0x 00000001;//請求連接
CMPP連接RESP
= 0x 80000001;//請求連接回復
CMPP _終止
= 0x 000000002;//終止連接
CMPP _終止_ RESP = 0x 80000002;
//終止連接回復
CMPP _提交
= 0x 000000004;//提交短信
CMPP提交RESP
= 0x80000004//
提交短信回復
CMPP交貨
= 0x 000000005;//短信發送
CMPP交貨RESP
= 0x80000005//
發送短信回復
CMPP _查詢
= 0x 000000006;//短信狀態查詢
CMPP查詢RESP
= 0x80000006//
SMS狀態查詢響應
CMPP _取消
= 0x 000000007;//
刪除短信
RESP CMPP取消
= 0x80000007//刪除短信響應
CMPP _主動_測試
= 0x 000000008;//
激活測試
CMPP _主動_測試_ RESP = 0x 80000008;
//激活測試響應
實現CMPP協議模塊,也就是實現上面的八條指令就夠了。根據上面提到的設計模式,下面結合API開發來談談協議的實現。
對於API外部接口,壹般定義格式如下:
extern " C " _ _ declspec(dll export)int WINAPI active test()
下壹步是實現接口的功能:
1、建立套接字連接
struct sockaddr _ in _ socaddr
_socaddr.sin_family =
AF _ INET
_socaddr.sin_port = htons( _port
);
_ so caddr . sin _ addr . s _ addr = inet _ addr(_ ismg);
返回連接(_soc,(struct
sockaddr *)& amp;_socaddr,sizeof(_ SOC addr));
至於設置發送超時、接收超時和阻塞或非阻塞接收模式,可以根據自己的需要設置不同的目標值。
其次,登錄到短信網關服務器
根據CMPP協議,主要部分是authenticator source = MD5(source _ addr+9字節的0 +shared。
Secret+timestamp),MD5算法可以直接在網上找到源程序,所以不需要重寫這個內容。這部分內容比較簡單,這裏就不寫了。
//計算單向哈希函數的值
strcpy( authsrc,_ spid);
char * pos = auth src+strlen(_ spid)+
9 ;//0的9個字節
strcpy( pos,_ passwd);
pos+= strlen(_ passwd);
strcpy( pos,_timestamp( timestr
));//時間戳
pos+= strlen(timestr);
CTX . update((unsigned char *)auth src,(int)( pos -
auth src));
CTX . finalize();
CTX . raw _ digest(msg-& gt;摘要
);
//將轉換成MD5的字符串復制到發送的數據中。
第三,提交短信
相比登錄,提交短信並不是很復雜。只需將客戶端程序提交的數據復制到發送窗口,發送它並等待網關響應的狀態。當然,在接收到用戶的數據之前,可以做壹些適當的判斷,比如手機號是否全是數字,是否定時發送或者存活時間是否正確。
//註意可變的用戶數據和保留字段的內容:
memcpy(_ pkg-& gt;數據和。msg,sizeof(CMPP _提交));
//將保留數據的最後8個字節復制到適當的位置。
memcpy(
(_ pkg-& gt;數據+ nsize - sizeof(
msg.reserve ) - sizeof( CMPP_HEAD)),
msg.reserve
sizeof( msg.reserve))。
第四,接收短信和狀態
收到短信後有兩種處理方式。
1,使用回調函數:
與API相比,這種方法相對簡單。只需定義接口函數。當客戶端程序初始化時,它會傳入回調函數的函數指針。當API接收到短信網關發送的短信時,調用函數指針將相關參數數據傳遞給客戶端。不需要考慮排隊或者其他細節。
我在API中定義的回調函數如下:
typedef int(WINAPI * TProcGetDeliver)(
字符*
msgid,
無符號的
char * destnumber,
無符號的
char * serviceid,
無符號的
char tppid,
無符號的
char tpudhi,
無符號的
char msgfmt,
無符號的
char * srcnumber,
無符號的
char交付,
無符號的
查爾姆斯格倫,
無符號的
char * msgcontent,
無符號的
char *儲備,
無符號的
char * stat,
無符號的
char * submittime,
無符號char * donetime);
在API外部接口中,只需定義壹個參數作為傳遞函數指針,如下:
extern " C " _ _ declspec(dll export)int WINAPI IFInitInterface(char * ismg,
無符號短端口,TProcGetDeliver fCltGetDeliver);
FCltGetDeliver是指向用戶傳遞的回調函數的指針。調用時,直接使用參數打開調用即可。如下所示:
err = _ fCltGetDeliver((char *)& amp;_msgid,
_ msg-& gt;destnumber,
_ msg-& gt;serviceid,
_ msg-& gt;tppid,
_ msg-& gt;tpudhi,
_ msg-& gt;msgfmt,
_ msg-& gt;src號碼,
_ msg-& gt;交貨,
_ msg-& gt;姆斯格倫,
_ msg-& gt;msgcontent,
_ msg-& gt;儲備,
_stat,
_submittime,
_donetime
) ;
這樣,所有收到的參數都成功地傳遞給了客戶端。
2.調用API的客戶端程序定期檢測並從緩沖隊列中提取它:
對於隊列方法,API必須自己建立壹個緩沖隊列。為了安全起見,這個隊列必須是線程安全的(它可以由臨界區保護)。接收分布式消息時,數據會臨時存儲在隊列中:
int nl = sizeof(CMPP _海德)+ sizeof(
CMPP _交付);
_pushrecqueue( _recpkg,nl);
下面是_pushrecqueue函數;
void WINAPI _ pushrecqueue(void * inval,int nl)
{
char * _ queval = new char[nl+1];
memset( _queval,0,sizeof(nl+1));
memcpy( _queval,inval,nl);
_queue。_ push(_ que val);
}
為了最大限度地保證數據的安全性,不要接收太多的數據進入隊列。放入隊列的數據只是等待用戶使用API接口來取走它。相關接口類似於回調函數的參數,這裏就不解釋了。只需說明可以提供壹個接口讓客戶端檢測隊列中是否有可以接收的數據:
extern " C " _ _ declspec(dll export)int
WINAPI has deliver();
由客戶端決定是否獲取數據。
動詞 (verb的縮寫)發送接口
對於API來說,最重要的部分就是發送數據。通過SOCKET套接字與網關連接後,將自己的數據發送出去。
在發送數據之前,組裝要發送的數據包,並將數據復制到發送窗口。對於沒有響應的數據包,只要發送成功,就可以退出發送過程。對於等待響應的數據包,啟動時鐘檢測是否收到響應,並將結果返回給發送窗口。設置發送次數。如果第壹次發送超時,則按照設置的發送次數再次發送。如果傳輸不成功,您可以設置壹個標誌位來發送鏈路檢測標誌。如果鏈接檢測失敗,妳應該在用戶發送的時候重新連接或者直接返回鏈接斷開的結果。
CMPP _包* _ pkg =(CMPP _包*)buf;
int err = API _ E _ UNKNOW _ ERR
if( _soc == INVALID_SOCKET)返回API _ E _ INVALT _ SOC
while(_ pkg-& gt;n & gt0 )
{
_ pkg-& gt;n = _ pkg-& gt;n - 1
;//傳輸次數
_ lastactime = time(NULL);
EnterCriticalSection(
& amp_ csec _ SOC);
嘗試
{
錯誤=
send( _soc,buf,len,0);
}
接住(...)
{
}
LeaveCriticalSection(
& amp_ csec _ SOC);
如果(err & ltLen) //發送失敗。
{
錯誤=
API _ E _ SO _ SENDERR
繼續
}
//不要跳出來回應結果。
if(_ pkg-& gt;resp == false)
{
err = 0
破裂
}
//等待響應事件
…………
}
//如果傳輸超時,則將傳輸鏈路檢測標誌設置為on。
if( err == API_E_SO_OVETIME)
{
_ needacttest = _ needacttest+1
}
返回err
六、接收界面
對於接收數據,因為發送短信的接口數據量不是很大,所以可以接收。
部分,說明部分功能,這樣可以降低設計難度。當然,壹次性接收緩沖區中的數據並逐段分析並不難。
對於接收,啟動壹個新的線程,這樣也可以保證接收的及時性和可調度性。首先,接收包頭信息以確定整個包的長度,直到接收到完整包的內容才接收下壹個包。有些包也可能沒有包體結構,所以也可以在收到正確的包頭後再進行分析。其結構如下:
//首先接收報頭部分,確定數據包的大小和類型。
err = _ pscoket-& gt;_recv(
_ pscoket-& gt;_rec_window,sizeof(CMPP _ HEAD));
if( err == SOCKET_ERROR || err ==
API_E_INVALT_SOC)繼續;
CMPP _包* _recpkg =(
CMPP _ PACKAGE *)_ pscoket-& gt;_ rec _ window
if(ntohl(_ rec pkg-& gt;頭部.尺寸)
& gt0 )
{
//接收數據包正文
_reclen =
0 ;//接收長度
_ recdatalen = nto HL(_ rec pkg-& gt;head . size)-sizeof(_ rec pkg-& gt;頭);
做
{
睡眠(1);
_ recdatalen = _ recdatalen-_ rec len;
err = _ pscoket-& gt;_recv(
_ rec pkg-& gt;data + _reclen,_ recdatalen);
if(err = = SOCKET _ ERROR | | err = = API _ E _ inv alt _ SOC)繼續;
_ reclen = err
}而(
_ recdatalen-_ rec len & gt;0 ) ;
如果(
_ recdatalen & gt_reclen)繼續;
}
嘗試
{
_ pscoket-& gt;_ analysisrecpack(_ rec pkg);//分析包的內容
}
接住(...)
{
}
七、分析協議包
收到壹個完整的CMPP協議包後,根據命令字進行解析相對不難,但需要註意網絡數據流轉化為主機數據流的問題。
無符號char _ result = 0;
CMPP _ DELIVER * _ msg =(CMPP _ DELIVER *)_ recp kg-& gt;數據
DELIVER _ CONTENT * _ pcont =(DELIVER _ CONTENT
*)_ msg-& gt;msgcontent
if(_ msg-& gt;msglen & lt= 0 )
{
_result = 4
;//收到的長度錯誤要求網關服務器重新發送此數據包。
}
else if(_ msg-& gt;msglen & gt200 )
{
_result = 6
;//收到的長度錯誤要求網關服務器重新發送此數據包。
}
else if(_ msg-& gt;送貨!= 0 | | _ msg->;送貨!=
1 )
{
_result = 1
;//收到的協議格式錯誤,要求網關服務器重新發送這個包。
}
//直接返回錯誤信息。
if(_ result & gt;0 )
{
_deliverresp(
_ rec pkg-& gt;head.seqid,_ msg-& gt;msgid,_ result);
返回;
}
if( _fCltGetDeliver)
{
//如果客戶端程序使用回調函數獲取收到的短消息或狀態,回調。
//傳輸數據的傳輸函數
……………
}
其他
{
//如果客戶端使用自行從隊列中提取的方法,直接將數據推送到queue//列供用戶提取。
………….
}