200分,C++网络通信,高手,工作过的进!!
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了200分,C++网络通信,高手,工作过的进!!相关的知识,希望对你有一定的参考价值。
我正在搞C++网络通信编程这个模块,以下的问题不是非常的明白请大家不惜赐教,就这两百分了!!
第一 我假设一个服务端应用程序ServerDlg(简称A),一个客户端应用程序CliengDlg(简称B),现在我我在A中定义一个CSock类m,m在初始化函数中创建,监听,在B中创建一个CSocket
类n并且在初始化函数中创建,并连接到A,现在问题是A中应该在什么地方调用Accept()?可以不创建线程来Accept(),而直接在它的其他成员函数中Accept()吗?
第二,就算服务端Accept()成功,以后接受与发送数据是不是只有通过线程进行?接受,发送数据的代码可以写在Dlg的成员函数中吗?我没通过继承CSocket来实现,所以不能OnReceive(
)和OnSend()来实现通信?
第三,如果我把它换成异步CAsynSocket,那么上面感觉行不通了?怎样用到异步CAsynSocket?
第四,如果我把上面两个CSocket换成CAsynSocket,怎样才能使两个不同应用程序的对话框他们实现通信?关键是阻塞与非阻塞我不能灵活运用?(注意,我不是通过继承,而是维护
CSocket对象实现通信,这样做可以嘛??
CSocket是MFC在CAsyncSocket基础上派生的一个同步阻塞Socket的封装类。它是如何又把CAsyncSocket变成同步的,而且还能响应同样的Socket事件呢? 其实很简单,CSocket在Connect()返回WSAEWOULDBLOCK错误时,不是在OnConnect(),OnReceive()这些事件终端函数里去等待。你先必须明白Socket事件是如何到达这些事件函数里的。这些事件处理函数是CSocketWnd窗口对象回调的,而窗口对象收到来自Socket的事件,又是靠线程消息队列分发过来的。总之,Socket事件首先是作为一个消息发给CSocketWnd窗口对象,这个消息肯定需要经过线程消息队列的分发,最终CSocketWnd窗口对象收到这些消息就调用相应的回调函数(OnConnect()等)。 所以,CSocket在调用Connect()之后,如果返回一个WSAEWOULDBLOCK错误时,它马上调用一个用于提取消息的函数PumpMessage(...),就是从当前线程的消息队列里取关心的消息. PumnMessage会遇到下面几种情况: 1 提取出了(从消息队列中移出来Remove),用户正在使用的一个Socket发送的WM_SOCKET_NOTIFY消息和对应的 FD_XXX事件,返回True. 2 提取出了(从消息队列中移出来Remove),用户正在使用的一个Socket发送的WM_SOCKET_NOTIFY消息和对应的 FD_Close事件,返回True. 3 提取出了(从消息队列中移出来Remove),PumpMessage(..)设定的定时器的WM_TIMER消息,TimeOut事件为 CSocket的一个成员变量,m_nTimeOut=2000ms,返回True 4 用户调用了CancelBlockingCall() 设置错误代码为WSAEINTR(被中断了),返回False 5 用户一直没有取到用户正在使用的一个Socket发送的WM_SOCKET_NOTIFY消息和对应的FD_XXX事件,但是取到 了同一个线程中的其他Socket的WM_SOCKET_NOTIFY消息及其对应的消息,则将这些消息,加入到一个辅助性 的队列中去,以后处理. 6 没有取到任何WM_SOCKET_NOTIFY消息,则开始查看(不是取出来,而是查看)本线程的消息队列中是否有其他 消息,如果有的话,调用虚函数OnMessagePending(),来处理这些消息(OnMessagePending()用户可以自定义 在阻塞时,用户想要处理的消息),如果没有,则调用WaitMessage()开始等待消息的到来. 代码说明如下: A 先看Connect,因为Connect的阻塞的实现和Accept,Receive,ReceiveFrom,Send,SendTo都有点不同. 也许你们会奇怪为何是ConnectHelper(...),而不是Connect(...).其实ConnectHelper(...)才是Connect(..) 真正调用的东西,如下: BOOL CAsyncSocket::Connect(const SOCKADDR* lpSockAddr, int nSockAddrLen) return ConnectHelper(lpSockAddr, nSockAddrLen); //ConnectHelper( ... )为一虚函数 //继承自CAsyncSocket,Csocket有重新定义过. //这也是为什么CSocket是Public继承的原因 BOOL CSocket::ConnectHelper( ... ) //一旦调用 就先检查当前是否有一个阻塞操作正在进行 //如果是,立马返回,并设置错误代码. ...... ...... m_nConnectError = -1; //注意它只调用了一次CAsyncSocket::ConnectHelper( ... ) if( !CAsyncSocket::ConnectHelper( ... ) ) //由于Connect(...)要求自己和Server进行三步握手 //即需要发送一个Packet,而且等待回复(这个也就是 //涉及连接和发送数据操作的Socket API会阻塞的原因) //所以CAsyncSocket::ConnectHelper(...)会立即返回, //并且设置错误为WSAEWOULDBLOCK(调用该函数会导致阻塞) if( WSAGetLastError() == WSAEWOULDBLOCK ) //进入消息循环,以从线程消息队列里查看FD_CONNECT消息, //收到FD_CONNECT消息(在PumpMessage中会修改m_nConnectError),返回 //或者WM_TIMER/FD_CLOSE(return true,但不会修改m_nConnectError), //继续调用PumpMessage来取消息 //或者错误,那么就返回socket_error while( PumpMessages( FD_CONNECT ) ) if (m_nConnectError != -1) WSASetLastError(m_nConnectError); return (m_nConnectError == 0); //end while return false; return true; //在PumpMessages中会设置一个定时器,时间为m_nTimeOut=2000ms //当在这个时间之内,依然没有得到消息的话,就返回 BOOL CSocket::PumpMessages( UINT uStopFlag ) //一旦进入这个函数,就设置Socket当前状态为阻塞 BOOL bBlocking = TRUE; m_pbBlocking = &bBlocking; .................... ..................... .................... CWinThread* pThread = AfxGetThread(); //bBlocking是一个标志, // 用来判断用户是否取消对Connect()的调用 //即是否调用CancelBlockingCall() while( bBlocking ) //#define WM_SOCKET_NOTIFY 0x0373 //#define WM_SOCKET_DEAD 0x0374 MSG msg; //在此处只是取WM_SOCKET_NOTIFY 和 WM_SOCKET_DEAD消息 if (::PeekMessage(&msg, pState->m_hSocketWindow,WM_SOCKET_NOTIFY, WM_SOCKET_DEAD, PM_REMOVE)) if (msg.message == WM_SOCKET_NOTIFY && (SOCKET)msg.wParam == m_hSocket) //这个是PumpMessage的第2种情况 if (WSAGETSELECTEVENT(msg.lParam) == FD_CLOSE) break; //这个是PumpMessage的第1种情况 if(WSAGETSELECTEVENT(msg.lParam) == uStopFlag ) ......; break; //这个是PumpMessage的第5种情况 if (msg.wParam != 0 || msg.lParam != 0) CSocket::AuxQueueAdd(msg.message, msg.wParam, msg.lParam); //这个是PumpMessage的第3种情况 else if (::PeekMessage(&msg, pState->m_hSocketWindow,WM_TIMER, WM_TIMER, PM_REMOVE)) break; //这个是PumpMessage的第6种情况 if (bPeek && ::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) if (OnMessagePending()) else WaitMessage(); ..... //end while ////这个是PumpMessage的第4种情况 if (!bBlocking) WSASetLastError(WSAEINTR); return FALSE; m_pbBlocking = NULL; //将WM_SOCKET_NOTIFY消息发送到Creat CSocketWnd线程的消息队列中 //以便处理其他的Socket消息 ::PostMessage(pState->m_hSocketWindow, WM_SOCKET_NOTIFY, 0, 0); return TRUE; B 再看Receive(..) //其实CSocket的这种实现方式决定了,应用程序不能够在一个线程中Create一个socket, //然后创建一个新的线程来专门Receive,因为这个新的线程将永远不能取到FD_Read的事件, //因为并不是在这个线程中创建的CSocketWnd对象,它无法接受到发送到CSocketWnd的消息 //(在Windows中接受消息的主体是窗口) //Receive和Connect的实现方式的最大区别为 //Connect 是不断的调用PumpMessage(..) //而Receive则不断的调用自身 int CSocket::Receive(void* lpBuf, int nBufLen, int nFlags) if (m_pbBlocking != NULL) WSASetLastError(WSAEINPROGRESS); return FALSE; int nResult; while ((nResult = CAsyncSocket::Receive(lpBuf, nBufLen, nFlags)) == SOCKET_ERROR) if (GetLastError() == WSAEWOULDBLOCK) //一旦提取到FD_READ///FD_CLOSE///WM_TIMER时 // 就再次调用CAsyncSocket::Receive(...) if (!PumpMessages(FD_READ)) return SOCKET_ERROR; else return SOCKET_ERROR; return nResult; 最后,总结一下自己对CSocket的看法, 1 虽然它解决了结束阻塞线程的方法,调用CancelBlockingCall,但是多线程模式根本就不适合于CSocket 2 CSocket和CAsyncSocket利用Windows的消息模式将前台的界面处理和后台的网络通信都整合到消息传递模型 下,但是很明显,一旦后台的网络过于繁忙,则前台的处理可能就无法顾及,所以CSocket也就只能小打小闹
建议你多看MSDN,上面说的清楚,和孙鑫的VC++视频!! 参考技术A 来学习下 以后也是这方面的工人了 参考技术B MFC下面的两个SOCKET是基于SOCKET的异步选择模型,该模型通过WINDOWS的消息
机制完成事件的通知机制,该模型需要你提供一个窗体来完成对注册的网络事件
的响应。由于MFC中已经将这个窗体的操作实现了,所以程序员不需要在去做类
似的工作,需要做的是通过继承并重写CAsynSocket提供的虚函数
(如virtual void OnAccept( int nErrorCode )), 从而接收来自网络的消
息。
综上所述、关于问题:
第一,可以在单线程中操作CAsynSocket, CSocket. 但是不继承这两个类是不行
的,因为这两个提供的唯一的接口就是他自己本身的虚函数,也就是说他们即是
实现类,也是接口类。
A中需要家一个类继承CSocket 响应virtual void OnAccept( int
nErrorCode ); 在里面调用Accept()。
第二,同上、不需要单开辟线程,但是需要一个继承类,至少要响应
CAsyncSocket::OnReceive 才能知道什么时候会有网络数据来。
第三,CAsynSocket只是操作异步化了,响应方式没有变化,因为CSocket就是从
CAsynSocket继承来的。
第四,SOCKET通信,你需要做的就是了解I/O的模型,然后组织自己的应用协
议,管理内存。
CSocket提供了阻塞调用,但不是最关键的,关键是他提供了基于序列对象的
功能。
如果还有疑问, 可以再联系我。 我一直在做网络服务器程序的开发。如果你希
望开发出高并发的服务器程序,我们也可以切磋一下。 参考技术C - -!
楼主是初学者吧?
建议你去看看孙鑫的视频教程 然后我相信你就不会问这个问题了 参考技术D windwos下是现成的,你需要做的就是两头,数据组织和加工 第5个回答 2010-06-17 如果你不想继承的话,建议你直接使用Socket API。
使用函数int WSAAsyncSelect(
__in SOCKET s,
__in HWND hWnd,
__in unsigned int wMsg,
__in long lEvent
);
将socket和窗口句柄绑定起来,lEvent里选择FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT和FD_CLOSE等等你所希望处理的事件。
这样当事件发生是,系统会向你的窗口发送WM_SOCKET消息。
你可以重载WinProc函数来检查WM_SOCKET消息。
具体可以参考《WINDOWS网络编程技术》第8章
请高手进来帮忙,关于SQL和网络通信链接失败的问题,追加50分。
我有一网络销售软件,用VFP开发的基于SQL远程数据库的软件。
当保存时出现以下提示:
错误发生于:CS_APLICATION.SQLEXECUTE()
错误内容为:
连接错误:[Microsoft][ODBC SQL Server Driver]通讯链接失败
发生问题的SQL为:
Rollback
有一个按钮:【是(Y)】
点是后又出一个错误窗口:标题:错误
错误发生于:STOCKINFO.GENERATE_SALESINV()
错误内容为:
错误发生于:CS_APLICATION.SQLEXECUTE()
错误内容为:
连接错误:[Microsoft][ODBC SQL Server Driver][DBNETLIB]ConnectionRead (recv()).
发生问题的SQL语句为:
Insert into SalesInvoice(...............
下面是很多英文。是需要保存的明细的缩写。
这个软件全公司30多个用户都在不同的城市用,都没问题。只是在城市P里就有问题。版本都是相同的。都用网通的宽带。
在P市里刚开始半年没问题,最近网通出现故障,断网半天。修好后就这样了。但上网浏览,上QQ,下载都没问题。
但在家(P市里员工宿舍,离单位1000米远)试,没问题。同一台电脑,搬到单位不好使,搬到家好使,试了四台电脑,都是在家好使,在单位不好使。在单位的门市房的邻居家试也好使。
有方法的尽管说。没被采纳如果能给分也给分。
我都郁闷了5天了,请各位高手帮帮忙吧!如果能解决,追加200分。
先谢谢了!
也祝路过的天天开心,谢谢您的关注。
摘 要:
本文讨论了Visual Basic应用程序访问SQL Server数据库的几种常用的方法,分别说明了每种方法的内部机理并给出了每种方法的一个简单的实例,最后比较了每种方法性能和优缺点。
一、引言
SQL Server是微软推出的中小型网络数据库系统,是目前最常用的数据库系统之一。随着SQL Server网络数据库应用程序日益增多,这种Web数据库应用系统的正常运行一般依赖于已存在的用户数据库。创建维护数据库的工作可用SQL Server提供的SQL Enterprise Manager工具来进行,如能提供一种定制的数据库管理工具,通过管理应用程序来管理数据库及其设备,对用户来说无疑是最理想的。
Visual Basic作为一种面向对象的可视化编程工具,具有简单易学,灵活方便和易于扩充的特点。而且Microsoft为其提供了与SQL Server通信的API函数集及工具集,因此它越来越多地用作大型公司数据和客户机—服务器应用程序的前端,与后端的Microsoft SQL Server相结合,VB能够提供一个高性能的客户机—服务器方案。
二、VB访问SQL Server数据的常用方法
使用Visual Basic作为前端开发语言,与SQL Server接口有几种常用的方法,即:
①数据访问对象/Jet
②为ODBC API编程
③使用SQL Server的Visual Basic库(VBSQL)为DB库的API编程
④RDO 远程数据对象(RemoteData Objects)
⑤ADO 数据对象(Active Data Objects)
1、数据访问对象/Jet
VB支持Data Access Objects(DAOs)的子集。DAO的方法虽然不是性能最好的管理客户机—服务器之间的对话方式,但它的确有许多优点。DAO/Jet是为了实现从VB访问Access数据库而开发的程序接口对象。使用DAOs访问SQL Server的过程如下:应用程序准备好语句并送至Jet,Jet引擎(MASJT200.DLL)优化查询,载入驱动程序管理器并与之通讯,驱动程序管理器(ODBC.DLL)通过调用驱动器(SQLSRVR.DLL)的函数,实现连接到数据源,翻译并向SQL Server提交SQL语句且返回结果。下面是一个用DAOs访问SQL Server的VB实例。
注释:Form Declarations
Dim mydb As Database
Dim mydynaset As Dynaset オ
Private Sub Form_Load()
Set mydb = OpenDatabase("", False, False, "ODBC; DSN=Myserver; WSID=LCL; DATABASE = sales")
Set mydynaset = mydb CreateDynaset("Select*from Customers") オ
End Sub ァ
上述例子是以非独占、非只读方式打开sales数据库,并检索Customers表中的所有字段。OpenDatabase函数的最后一个参数是ODBC连接字符串参数,它指明了Microsoft Access连接到SQL Server所需要知道的一些内容。其中“DSN”为数据源名,“WSID”为工作站名,“DATABASE”为所要访问的数据库名。
2、利用ODBC API编程
ODBC(Open Database Connectivity)的思想是访问异种数据库的一种可移植的方式。与数据资源对话的公用函数组装在一个称为驱动程序管理器(ODBC.DLL)的动态连接中。应用程序调用驱动程序管理器中的函数,而驱动程序管理器反过来通过驱动器(SQLSRVR.DLL)把它们送到服务器中。
下面的代码使用上面一些函数先登录到一个服务器数据库,并为随后的工作设置了语句句柄。
Global giHEnv As Long
Global giHDB As Long
Global giHStmt As Long
Dim myResult As integer
Dim myConnection As Srting
Dim myBuff As String*256
Dim myBufflen As Integer
If SQLAllocEnv(giHEnv)<>SQL_SUCCESS Then
MsgBox"Allocation couldn注释:t happen!"
End If
If SQLAllocConnect(giHEnv,giHDB)<>SQL_SUCCESS Then
MsgBox "SQL Server couldn注释:t connect!"
End If
myConnection="DSN=myServer;UID=LCL;PWD=;APP=ODBCTest;WSID=LCL;DATABASE=sales"
myResult=SQLDriverConnect(giHDB,Test,form1.hWnd,myConnection.len(myConnection),myBuff,256,myBufflen,SQL_DRIVER_COMPLETE_REQU IE D)
myResult=SQLAllocStmt(giHDS,giHStmt)
myResult=SQLFreeStmt(giHStmt,SQL_COLSE)
rsSQL="Select * from Customers Where City = "Hunan""
myResult = SQLExecDirect(giHStmt,rsSQL,Len(rsSQL))
3、使用VBSQL对DB库API编程
DB库是SQL Server的本地API,SQL Server的Visual Basic库(VBSQL)为Visual Basic程序员提供API。从一定意义上说,VBSQL是连接Visual Basic程序到SQL Server的性能最好最直接的方式。VBSQL包含以下三个文件:
VBSQL.VBX: 包含库函数,具有访问重要的消息和处理错误的能力
VBSQL.BI:包括所有的常量和变量说明
VBSQL.HLP:Windows帮助文件,使用VBSQL的指南
使用VBSQL时,必须将VBSQL.BI加入到Visual Basic工程文件中,并确保VB程序运行时有VBSQL.VBX文件。
一般的DB库API编程的过程是这样的:先通过调用SqlInit对DB库进行初始化,再调用SqlConnection打开一个连接,然后就可做一些工作。下面的代码是一个初始化DB库并登录到服务器的通用例程。
Private Sub InitializeApplication()
DBLIB_VERSION=SqlInit()
If DBLIB_VERSION=""Then
MsgBox"Could not initialize DBLIB!Exit application.", MB_ICONEXCLAMATION
End If
End Sub
Private Function LoginToServer() As integer
loginToServer=SUCCEED
Status%=SqlSetloginTime%(loginTimeOut)
If giSqlConn<>0 Then
SqlClose(giSqlConn) 注释:关闭已打开的连接
giSqlConn=SqlOpenConnection(gsServerName, gsLoginID, gsPassword, ProgramName, ProgramName)
If giSqlConn<>0 Then
liresuit=SqlUse(giSqlConn,"Sales")
Else
LogintoServer=FAIL
End If
End Function
4、RDO 远程数据对象(RemoteData Objects)
要讨论RDO,就必然要谈到DAO。RDO是从DAO派生出来的,但两者很大的不同在于其数据库模式。DAO是针对[记录(Records)]和[字段( Fields)],而RDO是作为[行(Rows)]和[列(Columns)]来处理。也就是说DAO 是ISAM模式,RDO是关系模式。此外DAO是访问Access的Jet引擎(Jet是ISAM)的接口,而RDO则是访问ODBC的接口。
可见,RDO是综合了DAO/Jet、VBSQL/DBLib以及ODBC的优点的对象(Object)。需要强调的是,RDO是包裹着ODBC API的一层薄薄的外壳, 被设计成在后台(服务器端)有数据库存在的前提下运行,同时也是针对SQL Server和Oracle而特别设计的。
RDO的优势在于它完全被集成在VB之中。此外,直接访问SQL Server存储过程、完全支持T-SQL、T-SQL调试集成在开发环境中、Visual Database Tools的集成化等,也是RDO的长处。
在RDO的对象和集合中,有很多对数据库的状态和设定进行操作的属性(Property),以及对数据库进行操作的方法(Method)。利用这些,从RDO2.0起就可以开发事件驱动的数据库应用程序。
RDO对象与VB中其他对象的概念相同。与VB用的ActiveX控件(以往称为Custom Control或OCX、VBX)相似的是,RDO也带有属性和方法;但同Spread、InputMan等普遍应用的ActiveX控件不同的是,RDO没有自己的用户界面,因而可以和VB标准的Timer控件归为同一类。当然也可以将RDO看作调用ODBC API函数,进而对后台数据库操作加以控制的对象。在RDO的属性和方法中,包含了对单个的ODBC API函数以及一连串API函数的调用。
①rdoEngine对象
最初调用RDO对象以及RDC(远程数据控件)时,自动生成rdoEngine对象的附带事件(incident)。rdoEngine用于对RDO全局属性的参数、选项进行设置,是在RDO的阶层结构内处于最上层的对象,包含了所有的其他对象。
rdoEngine对象与DAO/Jet不同,虽然被多个应用程序共享,但体现rdoEngine对象的设定值的属性却并不共用,而是在各自的应用程序的程序界面中对其分别加以设定。这些设定值对其他使用RDO以及RDC的应用程序没有任何影响。rdoEngine不是集合的要素,而是重新定义的对象,rdoEngine对象不能被追加作成对象属性的初值。
②rdoEnvironment对象
RDO对象在自动创建rdoEngine对象时,将rdoEnviroment对象的初始值生成并保存为rdoEnviroments(0)。一般情况下,应用程序中不必追加rdoEnvironment对象,大多只需对已有的rdoEnviroments(0)进行操作就可以了。只有在支持一个以上事务(Transaction),需要将用户名和口令信息分别处理的情况下,利用rdoCreateEnvironment方法将特定的用户名和口令值做成新的rdoEnvironment对象。在这个方法中可以指定固有名、用户名和口令,如果所指定的值与rdoEnvironments集合的已经存在的成员名称相同,会产生错误。新建的rdoEnvironment对象自动追加在rdoEnvironments集合的最后。调rdoCreateEnvironment方法时,其name参数可以是长度为0的文字列,这时新的rdoEnvironment对象将不会被追加在rdoEnvironments集合之中。
③rdoConnection对象
rdoConnection对象用于同SQL Server的连接管理。
下面是与SQL Server连接的例子。
用OpenConnection方法的一个实例。
设定的DSN为MyDSN:
Dim Cn As rdoConnection
Dim En As rdoEnvironment
Dim Conn As String
Conn = "DSN = MyDSN; UID = Jacob;" & "PWD = 123456; DATA BASE = MyDb;"
Set Cn= En.OpenConnection("", rdDriverPrompt, False, Co nn)
Set Cn= En.OpenConnection(Prompt:= rdDriverPrompt, Rea dOnly:= False,Connect:= Cnn)
用EstablishConnection方法的一个实例。
这里以独立的rdoConnection对象为例说明与SQL Server的连接。
Public WithEvents Eng As rdoEngine
Public WithEvents Cn As rdoConnection
Private Sub Form_Load()
Set Eng = New rdoEngine
Set Cn = New rdoConnection
With Cn
.Connect = "UID = ; PWD = ;" & "DATABASE = pubs; DSN = biblio"
.LoginTimeout = 5
.EstablishConnection rdoDriverNoPromt, True, rdAsyncEna ble
End With
End Sub
在这个例子中,Form_Load函数对rdoEngine和rdoConnection对象进行初始化。这里有一点需要注意,rdoConnection对象是处于独立的状态之下,即使是处于未连接状态也可以设置属性的值。接下来是rdoConnect对象的事件处理程序。从RDO 2.0起可以实现异步方式(rdAsyn cEnable),EstablishConnection就设定为该值。在异步状态下,不必等待与数据库的连接,程序可以迅速从Form_Load 函数中退出。 然后是BeforeConnect事件,该处理在与数据库的连接开始以前被激发,此时不能进行有关终止连接的操作:
Private Sub Cn_BeforeConnect(ConnetString As String, Pro mpt As Variant)
MsgBox "正在连接" & ConnectString, vbOKOnly, "连接前"
End Sub
连接完成之后的事件处理:
Private Sub Cn_Connect(ByVal ErrorOccurred As Boolean)
Dim M As String
If ErrorOccurred Then
For Each er In rdoErrors
M = M & er & vbCrLf & M
Next
MsgBox "连接失败" & vbCrLf & M
Else
MsgBox "连接成功"
注释:这是确认连接状态的测试代码
Cn.Excute "use pubs"
End Sub
RDO连接处理结束后,在该事件中确认连接成功与否。连接成功的情况下ErrorOccurred返回False,失败时为True,由此可以对rdoErrors集合进行检测:
Private Sub Eng_InfoMessage()
For Each er In rdoErrors
Debug.Print er
Next
RdoErrors.Clear
End Sub
不能与SQL Server连接的原因多种多样,有可能是由于对数据库的访问权限、网络连接问题、数据库表的信息错误、SQL Server同时连接的许可数、资源不足等等,具体情况需要与网络管理员商量。 断开连接的操作非常简单,但又很重要,因为RDO不提供自动断开的功能。
Cn.Close
Set Cn = Nothing 注释:释放对象所占的内存资源
En.Close
Set En = Nothing 注释:释放对象所占的内存资源
VB是对象语言,Form、ActiveX控件也都是对象。使用对象后必须养成将对象设为Nothing把它从内存中释放的编程习惯。这样可以预防很多不可预测错误,往往程序中发生原因不明的错误时,其原因就在于此。
5、ADO 数据对象(Active Data Objects)
ADO是基于全新的OLE DB技术,OLE DB可对电子邮件、文本文件、复合文件、数据表等各种各样的数据通过统一的接口进行存取。随着ActiveX控件的升级(Windows 98的ActiveX 5.0),RDO将被以ActiveX技术为基础的ADO接口所替代。下面将介绍基于ActiveX技术的ADO访问SQL Server 6.5数据库的技术和方法。基于浏览器的ADO接口常用函数如下:
(1)取当前的工作数据库
由于管理任务一般都必须在Master库中完成,因此在执行管理任务之前,最好保存当前工作库,以便完成任务之后再切换回原来的任务。
Public Function SQLGetCurrentDatabaseName(Cn As ADODB.Connection) As String
Dim sSQL As String
Dim RS As New ADODB.Recordset
On Error GoTo errSQLGetCurrentDatabaseName
sSQL="select CurrentDB=DB_NAME ( )"
RS.Open sSQL, Cn
SQLGetCurrentDatabaseName=Trim $ (RS! CurrentDB)
RS.Close
Exit Function
errSQLGetCurrentDatabaseName:
SQLGetCurrentDatabaseName=" "
End Function
(2)取SQL Server安装目录下的DATA子目录路径
取SQL Server的设备文件缺省目录,返回如D:MSSQL DATA。
Public Function SQLGetDataPath(Cn As ADODB.Connection) As String
Dim sSQL As String
Dim RS As New ADODB.Recordset
Dim sFullPath As String
On Error GoTo errSQLGetDataPath
sSQL="select phyname from master..sysdevices where name=注释:master注释: "
RS.Open sSQL, Cn
sFullPath = RS! phyname
RS.Close
SQLGetDataPath=Left $ (sFullPath, Len(sFullPath) -10) 注释:MASTER.DAT的大小
Exit Function
errSQLGetDataPath:
SQLGetDataPath=" "
End Function
(3)创建一个新数据库
Public Function SQLCreateDatabase65 (Cn As ADODB.Connection,sDBName As String, sDataDeviceName As String, nDataSize As Integer, Optional sLogDeviceName, Optional nLogSize) As Boolean
Dim sSQL As String
On Error GoTo errSQLCreateDatabase65
Dim sDB As String
sDB =SQLGetCurrentDatabaseName(Cn)
sSQL = "USE master"
Cn.Execute sSQL
sSQL ="CREATE DATABASE" & sDBName
sSQL = sSQL &" ON " & sDataDeviceName & "=" & nDataSize
If Not IsMissing(sLogDeviceName) And Not IsMissing(nLogSize) Then
sSQL = sSQL & "LOG ON" & sLogDeviceName & "="& nLogSize
End If
Cn.Execute sSQL
sSQL = "USE" & sDB
Cn.Execute sSQL
SQLCreateDatabase65 = True
Exit Function
errSQLCreateDatabase65:
On Error Resume Next
sSQL = "USE " & sDB
Cn.Execute sSQL
SQLCreateDatabase65 = False
End Function
(4)判断一个数据库是否存在
Public Function SQLExistDatabase(Cn As ADODB.Connection, sDBName As String) As Boolean
Dim sSQL As String
Dim RS As New ADODB.Recordset
Dim bTmp As Boolean
on Error GoTo errSQLExistDatabase
sSQL = "select CntDB = count ( * ) "
sSQL = sSQL & "From master.dbo.sysdatabases"
sSQL = sSQL & "Where name = 注释: "& sDBName & " 注释: "
RS.Open sSQL, Cn
If RS! CntDB = 0 Then bTmp = False Else bTmp = True
RS.Close
SQLExistDatabase = bTmp
Exit Function
errSQLExistDatabase:
SQLExistDatabase = False
Exit Function
End Function
(5)删除一个数据库
Public Function SQLDropDatabase (Cn As ADODB.Connection, sDBName As String) As Boolean
Dim sSQL As String
On Error GoTo errSQLDropDatabase
If Not SQLExistDatabase(Cn, sDBName) Then
SQLDropDatabase = True
Exit Function
End If
Dim sDB As String
sDB = SQLGetCurrentDatabaseName(Cn)
sSQL = "Use master"
Cn.Execute sSQL
sSQL = "DROP DATABASE " & sDBName
Cn.Execute sSQL
sSQL = "USE " & sDB
Cn.Execute sSQL
SQLDropDatabase = True
Exit Function
errSQLDropDatabase:
On Error Resume Next
sSQL = "USE " & sDB
Cn.Execute sSQL
SQLDropDatabase = False
End Function
三、性能比较及应用说明
用VB开发基于SQL Server的数据库系统,以上几种访问SQL Server的方法各有各的特点。DAOs方法是基于对象的,因而便于使用,但是它是从Visual Basic到SQL Server最慢的连接方式。ODBC API和VBSQL方法从本质上讲是基于程序的。ODBC API方法通用性好,允许最强的互操作性,编程简单,但速度慢于VBSQL方法。VBSQL方法通过VBSQL控件,提供了重要的SQL Server前端应用程序所需的灵活性、强大功能和良好性能。它具有真正的事件驱动及错误处理能力,完全支持异步处理、游标和计算列等。这些都是VBSQL方法超出其它方法的优势,但其编程稍复杂。RDO是位于ODBC API之上的一个对象模型层,它依赖ODBC API、ODBC驱动程序以及后端数据库引擎来实现,用RDO所需的程序短小(约250 KB)、快速。RDO具备基本的ODBC处理方法,可直接执行大多数ODBC API函数,RDO包含在VB 4.0/VB 5.0企业版中,由MSRDO32.DLL动态连接库来实现。RDO是综合了DAO/Jet、VBSQL/DBLib和ODBC的优点的对象模型,包含ODBC API应用层,设计为在后台(服务器端)有数据库存在的前提下运行,是针对SQL Server和Oracle而特别设计的。RDO的优势在于它完全被集成在VB之中,可直接访问SQL Server存储过程、完全支持T-SQL、T-SQL调试集成在开发环境中、Visual Database Tools的集成化等。但微软已宣布今后不再对VBSQL/DBLib进行升级,而ODBC API函数一般的编程方式也不为人们所喜爱,RDO的应用将逐渐减少。至于实际使用哪一种接口方式,在很大程度上依赖于用户的应用程序的具体情况而定。
四、VisualBasic访问数据库的前景
近来随着Web应用软件的迅速发展和现有数据存储形式的多种多样,Visual Basic访问数据库的解决方案面临诸如快速提取分布于企业内部和外部有用商业信息等的多种挑战。为此Microsoft提出一种新的数据库访问策略,即“统一数据访问”(UniversalDataAccess)的策略。“统一数据访问”提供了高性能的存取包括关系型和非关系型在内的多种数据源,提供独立于开发工具和开发语言的简单的编程接口,这些技术使得企业集成多种数据源、选择更好的开发工具、应用软件、操作平台、建立容易维护的解决方案成为可能。
“统一数据访问”的基础是Microsoft的数据访问组件。这些组件包括ActiveXDataObjects(ADO)、RemoteDataService(RDS,也称“高级数据连接器或”ADC)、OLEDB和ODBC。
以上是关于200分,C++网络通信,高手,工作过的进!!的主要内容,如果未能解决你的问题,请参考以下文章
请高手进来帮忙,关于SQL和网络通信链接失败的问题,追加50分。