苏戈
摘要:多源气象资料的收集现在已经成为各级气象部门的重要工作。该文利用C++开发了一套多源资料收集系统,可以提高气象资料收集的自动化程度,减轻气象预报员的负担,使之可以集中精力做好气象保障工作。
关键词:FTP;多线程;数据库;C++
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2018)20-0074-03
1 引言
高质量的气象资料是提高气象保障能力和水平的重要因素,现代气象预报的准确程度很大程度上取决于对各种气象资料的准确分析,这就要求我们必须及时有效地获取丰富的气象资料。而现代的气象资料种类繁多,来源也多种多样,因此高效的资料收集工作显得格外重要。
目前各气象保障单位向外发布气象资料的手段很多,最常见的有WWW、FTP等方式。就方便性和及时性而言,FTP发布依然是比较有效的一种手段。现在常见的一些FTP下载工具通常只提供简单的单站点、单个文件或目录下载,不能对资料进行有效的筛选。这对需要大量实时和历史资料的气象预报工作来说,资料下载无疑将成为一件灾难性的工作。本文作者利用C++开发了一套多源气象资料定时收集系统,充分利用现有的FTP协议,并结合Windows的多线程和数据库技术,可以对气象资料的下载来源、种类、时间等进行定制,满足气象资料下载的实时性和多样性要求。
2 關键技术解析
2.1 多线程编程
Windows系统是一个提供线程操作的系统,它可以使我们更好地利用计算机的系统资源和网络带宽,提高下载效率。Windows支持两种线程模式,一种是辅助线程,一种是用户界面线程。它们的不同之处在于用户界面线程通常有窗口,具有自己的消息循环,而辅助线程则没有窗口,因此不需要处理消息。辅助线程的编程工作相比较而言比较简单,而且通常更加有用。在本系统的开发中,我们使用用户线程作为主线程,接受用户请求,监控辅助线程的工作状态;利用辅助线程来完成FTP下载的主要工作。对于不同的资料来源,可以由用户线程建立多个辅助线程,同时进行工作。
辅助线程的工作通常分下面几步来完成:
1) 构建全局函数
全局函数供主程序在启动辅助线程时使用,它应该返回一个UINT,并以一个32位值(声明为LPVOID)作为参数,在启动线程的时候可以通过它来传递任何需要在辅助线程中使用的参数。它的基本声明应该和下面的声明相类似:
UINT ThreadProc(LPVOID lpVoid){
…… //自己的处理代码
return 0;
}
2) 启动辅助线程
启动辅助线程的工作一般应在主程序中进行,比如要启动上面定义的辅助线程,过程如下所示:
CWinThread* pThread=
AfxBeginThread(ThreadProc, //辅助线程的处理函数
GetSafeHwnd(),//传递的参数指针
THREAD_PRIORITY_NORMAL);//优先级
辅助线程在全局函数返回后自动终止,而且如果调用它的进程终止,辅助线程也将终止。
3) 辅助线程和主线程的通信
辅助线程和主线程之间的通信最便捷而有效的方式就是辅助线程向主线程发送消息,窗口句柄的传递可以在AfxBeginThread中进行,消息发送的函数原型为:
LRESULT SendMessage(
HWND hWnd, // 目标窗口句柄
UINT Msg, // 要发送的消息名
WPARAM wParam, // 第一个消息参数
LPARAM lParam // 第二个消息参数
);
2.2 数据库编程
因为需要对各个站点的参数和下载文件的大小、时间等进行定制,普通的文本文件存储此类信息的能力有限,而且也不便于修改和维护,因此在本系统中采用Access数据库来保存这些信息。VC++对数据库的访问提供了很好的支持。这里采用CDatabase和CRecordset两个MFC类来访问数据库。具体步骤如下:
1) 建立数据源
建立数据源有很多种办法,我们直接用Office的Access应用程序建立一个名为FileTrans.mdb的数据文件,然后利用向导建立需要的信息表FtpSrcTable和FtpDenTable,主要包括FTP服务器IP地址、端口号、登录用户名、密码、文件扩展名、处理时间间隔等参数信息。
2) 建立数据库连接
要建立和数据源的联接需要使用CDatabase类的成员函数:
virtual BOOL OpenEx( LPCTSTR lpszConnectString, DWORD dwOptions = 0 );
其中lpszConnectString参数用来指定数据源的位置和参数信息(访问用户名、密码等);dwOptions用来指定打开方式。如果返回值为真的话表示连接成功,可以进行下一步了。
3) 读取参数信息
连接数据库成功后,可以直接用CRecordset类的构造函数构造一个CRecordset对象CRecordset* pRS=new CRecordset(pDB);(其中pDB为连接数据库指针)。利用构造好的pRecordset对象就可以操作打开的数据库,读取相关的参数信息。
2.3 FTP访问
关于FTP的访问,既可以利用现有的FTP控件,也可以直接使用Windows提供的API函数。由于现有的FTP控件提供的功能过于简单,同时也不便于多线程编程,因此本系统直接采用Windows提供的API函数进行FTP访问。主要的API函数如下:
1)InternetOpen,初始化一個Win32 Internet应用。
2)InternetConnect,连接FTP服务器。
3)FtpSetCurrentDirectory,设置FTP服务器的当前虚拟目录。
4)FtpFindFirstFile,查找FTP服务器上的第一个索引文件。
5)InternetFindNextFile,查找FTP服务器上的下一个文件。
6)FtpGetFile,从FTP服务器下载指定的文件并存储到本地磁盘上。
7)InternetCloseHandle,关闭Internet连接。
这些API函数的详细参数说明请大家参阅MSDN等资源,这里不再赘述。
3 关键模块实现
系统的完整实现不可能在文中一一列出,这里只给出系统的几个关键模块的功能实现。
3.1 读取参数信息
读取表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; //数据库值变量
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参数为自定义的一个结构体,用来存储一个辅助线程用到的参数信息。
3.2 FTP下载辅助线程函数
FTP的文件和目录列表功能一次连接只能列举一个特定虚拟目录下的文件和子目录,因此如何遍历站点的所有目录和文件就成为系统功能实现的关键。这里采用简单的递归函数解决这个问题,程序列表如下:
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 結束语 本软件在Windows2003以上系统上编译通过,实际运行稳定,在单位的气象资料收集工作中发挥了较好的作用。