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