1、概述
随着Internet/Intranet的普及以及WEB技术的发展,人们对信息的需求越来越强烈,数据库与Internet/Intranet应用软件的集成已经成为了非常迫切的问题。互联网上数据库产品的复杂,有限的带宽,以及互联网上软件产品的跨平台性,将是我们遇到的最大困难。本文通过作者的工作实践,介绍了JDBC规范及应用JAVA的SOCKET机制,以及JAVA的客户/服务器计算模式,最后根据以上的原理,实现了一个简单的基于客户/服务器的JAVA查询远端服务器上的数据库的小例子。
2、问题提出
当我从事Internet/Intranet软件开发时,为了达到很好的兼容性,安全性以及跨平台性,不得不采用JAVA编程,但是JAVA数据库方面的不足,确实众所周知。虽然JDBC技术在一段时间以前就已经发布了,但是对于大多数数据库产品而言,却要为其编制特殊的驱动。虽然有一些数据库厂商用JAVA编写了自己产品的驱动(如IBM的DB2数据库软件),但是这类产品的价格和复杂的使用方法却是使一些中小企业和Internet爱好者们望而却步,另外,在有限的带宽下如何减少数据的流量等一系列问题,将是解决这类问题的最大的绊脚石。因此,我们必须解决这类问题,并且产生一个跨平台性的,能挂接多种数据库的,基于客户/服务器的软件解决方案。
3、分析问题
以上的问题我们可以用JAVA的Socket机制实现客户/服务器,然后在服务器端用JDBC来完成客户端所提交的查询要求,因此,将问题分为如下两个方面。
(1)JAVA中的Socket机制
在各种网络的客户/服务器应用中,客户与服务器之间的通讯机制是多种多样的,但大多数都采用的是基于TCP/IP的Socket机制来完成的,Socket是两个程序间用来进行双向传输的网络通讯端点,在服务器程序方面通过IP在网络中标识自己,然后,通过一个客户端程序知道的端口号来提供服务,而客户端在网络中通过服务器的IP来找到服务器,通过连接他的端口号来获得服务器的某项服务。当然,计算机也拥有一些内置的用来提供其他服务的端口和空闲的端口,这样这些空闲端口就可供程序员来使用。由于Socket通讯机制是一种较为底层的通讯机制,所以通过Socket的数据表示是一种原始的字节流信息。那么客户端 服务器的程序就应该按照程序员制定的一种约定来进行数据的格式化处理后才能进行具体的应用,这种约定实际上就是一种协议。
Socket通讯机制提供了两种通讯方式,一种是有连接的,另一种是无连接的。有连接的方式是指程序在开始时,双方就建立了连接,形成了一条通讯链路,这条通讯链路一直存在,直到任意一方终止连接为止。在连接的同时,双方就通过这个通讯链路进行I/O操作,这种方式是可靠的而且是全双工的。无连接的方式是指提供不可靠的连接,数据传送时是以数据报的方式发送,类似与我们的寄信。本文采用的为有连接方式。
JAVA在现实中有两个类用于Socket连接,一个是ServerSocket类,应用于服务器方;一个是Socket,应用于客户方。在服务器端用如下语句对端口进行初始化及监听:
try{
ServerSocket server=new ServerSocket(9001);//监听9001端口,此端口不能与系统的
//端口号冲突
Socket socket=server.accept();//阻塞进程,一旦有客户连接初始化socket类
DataInputStream in=new DataInputStream(socket.getInputStream()); //建立输入流
PrintStream out=new PrintStream(socket.getOutputStream()); //建立输出流
}catch(IOException e){}
在客户端用如下语句即可与服务器连接:
try{
socket=new Socket(hostname,9001);//连接以hostname的服务器,端口号为9001
//这里的hostname为服务器的IP地址
in=new DataInputStream(socket.getInputStream());//初始化输入流
out=new PrintStream(socket.getOutputStream());//定义输出流
}catch(IOException e){}
通过以上语句后,在客户与服务器之间就建立了in和out这两个输入和输出流,那么就可以进行通信了。最后,为了在服务器端能响应多个客户端的请求,在服务器的输入和输出流的处理中应用了多线程,是每一个线程对应一个客户端的服务,由于JAVA的内存管理是一种????管理,当某一个客户的连接断开后,服务器程序分配给这一个客户的线程的资源就会被自动收回。其多线程的语句如下:
new ServerThread(clientname,clientcount,socket).start();
//初始化一个新的ServerThread类(注:ServerThread类为程序的一个线程类
在以上的语句中clientcount为传给ServerThread类的线程号。
经过以上的语句后,客户端与服务器端之间就建立了联系,而且实现了多线程。
(2)JDBC浅析。
随着JAVA的兴起,JAVA的子公司JAVASOFT为了实现JAVA存取数据库而提供了一套数据库连接规范JDBC(JAVA DATABASE CONNECTIVITY)有一些数据库厂商根据这个数据库规范开发了自己的数据库JAVA的API,但这些在价格和使用的复杂度方面往往让人难以接受。另外,由于这些API增加了很多功能,是基于浏览器的JAVA APPLET加载的速度变慢而且影响了速度。而本程序是为一定需要而设计的故代码很小,执行速度相对加快。
JAVA为了实现数据库的连接而推出了JDBC规范,JDBC设计是基于X/OPEN SQL CLI(CALL LEVEL INTERFACE)这一模型,定义了一组API对象和方法,程序员可以使用这些API与数据库程序进行交互。JDBC API是一个应用于数据库的接口,用来解释和执行来自应用程序中的SQL语句,并返回结果集。它可以全部用JAVA写作为APPLET的一部份下载,又可以是一种本地模块与现有的数据库连接。如SUN和INTERSOLV合作开发的JDBC-ODBCBRIDGE就是一种通过JDBC来访问机器上的已定义的ODBC数据源,JDBC-ODBCBRIDEG可以通过在JDBC和ODBC之间建立一个桥梁,让JAVA程序通过JDBC来访问ODBC中的数据源。
在JDK1.1.5以上的版本中已经包含了JDBC-ODBCBRIDGE,读者如没有也可从SUN的网站上下载。在我们的程序中即是采用了这一种方式来实现对各种ODBC数据源的访问的。下面我们简要的介绍一些本套程序要用到的一些基本的类,读者如想获得更多的帮助可以察看SUN JDBC的帮助文档。
DriverManager类用于处理驱动程序的装入。使用该类来定义一些程序中使用的所要连接的驱动,在本程序中就是JDBC-ODBCGRIDGE;
Connection类定义了一个到特定数据库的连接。当程序定义了驱动以后,就要与数据库建立连接,该类将完成这些工作;
Statement类提供了一个执行SQL语句的容器。用来提交一个SQL查询;
ResultSet类用于控制结果类的存取。其作用是获得查询结果的一些信息,如结果的纪录数,字段数,字段名,结果集等与结果相关的信息。
(注意!由于本程序意在介绍编程方法和思想,所以JDBC的一些具体类的使用方法不属于本文的范畴。有关其中具体的使用方法将会在后面程序中写出。)
四、程序实现
本套示例程序采用客户/服务器的模式,因此存在客户端与服务器端两套程序。下面分别介绍两套程序的实现方法和程序片断,完整程序将在后面附上。
(1) 服(2) 务器端程序
作为服务器端的程序,本示例才用JAVA的Application方式来编程,生成一个应用程序,可直接在JAVA的解释环境下运行。这个服务端程序的功能是监听服务器的某一个端口,当有客户程序进行连接时分配一个单独的线程为该客户服务,接受客户端的SQL查询,然后连接到服务器的数据库,把查询后的结果以数据流的方式返回给客户端。当客户端断开连接后,终止该线程,收回分配给该客户的资源。本程序的界面比较简单如图4-1所示,由两个Label框来显示一些信息。Label1用来显示服务器程序监听的端口号。Label2用来显示当有客户程序连接似的客户的连接数目,当客户端提交申请时,Label2还显示客户的查询的SQL语句及查询结果,该Label框是被每一个线程公用的,所以当有很多客户连接时,是很繁忙的。Button1按钮的作用是结束该服务器程序。
该客户程序由两部分组成:一个为应用程序的主类Server类,另一个为线程类ServerThread类。下面我们将分别介绍这两个类。
Server类:该类是由Frame类派生而来。
Server(String title):该函数为Server类的构造函数,主要用来进行程序的一些初始化工作。它调用AddComponent()函数来生成程序的界面。
AddComponent():该成员函数用来生成程序的界面,主要是在Frame中添加了四个Label框和一个Button按钮以及处理按钮的点击事件。
StartListen():该成员函数用来当程序启动时监听9001端口(注:该端口可由用户自己定义一个,但不要与系统冲突),然后启动一个无限循环来监听客户的连接,当有客户连接时生成一个线程为该客户服务。
Main():该成员函数是JAVA Application的程序入口。
ServerThread类:该类是由Thread类派生而来,主要功能为接受用户要求,查询数据库,以及返回查询结果。由如下成员函数组成:
ServerThread(Label lab,int i,Socket s):该函数为ServerThread类的构造函数,主要功能为初始化该类。lab参数为一个Label类,用来显示一些系统