分类:
2008-10-13 17:07:12
介绍一种数据库远程备份方案
作者:
功能简述
服务端(发送方)和客户端(接收方)定好数据库结构及内容格式后,由服务端将数据库生成指定XML格式文件后, 通过Socket发给客户端。客户端接收成功后,用收到的XML库格式文件,生成数据库。
用例图:
设计
根据以上需求,采用VC6.0生成两个工程文件:DBClent(客户端及接收方)和DBServer(服务端及发送方)。其中DBClient负责接收XML库格式文件并生成数据库,DBServer负责生成指定格式的XML库文件与发送此文件给DBClient。
总体类图:
实现
1、数据库转换为XML格式设计 规定:数据库用Database,在其节点属性中用name来指定数据库名称。其子节点可能有多个表,表节点名为:Table,其节点属性中name用来指定表名称。表节点下包括表结构描述Struct和内容描述Content。表结构中列名用节点Field表示,名称/类型/长度等在其属性中表示。表内容中用节点Record表示每个记录,详细信息在属性中表示。
示例: DBTest.xml
2、服务端(发送方)实现 采用Socket套接字在指定端口发送生成的XML库格式文件。生成程序界面效果如下:
示例代码如下:
// 发送指定的XML文件 #define PRE_AGREED_PORT 8686 // 端口号 #define SEND_BUFFER_SIZE 4096 // 缓冲区大小 BOOL CDBServerDlg::SendFileToRemoteRecipient(CString fName) { AfxSocketInit( NULL ); CSocket sockSrvr; sockSrvr.Create( PRE_AGREED_PORT ); // 指定端口创建socket sockSrvr.Listen(); // 侦听端口上的客户端 CSocket sockConnection; sockSrvr.Accept( sockConnection ); // 用另一个socket接收连接 // 是否成功 BOOL bRet = TRUE; int fileLength, cbLeftToSend; // 文件长度及发送进度 BYTE* sendData = NULL; // 发送数据缓冲指针 CFile sourceFile; CFileException fe; BOOL bFileIsOpen = FALSE; if( !( bFileIsOpen = sourceFile.Open( fName, CFile::modeRead | CFile::typeBinary, &fe ) ) ) { TCHAR strCause[256]; fe.GetErrorMessage( strCause, 255 ); TRACE( "SendFileToRemoteRecipient encountered an error while opening the local file\n" "\tFile name = %s\n\tCause = %s\n\tm_cause = %d\n\tm_IOsError = %d\n", fe.m_strFileName, strCause, fe.m_cause, fe.m_lOsError ); bRet = FALSE; goto PreReturnCleanup; } // 首先,发送文件长度 fileLength = sourceFile.GetLength(); fileLength = htonl( fileLength ); cbLeftToSend = sizeof( fileLength ); do { int cbBytesSent; BYTE* bp = (BYTE*)(&fileLength) + sizeof(fileLength) - cbLeftToSend; cbBytesSent = sockConnection.Send( bp, cbLeftToSend ); // 是否出错 if( cbBytesSent == SOCKET_ERROR ) { int iErr = ::GetLastError(); TRACE( "SendFileToRemoteRecipient returned a socket error while sending file length\n" "\tNumber of Bytes sent = %d\n" "\tGetLastError = %d\n", cbBytesSent, iErr ); bRet = FALSE; goto PreReturnCleanup; } // 发送成功后, 剩余发送总数= 总长度-已发送长度 cbLeftToSend -= cbBytesSent; } while( cbLeftToSend > 0 ); // 然后,发送文件数据 sendData = new BYTE[SEND_BUFFER_SIZE]; cbLeftToSend = sourceFile.GetLength(); do { // 从文件中读取指定缓冲字节 int sendThisTime, doneSoFar, buffOffset; sendThisTime = sourceFile.Read( sendData, SEND_BUFFER_SIZE ); buffOffset = 0; do { doneSoFar = sockConnection.Send( sendData + buffOffset, sendThisTime ); // 是否出错 if( doneSoFar == SOCKET_ERROR ) { int iErr = ::GetLastError(); TRACE( "SendFileToRemoteRecipient returned a socket error while sending chunked file data\n" "\tNumber of Bytes sent = %d\n" "\tGetLastError = %d\n", doneSoFar, iErr ); bRet = FALSE; goto PreReturnCleanup; } // 发送成功后, 剩余发送字节及偏移 buffOffset += doneSoFar; sendThisTime -= doneSoFar; cbLeftToSend -= doneSoFar; } while ( sendThisTime > 0 ); } while( cbLeftToSend > 0 ); PreReturnCleanup: // 结束及清理 // 释放内存及关闭打开句柄 delete[] sendData; if( bFileIsOpen ) sourceFile.Close(); sockConnection.Close(); return bRet; }3、客户端(接收方)实现 接收XML库格式文件,并生成数据库。程序界面效果如下:
示例代码如下:
// 接收指定的XML文件 #define PRE_AGREED_PORT 8686 // 指定端口号 #define RECV_BUFFER_SIZE 4096 // 缓冲区大小 BOOL CDBClientDlg::GetFileFromRemoteSender(CString strIP, CString fName) { // 创建客户端socket AfxSocketInit( NULL ); // 初始化socket CSocket sockClient; sockClient.Create(); // 连接到指定IP和端口号 sockClient.Connect( strIP, PRE_AGREED_PORT ); // PRE_AGREED_PORT 端口号被指定为 8686 // 传送是否成功 BOOL bRet = TRUE; int dataLength, cbBytesRet, cbLeftToReceive; // 接收数据长度及进度 BYTE* recdData = NULL; // 接收数据缓冲 CFile destFile; CFileException fe; BOOL bFileIsOpen = FALSE; // 打开或创建指定文件,用来接收数据 if( !( bFileIsOpen = destFile.Open( fName, CFile::modeCreate | CFile::modeWrite | CFile::typeBinary, &fe ) ) ) { TCHAR strCause[256]; fe.GetErrorMessage( strCause, 255 ); TRACE( "GetFileFromRemoteSender encountered an error while opening the local file\n" "\tFile name = %s\n\tCause = %s\n\tm_cause = %d\n\tm_IOsError = %d\n", fe.m_strFileName, strCause, fe.m_cause, fe.m_lOsError ); bRet = FALSE; goto PreReturnCleanup; } // 首先,获取文件长度 cbLeftToReceive = sizeof( dataLength ); do { BYTE* bp = (BYTE*)(&dataLength) + sizeof(dataLength) - cbLeftToReceive; cbBytesRet = sockClient.Receive( bp, cbLeftToReceive ); // 是否出错 if( cbBytesRet == SOCKET_ERROR || cbBytesRet == 0 ) { int iErr = ::GetLastError(); TRACE( "GetFileFromRemoteSite returned a socket error while getting file length\n" "\tNumber of Bytes received (zero means connection was closed) = %d\n" "\tGetLastError = %d\n", cbBytesRet, iErr ); bRet = FALSE; goto PreReturnCleanup; } // 接收成功,剩余长度 cbLeftToReceive -= cbBytesRet; } while( cbLeftToReceive > 0 ); dataLength = ntohl( dataLength ); // 然后,每次接收指定缓冲的数据 recdData = new byte[RECV_BUFFER_SIZE]; cbLeftToReceive = dataLength; do { int iiGet, iiRecd; iiGet = (cbLeftToReceive0 ); PreReturnCleanup: // 结束及清理 // 释放内存及关闭打开句柄 delete[] recdData; if ( bFileIsOpen ) destFile.Close(); sockClient.Close(); return bRet; } // 解析XML库并生成数据库 bool CBulidDB::Parse_XML_Document() { if( !m_bDone ) { if( Is_Tag( " " ) ) { // 获取生成数据库名称 if( Is_Having_Attribute( "name" ) ) m_strDBName = Get_Attribute_Value(); } if( Is_Tag( " " ) ) { // 获取库内表名 if( Is_Having_Attribute( "name" ) ) m_strTableName = Get_Attribute_Value(); } // 库名或表名为空退出 if( "" == m_strDBName || "" == m_strTableName ) return false; // 获取主程序所在路径,存在sPath中 CString sPath; GetModuleFileName( NULL, sPath.GetBufferSetLength( MAX_PATH + 1 ), MAX_PATH ); sPath.ReleaseBuffer(); int nPos; nPos = sPath.ReverseFind( ''\\'' ); sPath = sPath.Left( nPos ); CString lpszFile = sPath + "\\" + m_strDBName; CFileFind fFind; BOOL bSuccess; bSuccess = fFind.FindFile( lpszFile ); fFind.Close (); CDaoDatabase db; // 数据库 CDaoRecordset RecSet( &db ); // 记录集 // 是否已有创建好的库文件,没有则创建它 if( !bSuccess ) { // 创建Mdb库 db.Create( lpszFile ); // 移动节点到指定位置 Go_to_Parent("Table"); Go_to_Child("Struct"); Go_to_Child( "Field" ); // 获取库结构 CString sqlCmd = "CREATE TABLE " + m_strTableName + "("; while( Is_Tag( "
" ) && Is_Child_of( "
" ) ) { CString strField = ""; // 列名 if( Is_Having_Attribute( "fieldName" ) ) strField = Get_Attribute_Value(); sqlCmd += strField + " "; // 列类型 if( Is_Having_Attribute( "fieldType" ) ) strField = Get_Attribute_Value(); sqlCmd += strField + "("; // 列长度 if( Is_Having_Attribute( "fieldLength" ) ) strField = Get_Attribute_Value(); sqlCmd += strField + "),"; // 同一级下一节点 if( !Go_Forward() ) break; } // 删除尾部多余逗号 if( -1 != sqlCmd.ReverseFind( '','' ) ) sqlCmd.Delete( sqlCmd.GetLength() - 1, 1 ); sqlCmd += ");"; // 创建库结构 db.Execute( sqlCmd ); CString strQuery = "SELECT * FROM " + m_strTableName; // 打开已创建的数据表 RecSet.Open( AFX_DAO_USE_DEFAULT_TYPE, strQuery, 0 ); // 移动节点到指定位置 Go_to_Parent("Table"); Go_to_Child("Content"); Go_to_Child( "Record" ); // 创建库内容记录 while( Is_Tag( " " ) && Is_Child_of( "
" ) ) { CString sqlRecordCmd = "INSERT INTO " + m_strTableName + "(Name,Age) VALUES("; CString strRecord = ""; // 名字 if( Is_Having_Attribute( "name" ) ) strRecord = Get_Attribute_Value(); sqlRecordCmd += "''" + strRecord + "'', "; // 年龄 if( Is_Having_Attribute( "age" ) ) strRecord = Get_Attribute_Value(); sqlRecordCmd += strRecord + ")"; // 插入记录 db.Execute( sqlRecordCmd ); // 同一级下一节点 if( !Go_Forward() ) break; } // 关闭记录集及库 RecSet.Close(); db.Close(); // 完成 m_bDone = true; AfxMessageBox( lpszFile + "Access库成功创建!" ); return true; } else { AfxMessageBox( lpszFile + "Access库已经存在!" ); } } return false; } 小结 数据库远程备份基本功能已实现,还有很多待完善的地方,如服务端没有实现直接读取数据库生成指定XML库文件的功能。服务端和客户端没有实现多线程发送/接收等。
--------------------next---------------------徐景周您好,很想和你联系,可是按你原来的邮箱发信,总是被打回来,你现在用什么邮箱?能给我的邮箱发一封测试信件?我有事向您请教,是关VC2005下用MFC操作Word2003的问题,这是你很熟悉的内容吧?我的邮箱是:yuds@163.com等你的回信.谢谢! ( 笑看人间 发表于 2006-7-9 2:26:00)
远程数据传输或交换,目前偶认为最好是。net 的web services。真的非常方便。 ( hailbo 发表于 2006-6-28 10:50:00)
再见徐景周,感觉真好!
-----------------------------
同感! ( whwjn 发表于 2006-6-22 3:44:00)
XML是有得有失,就多平台和兼容性来考虑,XML是最佳选择。即使不使用XML再很多时候数据也是需要加密的。
PS:再见徐景周,感觉真好! ( Vcleaner 发表于 2006-6-21 16:25:00)
本人认为在实际应用中使用XML进行数据传输并不一定是一个好的方案,XML冗余数据很大,各种标签等等。在真正应用中并不实用。真正应用还涉及到数据加密,数据交验等等 ( maimu 发表于 2006-6-20 13:14:00)
.......................................................
--------------------next---------------------
阅读(145) | 评论(0) | 转发(0) |给主人留下些什么吧!~~