目录
摘要……………………………………………………………………………………………… 1
一、引言……………………………………………………………………………………… 4
二、HTTP 服务器模型 …………………………………………………………… 5
(一)﹑超文本传输协议…………………………………………………………………5
(二)﹑Java API中网络类包简介……………………………………………………6
(三)﹑HTTP客户机与服务器的交互涉及骤………………………………… 7
(四)﹑HHTP操作的一次事务(Transaction)……………………………… 8
(五)﹑HTTP消息…………………………………………………………………………8
三﹑HTTP服务器的兼容性………………………………………………………9
四﹑Java实现多线程HTTP服务器设计…………………………………… 10
(一)﹑Java语言网络编程………………………………………………………… 10
(二)﹑服务器总体设计……………………………………………………………… 12
1﹑总体设计的关键点……………………………………………………………… 12
2﹑模块设计………………………………………………………………………………12
3﹑HTTP服务器各类之间关系………………………………………………… 14
(三)﹑服务器详细设计分析…………………………………………………………15
1﹑线程重用………………………………………………………………………………15
2﹑各类和接口的主要功能…………………………………………………………15
(四)﹑Java多线程机制的应用………………………………………………………17
五﹑HTTP服务器运行………………………………………………………………23
六﹑结束语………………………………………………………………………………… 24
参考文献………………………………………………………………………………………25
一﹑引言
SUN推出Java语言之后,全世界的目光都被这个神奇的语言所吸引,Java语言从一开始就内置了支持网络编程,因此更容易编写网络应用程序;这也是与其他编程语言不同的地方。Java提供了大量类和接口可以处理各种网络协议,共享信息,并行计算,应用程序服务等优秀特点使java技术将会得到广泛的应用。Java是实现定制客户机和服务器的优秀工具,因为它支持多种在网络上发送和接收数据﹑提供服务﹑接收数据﹑调用服务的技术。Java作为一种面向分布式计算机环境语言,提供了完全意义的多线程支持,利用多线程编程接口,可以方便地写出支持多线程的应用程序,这一点也是Java所特有的。随着Internet的普及和Web技术的迅速发展,客户/服务器结构逐渐转向浏览器/服务器方向,Web文本发布具有简洁﹑生动﹑形象等特点,适用于发布信息,开发基于Browser/Server模式的Web数据库信息检索系统得到广泛应用。Web服务基于超文本传输协议(HTTP协议)。Java语言所具有的良好兼容性﹑安全性﹑健壮性及可扩展的跨平台等特性,使得Java成为开发基于B/S模式的Web数据库应用的首选计算机语言。本文使用Java开发一个高效兼容的HTTP服务器原型。(如图一)。
图一 HTTP服务器原型
该多线程HTTP服务器使用ServerSocket(服务器套接字)运行在服务器专用的TCP端口监听接收来自客户端的请求。当远程主机上的客户Socket试图与指定端口建立连接时,服务器被激活,根据具体情况来确定重用旧线程或生成一个新线程来响应客户端的请求直到该客户退出。服务器的主线程仍然处于监听状态,等待新客户请求。
二﹑HTTP 服务器模型
(一)﹑超文本传输协议(HTTP,Hypertext Transfer Protocol)是Web浏览器与Web服务器之间通信的标准协议,是个相当详细的协议。位于TCP/IP协议的应用层,具有简单﹑通用﹑无状态﹑灵活﹑无连接及面向对象等特点是互联网中最核心的协议之一。HTTP是RPC式的协议。RPC就是远程调用(Remote Procedure Call),是请求远程机服务和处理机制。RPC和编程语言中的过程调用相似,基于C/S和B/S模型,它建立在请求/响应(Request/Response)的基础上,服务端和客户端可能位于不同的系统上,客户端通过TCP SOKET向服务器端口(默认为80)发送包含请求方法﹑URL(是对统一资源定位符(Uniform Resource Locator)用来标识Internet上资源的位置。URL类是不能再小的最小类,它扩展Java.lang.Object。)和协议版本信息,请求建立Socket连接,客户的请求通过Socket被服务器接收,服务器对客户的请求信息参数进行分析处理,响应一个状态行,对客户的请求服务做出回应,把相应的资源文档以流形式通过Socket传给客户浏览器,整个过程见(图二)所示:
图二 Socket 连接
(二)﹑Java API中网络类包简介:
Java API中按类的形式提供了三种不同层次的网络技术机制,用URL(统一资源定位符)访问网络资源的类,利用Socket(套接字)通讯的类以及DataGran(数据报)类。前两种类方法更能体现Java作为网络语言的特点,支持URL类实际上也是依赖于下层支持Socket类方法来实现的。相对来讲,URL是对WEB的高层联接。Socket是一种底层的连接,客户机和服务器通过写入到Socket的字节流进行通信。Java系统的网络通信机制如图二所示,本文主要讨论基于Socket类的通信机制。
协议组TCP/IP的组合, TCP/IP技术的核心部分是传输层(TCP和UDP协议),网络层(IP)协议和物理层(面向各种物理硬件技术),能实现这三层协议的内核可称之为TCP/IP网络操作系统.TCP/IP协议技术中的中下层协议向外提供的只是原始的编程界面,而不是直接的用户服务,用户服务要靠核外的应用程序实现.应用程序和TCP/IP核心协议关系如图四所示.
(三)﹑HTTP客户机与服务器之间的交互涉及下列步骤:1﹑HTTP服务器听取请求:HTTP服务器打开听取端口,通常是端口80,这是在一个线程中进行的,然后线程阻止,等待入站的HTTP请求。2﹑HTTP客户机构造HTTP URL:HTTP客户机需要建立与HTTP服务器的初始连接。为此要使用URL,通常情况下是以http://www.any_server.com的形式。这里HTTP客户机指定HTTP服务器上的缺省资源,通常是页面index.htm或default.htm 。缺省Web页面通常是站点的主页。有时候URL会更加复杂。3﹑HTTP客户机建立连接:HTTP客户机建立发送套接,连接Web服务器。发送套接让客户机向服务器提交HTTP请求。4﹑HTTP服务器接受连接:HTTP服务器生成接受套接,从HTTP客户机接受请求。通常,接受套接在另一线程中运行,提高Web服务器的性能。5﹑HTTP客户机构造一个HTTP请求,发送到HTTP服务器:建立与HTTP服务器的连接之后,客户机构造HTTP请求并通过套接将其传递到HTTP服务器。6﹑HTTP服务器处理请求:HTTP服务器继续听取接受套接中的入站请求。得到请求时,服务器分析入站请求,并采取请求要求的操作。7﹑HTTP服务器将HTTP响应发送给HTTP客户机:HTTP服务器构造HTTP响应,有六个部分(1)、HTTP版本;(2)、HTTP状态码;(3)、HTTP状态描述(可选);(4)、响应头字段(可选);(5)、响应体(可选);(6)、内容长度(可选)。服务器通过与HTTP客户机的连接返回响应。8﹑HTTP客户机处理响应:HTTP客户机收到服务器的响应时,客户机分析响应,然后处理响应。9﹑关闭套接连接:HTTP客户机或服务器关闭连接。
(四)﹑一次HHTP操作称为一次事务(Transaction)。HTTP定义的事务处理由以下四步组成:
1﹑ 客户与Web服务器建立连接,打开一个称为socket(套接字)的虚拟文件,此文件的建立标志着连接成功。
2 ﹑ 客户通过socket向Web服务器提交请求。一般为GET或POST命令。
3 ﹑ 通过HTTP协议传送给Web服务器。接纳则进行事务处理,处理结果传回客户端。
4 ﹑客户和服务器断开连接。
HTTP采用TCP连接,该连接仅在此事务中保持,浏览器和服务器都不会记忆上次的连接状态。HTTP之所以采取这种无状态机制,完全是为了提高服务器的工作效率。在WEB中点击一个超链接时,浏览器有可能从当前站点转到另一个站点。因此,当文档传输完毕时,服务器都假定用户要退出对文本服务器上的文档的浏览连接,因而断开连接。如果继续浏览,就再次建立连接。如果用户确实要退出时,服务器就不需要执行任何操作,因为连接已经断开了。
(五)﹑ HTTP消息包括HTTP请求信息和HTTP响应消息两种,一个HTTP请求格式如下图五所示:
图五 HTTP请求信息格式
HTTP响应信息格式如图六所示:
图六 HTTP响应信息格式
三﹑HTTP服务器的兼容性
(一)﹑ HTTP1.1的兼容性:
要实现一个实用高效的HTTP1.1和CGI兼容HTTP服务器, HTTP1.1主要通过引入持久连接和更好的缓存验证命令而提高性能,而且把HTTP响应代码增加一倍多。能够向上兼容HTTP/1.0,HTTP消息包括请求消息和响应信息两种。
(二)﹑ HTTP1.0的兼容性:
HTTP1.0兼容服务器总是要响应请求的HTTP版本,可能是HTTP0.9或HTTP1.0。HTTPResquest要检查请求中是否存在版本号。如果不存在,则假设为HTTP0.9。这个版本不用头。其中在HTTP1.0版本里请求头的method可能为GET﹑POST或HEAD,而在HTTP1.1版本中增加了PUT和DELETE两种请求。URL统一资源定位器标明了目的位置。
(三)﹑ 与旧版本HTTP的兼容性:
HTTP1.0规范强制要求与HTTP.09的向下兼容性。我们要保证HTTP服务器具有下列功能:
1﹑识别HTTP0.9与HTTP1.0请求的请求行格式。
2﹑了解HTTP0.9与HTTP1.0格式的任何有效请求。
3﹑响应与客户机使用的相同协议版本消息。
四﹑Java实现多线程HTTP服务器设计
(一)﹑Java语言网络编程
在java的客户/服务器应用中,客户与服务器之间的通讯一般是采用基于TCP/IP的Socket机制来实现的。当通信数据到达的是目的主机网络层,IP地址连同端口号一起,提供了对两台机器之间唯一的无二义性的连接的标识,这个连接叫套接字(Socket)。一对套接字为一标识了每个端口连接:(1)发送方套接字=源IP地址+源端口号;(2)接收方套接字=目的IP地址+目的端口号。
Socket类实现基于连接客户插口。我们可以利用Java API中的Java.net 包中的插口类提供的套接字功能进行网络编程。
Socket类(套接字类)提供的重要函数有getInetAddress( ), getPort( ), getInputStream( ), 和close( )。分别产生用于获得信宿机的地址、信宿机端口号、获得输入流、获得输出流、关闭套接字。
ServerSocket类(服务器套接字类)的重要函数有:getInetAddress( ), getLocalPort( ), accept( )和close( ),分别用于获得信宿机的Internet地址、本地端口、监听和关闭套接字。ServerSocket类实现了TCP服务器插口。它提供两种构造函数指定服务器插口所用的端口,以便监听到连接请求。利用Accept( )方法引发服务器插口监听并等待,一旦连接就返回一个实例。
自定义类包括(1)HttpServer类(服务器类),(2)ReThead(线程重用类),(3)HttpProcess(一个接口类)。此外还包括各个类的请求处理类3个:HttpFile(静态文件处理), HttpCGI(CGI处理) ,HttpException(出错处理);以及数据流处理2个:HttpInputstream(数据输入流)和HttpOutputstream(数据输出流)。
图七 重要类的交互
(二)﹑服务器总体设计
1﹑总体设计的关键点
根据HTTP工作原理,在这个设计中抓住了几个关键点:(1)持续监听。根据TCP/IP,作为服务器端进行监听。在这个设计中,采用了一个永真的循环,等待客户的连接请求。(2)可以同时接受多个用户的连接请求。每个用户拥有自己的线程。(3)实现了线程的重用性。线程重用服务可以自动收集可重用的线程,以免过的的创建与销毁线程增加系统的负担,提高了系统的效率。
2﹑模块设计
把Http服务器分成两个组成模块:客户请求处理模块和响应生成发送模块,其中客户请求处理模块负责接收客户的连接,分析请求中的各个协议参数,此外还负责调用响应生成发送模块。而响应生成发送模块的任务就是根据对客户请求的分析的结果查找资源,生成响应和发送响应。
(1)﹑客户请求处理模块的任务就是负责监听系统的端口,以获取客户机到达本机的连接。当获得一个连接请求时,就把这一连接所建立的套接字连接交给一个负责客户请求处理程序——Client类处理这一个客户的请求,之后程序继续监听等待下一个客户的连接。在一个Client对象建立之后,它就把其在初始化时获得的一个Socket对象的输入流重定向给一个Request对象,使其能通过Request类提供的接口方法获取这一用户请求的相关参数,如URL的组成请求方法,所指文件以及协议版本等数据。然后把这一个Request对象连同Socket对象的输出流传递给一个ResourceHandler类的实例,由这一个ResourceHandler对象完成生产发送对用户请求的响应。主要由三个部分组成:①守护程序:一个HttpServer类的实例对象;②客户处理程序:一个Client用户类,由HttpServer对象负责建立其实例和执行其运行线程;③请求分析程序:一个Request用户类,由Client对象初始化其对象实例,用于分析客户请求中的协议参数(如下图八)。其中,1——调用;2——Socket对象;3——Socket对象;4——Socket对象输入流;5——Request对象;6——Socket对象的输入流和Request对象。Request对象是指经分析的客户请求。
图八 请求处理模块
(2)﹑响应生成发送模块实现的功能就是从客户请求程序中接受一个Request对象和一个标识一个用户连接的客户Socket对象的输入流(HttpInputStream)对象。通过Request对象中获得客户请求的协议参数,查找客户请求指定的资源,根据查找的结果,生成相应的响应,并把生成的响应写入到指定的HttpInputStream对象中,其操作结果就是把响应送往客户。包含了以下对象:ResourceHandler对象(响应产生发送处理)、SCManager(响应的状态码管理)对象、StatusCode对象(状态码管理)、MimeType对象(媒体类型描述处理),通过相互调用协调,共同完成模块的功能(如图六)。其中:1——Socket对象的输入流和Request对象;2——文件名;3——文件描述;4——状态码;5——状态码相关描述;6——文件名;7——媒体类型描述。
图九 响应生成发送模块
3﹑HTTP服务器各类之间关系
在Http服务器的设计中,Httpserver是主类,提供了程序的入口,打开ServerSocket类,并且接受客户端的连接。对于每个连接将创建一个新的Httpserver对象去处理客户端的请求,并且启动一个新的线程去执行该处理。HttpInputstream类对于请求信息进行分析。HttpProcessor类则提供了真正处理的接口,它分别被HttpFile类,HttpCGI类,HttpException类来实现。最后将响应信息由HttpOutputStream类格式化输出到客户端。HTTP服务器类图设计如(图十)所示:
图十 HTTP服务器类图
(三)﹑服务器详细设计分析
1﹑线程重用
在这个设计中用Rethread类来划分线程,这个类用一个矢量(Vector)来存储线程。这样避免了旧线程的销毁,并且当客户端有请求过来时,避免了新线程的创建,提高了服务器的响应效率。
2﹑各类和接口的主要功能
(1)﹑HTTPServer
HTTPServer.java中定义了一个变量类resources,保存这个类的所有场景特定消息。把这个变量定义为静态的,因为他在静态main()方法中使用。
(2)﹑HTTPRequest
HTTPReques类处理HTTP请求。对于每个接受的连接,HTTPServer run()方法构造一个HTTPRequest对象并传入从accept()调用取得的套接参数,作为服务器与浏览器进行通信的端点。构造HTTPRequest对象之后,run()方法调用start(),使请求在并发运行的线程中处理。这样HTTPServer类的run()方法可以立即继续,等待新的入站连接。
(2)﹑HTTPInPutStream
对于每个请求信息,用HTTPInPutStream类来进行分析处理,包括解释请求和读取所有请求报头。把这个类中请求进行分解为方法﹑请求路径﹑查询字符串以及协议版本,并且读取所有随后的请求报头。
(3)﹑HTTPOutPutStream
经过处理后的响应信息经过HTTPOutPutStream类进行格式化输入到客户端,这个类为写HTTP响应提供了支持,在这个类中添加了形成和发送HTTP响应报头的支持,包括状态行和随后的报头。
(4)﹑HTTPProcessor
HTTPProcessor接口提供了真正处理的方法,HTTPFile类,HTTPCGI类,HTTPException类分别实现了该接口。根据HTTPInPutStream类的分析来确定所有请求的信息该有哪个类来处理。(HTTPFile类﹑HTTPCGI类或HTTPException类)
(5)﹑HTTPFile
该类实现了HTTPProcessor接口,用于处理静态文件资源的get和head请求。
(6)﹑HTTPCGI
该类实现了HTTPProcessor接口,用于处理CGI程序。
(7)﹑HTTPException
该类是IOException的子类,它用于把HTTP错误返回到客户机。它实现HTTPProcessor接口,因此当在响应客户机请求而被抛出时,他能够发送一个适当的HTTP错误信息到客户机。
(四)﹑Java多线程机制的应用
多线程(Multi-Thread)是Java的一个重要特性,指的是通过系统的调度使几个具有不同功能的程序流即线程同时并行的运行。
Java从语言一级提供对线程的支持,可由语言和运行系统联合提供对共享数据段的管理功能和同步机制,这样就使多线程并行程序设计相对比较容易。
在Java中,当我们创建一个新线程时,先通过对Java.lang.Thread类的继承来派生一个子类class MyApplication extends Thread { ··· } 然后,通过run( )方法实现线程的行为。class MyApplication extends Thread { public void run( ) { ··· } }最后,由子类生成一个对象,并且进行启动操作,这样就得到一个处于可运行状态的线程。生成对象是完成线程的创建,启动则是对已经创建的线程进行操作。
My Application ex=new My Application( );
Thread th=new Thread(ex);
Th.start( );
这个服务器端程序以最简单的方式运用Socket对服务器和客户机进行操作。服务器的全部工作就是等候建立一个连接,然后用那个连接产生的Socket创建一个InputStream以及一个OutputStream。在这之后,它从InputStream读入的所有东西都会反馈给OutputStream,直到接收到行中止(END)为止,最后关闭连接。
public class myServer // 这个是服务器端程序
{
// 设定服务程序端口号,大于1024
public static final int PORT = 8080;
public static void main(String[] args)
throws IOException
{
ServerSocket s = new ServerSocket(PORT);
System.out.println("Started:"+s);
try{
Socket socket = s.accept();
try{
System.out.println("连接被接受"+socket);
BufferedReader in =
New
BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out =
new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())),true);
while(true){
String str = in.readLine();
if(str.endsWith("END")) break;
System.out.println("Echoing: "+str);
out.println(str);
}
}
catch(Exception e)
{ System.out.println(e.toString());}
finally{
System.out.println("closing ...");
socket.close();
}
}
finally{
s.close();
} } }
图十一 多线程服务端实例
下面是该服务器设计的部分代码:
……
* get Connect Info
*/
public String getConnectInfo(){
String szClientHostName=this.m_sClient .getInetAddress().getHostName ();
String szClientAddress=this.m_sClient .getInetAddress ().getHostAddress();
int ClientPort=this.m_sClient .getPort ();
String szRet="";
szRet="Accepted connection from "+szClientHostName+"("+szClientAddress+") "+"on port "+ClientPort+".";
return szRet;
}
……
HTTPRequest(Socket client)throws IOException{
this.m_sClient =client;
this.m_In =new HttpInputStream(this.m_sClient.getInputStream());
this.getRequest ();
}
public String getLog(){
StringBuffer szRet=new StringBuffer() ;
szRet.append ("Received the following request:"+"\\n");
Enumeration enum=m_vLines.elements ();
String szstr1="";
while(enum.hasMoreElements()){
szstr1=(String)enum.nextElement();
szRet.append (szstr1+"\\n");
}
return szstr1.toString();
}
boolean isGetRequest() {
if(this.m_vLines .size ()>0){
String firstline=(String)this.m_vLines.elementAt (0);
if(firstline.length ()>0)
if(firstline.substring(0,3).equalsIgnoreCase ("GET"))
return true;
}
return false;
}
……
* only use to recognize methed "get"
*/
class HttpInputStream extends FilterInputStream {
HttpInputStream(InputStream in){
super(in);
}
public void addLine(String line) {
m_vLines .addElement(line);
}
public String readLine() throws IOException {
StringBuffer result=new StringBuffer ();
boolean finished=false;
boolean isenter=false
……
Public static void main (String[ ] args) throws IOException {
ServerSocket server = new ServerSocket (8080);
/*创建一个监听8080端口的服务器套接字*/
While(time){
Socket client = server.accept ( );
/*等待一个连接,如果该连接未被创建,本方法阻塞当前线程。返回值是一个Socket对象,服务器程序利用这个对象与连接的客户通信。*/
HttpServer HttpServer =new HttpServer(client);
/*创建一个HttpServer 类的对象*/
ReThread ReThread = new ReThread (Httpserver);/*创建新线程*/
Rethread.start ( );/*新线程启动*/
}
}
}
……
五﹑HTTP服务器运行
编译好各个类后,在命令行中输入:java HTTPServer.class,会提示服务器的版本号及运行正常等信息,然后在IE的地址栏中输入http://localhost:8080或http://127.0.0.1:8080进行测试,同时在另外一台PC机器上测试输入该机的IP地址:http://210.38.171.250:8080可以浏览到预先放置的index.htm网页。
图十二 服务器运行实例
在浏览器中打开的预先放置的index.htm网页:
图十三 测试实例
六﹑结束语
首先要感谢邹咸林老师对我毕业设计的细心指导和支持,感谢邹老师为我提供了许多重要的参考文献,以及宝贵的建议和意见。才得以完成该论文设计。
在这个设计中,利用了Java网络编程和Java多线程的特性开发了HTTP服务器原型,并且利用了线程的重用性,提高了运行效率。Java本身也是线程安全的,在实现服务器程序采用多线程设计,可以改善整个服务器应用系统的响应,提高程序的运行效率,降低客户等待时间。在实际运行时状态稳定、良好。容易在该设计的基础上进行扩展并构建成功能强大的服务器。Java语言也将会深入更广泛的网络应用方面。
七﹑参考文献
[1] [美]John Grifffin Pascal de Haan 等著,邱仲潘等译。 Java 网络编程指南 [M]-北京:电子工业出版社,2002.3
[2] [美]哈罗德(Harold,E.)编著;刘东华等译。JavaTM网络编程 –北京:中国电力出版社,2001
[3] 刘斌 编著,深入浅出Jbuilder8程序设计与开发 –北京:中国青年出版社,2003
[4] [美] Ivor Horton.编著,李昭智,王哲明,王红广等译。Java2编程指南 [M] –北京:电子工业出版社,2003
[5] 宋波﹑马黎 等。用Java实现一个多线程HTTP服务器[J], Journal of Shenyang Electric Power Institute , 1008-312X(2001)01-0029-04
[6] [美] Harvey M.Deitel; Paul J.Deitel 著 Java How to Program, Fourth Edition –Publishing House of Electronics Industry.2002
[7] 张晨等编著,Java2应用编程150例, -北京:电子工业出版社,2003.9下载本文