蘇戈
摘要:多源氣象資料的收集現(xiàn)在已經(jīng)成為各級(jí)氣象部門(mén)的重要工作。該文利用C++開(kāi)發(fā)了一套多源資料收集系統(tǒng),可以提高氣象資料收集的自動(dòng)化程度,減輕氣象預(yù)報(bào)員的負(fù)擔(dān),使之可以集中精力做好氣象保障工作。
關(guān)鍵詞:FTP;多線程;數(shù)據(jù)庫(kù);C++
中圖分類號(hào):TP311 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1009-3044(2018)20-0074-03
1 引言
高質(zhì)量的氣象資料是提高氣象保障能力和水平的重要因素,現(xiàn)代氣象預(yù)報(bào)的準(zhǔn)確程度很大程度上取決于對(duì)各種氣象資料的準(zhǔn)確分析,這就要求我們必須及時(shí)有效地獲取豐富的氣象資料。而現(xiàn)代的氣象資料種類繁多,來(lái)源也多種多樣,因此高效的資料收集工作顯得格外重要。
目前各氣象保障單位向外發(fā)布?xì)庀筚Y料的手段很多,最常見(jiàn)的有WWW、FTP等方式。就方便性和及時(shí)性而言,F(xiàn)TP發(fā)布依然是比較有效的一種手段?,F(xiàn)在常見(jiàn)的一些FTP下載工具通常只提供簡(jiǎn)單的單站點(diǎn)、單個(gè)文件或目錄下載,不能對(duì)資料進(jìn)行有效的篩選。這對(duì)需要大量實(shí)時(shí)和歷史資料的氣象預(yù)報(bào)工作來(lái)說(shuō),資料下載無(wú)疑將成為一件災(zāi)難性的工作。本文作者利用C++開(kāi)發(fā)了一套多源氣象資料定時(shí)收集系統(tǒng),充分利用現(xiàn)有的FTP協(xié)議,并結(jié)合Windows的多線程和數(shù)據(jù)庫(kù)技術(shù),可以對(duì)氣象資料的下載來(lái)源、種類、時(shí)間等進(jìn)行定制,滿足氣象資料下載的實(shí)時(shí)性和多樣性要求。
2 關(guān)鍵技術(shù)解析
2.1 多線程編程
Windows系統(tǒng)是一個(gè)提供線程操作的系統(tǒng),它可以使我們更好地利用計(jì)算機(jī)的系統(tǒng)資源和網(wǎng)絡(luò)帶寬,提高下載效率。Windows支持兩種線程模式,一種是輔助線程,一種是用戶界面線程。它們的不同之處在于用戶界面線程通常有窗口,具有自己的消息循環(huán),而輔助線程則沒(méi)有窗口,因此不需要處理消息。輔助線程的編程工作相比較而言比較簡(jiǎn)單,而且通常更加有用。在本系統(tǒng)的開(kāi)發(fā)中,我們使用用戶線程作為主線程,接受用戶請(qǐng)求,監(jiān)控輔助線程的工作狀態(tài);利用輔助線程來(lái)完成FTP下載的主要工作。對(duì)于不同的資料來(lái)源,可以由用戶線程建立多個(gè)輔助線程,同時(shí)進(jìn)行工作。
輔助線程的工作通常分下面幾步來(lái)完成:
1) 構(gòu)建全局函數(shù)
全局函數(shù)供主程序在啟動(dòng)輔助線程時(shí)使用,它應(yīng)該返回一個(gè)UINT,并以一個(gè)32位值(聲明為L(zhǎng)PVOID)作為參數(shù),在啟動(dòng)線程的時(shí)候可以通過(guò)它來(lái)傳遞任何需要在輔助線程中使用的參數(shù)。它的基本聲明應(yīng)該和下面的聲明相類似:
UINT ThreadProc(LPVOID lpVoid){
…… //自己的處理代碼
return 0;
}
2) 啟動(dòng)輔助線程
啟動(dòng)輔助線程的工作一般應(yīng)在主程序中進(jìn)行,比如要啟動(dòng)上面定義的輔助線程,過(guò)程如下所示:
CWinThread* pThread=
AfxBeginThread(ThreadProc, //輔助線程的處理函數(shù)
GetSafeHwnd(),//傳遞的參數(shù)指針
THREAD_PRIORITY_NORMAL);//優(yōu)先級(jí)
輔助線程在全局函數(shù)返回后自動(dòng)終止,而且如果調(diào)用它的進(jìn)程終止,輔助線程也將終止。
3) 輔助線程和主線程的通信
輔助線程和主線程之間的通信最便捷而有效的方式就是輔助線程向主線程發(fā)送消息,窗口句柄的傳遞可以在AfxBeginThread中進(jìn)行,消息發(fā)送的函數(shù)原型為:
LRESULT SendMessage(
HWND hWnd, // 目標(biāo)窗口句柄
UINT Msg, // 要發(fā)送的消息名
WPARAM wParam, // 第一個(gè)消息參數(shù)
LPARAM lParam // 第二個(gè)消息參數(shù)
);
2.2 數(shù)據(jù)庫(kù)編程
因?yàn)樾枰獙?duì)各個(gè)站點(diǎn)的參數(shù)和下載文件的大小、時(shí)間等進(jìn)行定制,普通的文本文件存儲(chǔ)此類信息的能力有限,而且也不便于修改和維護(hù),因此在本系統(tǒng)中采用Access數(shù)據(jù)庫(kù)來(lái)保存這些信息。VC++對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)提供了很好的支持。這里采用CDatabase和CRecordset兩個(gè)MFC類來(lái)訪問(wèn)數(shù)據(jù)庫(kù)。具體步驟如下:
1) 建立數(shù)據(jù)源
建立數(shù)據(jù)源有很多種辦法,我們直接用Office的Access應(yīng)用程序建立一個(gè)名為FileTrans.mdb的數(shù)據(jù)文件,然后利用向?qū)Ы⑿枰男畔⒈鞦tpSrcTable和FtpDenTable,主要包括FTP服務(wù)器IP地址、端口號(hào)、登錄用戶名、密碼、文件擴(kuò)展名、處理時(shí)間間隔等參數(shù)信息。
2) 建立數(shù)據(jù)庫(kù)連接
要建立和數(shù)據(jù)源的聯(lián)接需要使用CDatabase類的成員函數(shù):
virtual BOOL OpenEx( LPCTSTR lpszConnectString, DWORD dwOptions = 0 );
其中l(wèi)pszConnectString參數(shù)用來(lái)指定數(shù)據(jù)源的位置和參數(shù)信息(訪問(wèn)用戶名、密碼等);dwOptions用來(lái)指定打開(kāi)方式。如果返回值為真的話表示連接成功,可以進(jìn)行下一步了。
3) 讀取參數(shù)信息
連接數(shù)據(jù)庫(kù)成功后,可以直接用CRecordset類的構(gòu)造函數(shù)構(gòu)造一個(gè)CRecordset對(duì)象CRecordset* pRS=new CRecordset(pDB);(其中pDB為連接數(shù)據(jù)庫(kù)指針)。利用構(gòu)造好的pRecordset對(duì)象就可以操作打開(kāi)的數(shù)據(jù)庫(kù),讀取相關(guān)的參數(shù)信息。
2.3 FTP訪問(wèn)
關(guān)于FTP的訪問(wèn),既可以利用現(xiàn)有的FTP控件,也可以直接使用Windows提供的API函數(shù)。由于現(xiàn)有的FTP控件提供的功能過(guò)于簡(jiǎn)單,同時(shí)也不便于多線程編程,因此本系統(tǒng)直接采用Windows提供的API函數(shù)進(jìn)行FTP訪問(wèn)。主要的API函數(shù)如下:
1)InternetOpen,初始化一個(gè)Win32 Internet應(yīng)用。
2)InternetConnect,連接FTP服務(wù)器。
3)FtpSetCurrentDirectory,設(shè)置FTP服務(wù)器的當(dāng)前虛擬目錄。
4)FtpFindFirstFile,查找FTP服務(wù)器上的第一個(gè)索引文件。
5)InternetFindNextFile,查找FTP服務(wù)器上的下一個(gè)文件。
6)FtpGetFile,從FTP服務(wù)器下載指定的文件并存儲(chǔ)到本地磁盤(pán)上。
7)InternetCloseHandle,關(guān)閉Internet連接。
這些API函數(shù)的詳細(xì)參數(shù)說(shuō)明請(qǐng)大家參閱MSDN等資源,這里不再贅述。
3 關(guān)鍵模塊實(shí)現(xiàn)
系統(tǒng)的完整實(shí)現(xiàn)不可能在文中一一列出,這里只給出系統(tǒng)的幾個(gè)關(guān)鍵模塊的功能實(shí)現(xiàn)。
3.1 讀取參數(shù)信息
讀取表FtpSrcTable的信息
CDatabase* pDB=new CDatabase();
try{
pDB->OpenEx(_T("DRIVER={Microsoft Access Driver(*.mdb)};DBQ=filetrans.mdb"),CDatabase::noOdbcDialog)
}
catch(CDBException* e){
return false;
}
CRecordset* pRS=new CRecordset(pDB);
CString strSQL; //查詢語(yǔ)句
CDBVariant m_DbVar; //數(shù)據(jù)庫(kù)值變量
try{
int iCount=0;
strSQL="select * from FtpSrcTable";
pRS->Open(AFX_DB_USE_DEFAULT_TYPE,strSQL);
while(!pRS->IsEOF()){
pRS->GetFieldValue("ID",m_DbVar);
pParam->pIDA->Add(*m_DbVar.m_pstring);
……
pRS->MoveNext();
}
pRS->Close();
}
catch(CDBException* e){
}
其中pParam參數(shù)為自定義的一個(gè)結(jié)構(gòu)體,用來(lái)存儲(chǔ)一個(gè)輔助線程用到的參數(shù)信息。
3.2 FTP下載輔助線程函數(shù)
FTP的文件和目錄列表功能一次連接只能列舉一個(gè)特定虛擬目錄下的文件和子目錄,因此如何遍歷站點(diǎn)的所有目錄和文件就成為系統(tǒng)功能實(shí)現(xiàn)的關(guān)鍵。這里采用簡(jiǎn)單的遞歸函數(shù)解決這個(gè)問(wèn)題,程序列表如下:
void TransFtpFile(FtpParam* pParam,CString strVirtualDir,int iIndex)
{
WIN32_FIND_DATA fileData;
CString strDenPath=pParam->pDenParam->pDenDirNameA->GetAt(0);
hOpen=InternetOpen("My",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
if(hOpen!=NULL){
hConnect=InternetConnect(hOpen,
pParam->pSrcParam->pIPA->GetAt(iIndex),
strtod(pParam->pSrcParam->pPortA->GetAt(iIndex),NULL),
pParam->pSrcParam->pLoginUserA->GetAt(iIndex),
pParam->pSrcParam->pLoginPwdA->GetAt(iIndex),
INTERNET_SERVICE_FTP,INTERNET_FLAG_PASSIVE,0); 0);
if(hConnect!=NULL){
if(MyFtpSetCurrentDirectory(hConnect,strVirtualDir)){
hFtp=FtpFindFirstFile(hConnect,
pParam->pSrcParam->pExtNameA->GetAt(iIndex),
&fileData;,0,0);
if(hFtp!=NULL){
strFile.Format(fileData.cFileName);
if(!(fileData.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY)){
if(strFile!="." && strFile!=".."){
if(FtpGetFile(hConnect, strFile,
strDenPath +"\\"+strFile,false,0,
FTP_TRANSFER_TYPE_BINARY ,0)){
MyCopyFile(strDenPath +"\\"+strFile,
strDenPath+"\\"+strVirtualDir+"\\"+strFile);
}
}
}
else{ if(strFile!="." && strFile!=".."){
m_Array.Add(strVirtualDir+"\\"+strFile);
}
}
}
}
while(InternetFindNextFile(hFtp,&fileData;)){
strFile.Format(fileData.cFileName);
if(!(fileData.dwFileAttributes
&FILE;_ATTRIBUTE_DIRECTORY)){
if(strFile!="." && strFile!=".."){
if(FtpGetFile(hConnect,strFile,strDenPath+"\\"+strFile,false,0,
FTP_TRANSFER_TYPE_BINARY ,0)){
MyCopyFile(strDenPath +"\\"+strFile, strDenPath +"\\"+strVirtualDir+"\\"+strFile);
}
}
}
else{
if(strFile!="." && strFile!=".."){
m_Array.Add(strVirtualDir+"\\"+strFile); }
}
}
}
}
}
else{
InternetCloseHandle(hOpen);
}
for(int j=0;j TransFtpFile(pParam,m_Array.GetAt(j),iIndex); } } 4 結(jié)束語(yǔ) 本軟件在Windows2003以上系統(tǒng)上編譯通過(guò),實(shí)際運(yùn)行穩(wěn)定,在單位的氣象資料收集工作中發(fā)揮了較好的作用。