Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9727872
  • 博文数量: 1227
  • 博客积分: 10026
  • 博客等级: 上将
  • 技术积分: 20273
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-16 12:40
文章分类

全部博文(1227)

文章存档

2010年(1)

2008年(1226)

我的朋友

分类: C/C++

2008-04-23 22:05:16

介绍一种数据库远程备份方案

作者:

功能简述

  服务端(发送方)和客户端(接收方)定好数据库结构及内容格式后,由服务端将数据库生成指定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 = (cbLeftToReceive 0 );

	

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库文件的功能。服务端和客户端没有实现多线程发送/接收等。

阅读(448) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~