精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>编程开发>>● Java>>JAVA编程>>网络编程>>用Java2实现一个简单的WEB服务器――总体设计

主题:用Java2实现一个简单的WEB服务器――总体设计
发信人: zjxyz(xyz)
整理人: zjxyz(2002-04-10 19:11:20), 站内信件
首先说明一下,我的设计并不是什么很好的设计,至少目前还存在不少不规范的地方,例如我没有刻意的为了实现代码可扩充、维护而把一些地方写成松耦合;在我看来,工作之外的编程是一件很愉快的事情,没有必要把自己搞得那么辛苦。这样的程序有一个好处就是,代码比较适合初学者阅读,我本身出发的目的就是如此。  

这是一个参考例子,让大家可以迅速的知道怎么用Java写一个WEB服务器,过程是怎样的。我这组文章是面向代码的,我很讨厌某些人开口就谈编程思想,而一谈到显得就一副不耐烦的样子;我始终认为,代码实现不了的思想,就是废话。这篇文章就是讨论代码的实现,讨论的思想是当前代码可以实现的思想,其他废话,免谈。  

一、请求与响应的对象:  
上面提到,服务器跟浏览器互交时,主要有两个步骤,就是接收浏览器请求,响应浏览器的请求,所以我们在设计类的时候,可以把这两个步骤抽象成两个类:请求类HttpRequest,响应类HttpResponse。工作过程大致是这样的:  
我在HttpRequest、HttpResponse之上设计一个HttpHandler类,用于调度对浏览器的响应;在侦听服务器侦听到一个连接后,就把通过某些方式把Sokcet句柄传给HttpHandler类的实例,然后由HttpHandler的实例把Soket句柄传给一个HttpRequest的实例;经过HttpRequest处理后,HttpHandler根据HttpRequest处理的结果,经过处理后,HttpHandler调度HttpResponse的实例响应浏览器。  
下面详细介绍 HttpHandler 工作的情况,根据HttpRequest处理的结果,我们分几种情况处理:  
1、 静态的页面的请求,这时 HttpHandler 将生成 staticsou 的实例,并把HttpRequest、HttpResponse的实例传给staticsou 的实例,后面处理请求的工作就交给 staticsou 完成了,当处理完毕或者处理出错,程序会返回到 HttpHandler,然后 HttpHandler 会做一些善后处理工作;  
2、 CGI请求,这时 HttpHandler 将生成 cgiRunTime的实例来处理相关的请求,过程跟静态的页面的请求类似;  
3、 聊天室请求,这时 HttpHandler 将生成 chat 的实例,并把HttpRequest、HttpResponse的实例传给chat 的实例,不过聊天处理跟上面的处理略有不同,就是客户端的socket连接在该 HttpHandler 实例销毁时,并没有销毁,而是保存在侦听服务实例的一个Hash表里面,当有用户发信息上聊天室时,系统会遍历整个Hash表,把信息发到各个连接去。  
4、 代理请求,这时 HttpHandler 将生成 j2proxy 的实例,并把HttpRequest、HttpResponse的实例传给j2proxy 的实例,而j2proxy会分析HttpRequest里面的信息,连接远程的WEB服务器,然后把把WEB 的资源取回来然后通过HttpResponse的实例转发给客户端。  

上面所述的是客户端的一个连接请求发到服务器时,服务器所做的工作的过程,当然,我为了实现比较高的性能,我使用的多实例,多线程的手段。下面将简述我在这方面的设计思想。  

二、服务器实例  
系统的主类是从 org.zjxyz.j2wsp.j2wsp 开始的。  
而运行系统之前,必须按要求(请参阅附录1)写好配置文件,放一个目录里面,一个目录及目录里面的配置文件就为系统提供了一个实例的配置。  
当j2wsp的main 方法执行时,系统就会搜索配置目录下的子目录,将实例的配置逐个读出,然后根据实例的配置的生成若干服务线程,每个服务线程负责侦听不同的端口,而每个服务线程根据实例的配置又生成若干个处理线程,用于响应服务连接。当有客户连接入侦听的端口时,服务进程通过调度唤醒文章某个处理线程来响应客户的请求,这样的话,每个服务线程就能同时处理多个连接请求。  
大概设计思想就是如此,不算特别复杂,具体的实现,我在后面中将逐一介绍。  


附录一、  
设置与运行  

j2wsp中实例的含义  

j2wsp是按侦听的端口来划分实例的,在j2wsp中,所谓实例,就是由一个侦听线程和一组响应线程所组成的线程集合,每一个实例负责侦听一个端口,不同的实例拥有独立的配置文件,以提供有别于其他实例的服务。  

安装好的j2wsp在 j2wsp/conf/ 目录下有几个子目录,这些都是实例的配置目录,每个子目录里面都有三个文件分别是:  

chatinit.txt --聊天室初始化信息  
mime.types --mime类型定义文件  
httpd.conf --实例配置文件  

实例的配置目录的目录名是随意起的,只要放在 j2wsp/conf/ 目录下面就可以了,每个正确配置目录的实例必须包含上述文件。当j2wsp启动时,会去j2wsp/conf/目录读取各子目录下的配置文件,创建实例。  

实例的管理  

如果要创建一个新的实例,只需要在 j2wsp/conf/ 建立一个新的目录,把上述三个文件copy进去,然后依照运行的需要对 httpd.conf 文件进行修改,重新启动j2wsp即可。  

如果要删除一个实例,只把要删除的实例的配置目录删掉,重重新启动j2wsp即可。  



配置 httpd.conf  

以下是一个完整的 httpd.conf 内容,  

############################################  
Port 8000  
backlog 100  
MaxKeepAliveRequests 20  
DocumentRoot ..\htdocs  
DefaultType text/plain  
DirectoryIndex index.html index.htm  
AddHandler cgi-script .cgi .pl  
ScriptAlias /cgi-bin/ ../cgi-bin/  
#############################################  

配置文件的规则:  

主要格式与 apache 的配置文件相似:  
采用[名字][空格][值][空格][值]来描述一个配置项的内容;  
[名字]不区分大小写;  
文件中间允许空行;  
允许在配置文件里用 # 来注释一行内容;  

要注意的是,上述配置项都是缺一不可的,因为程序没有判断某个配置项是否为空,所以,当缺少配置项时,程序运行的过程中会抛出空指针的异常。  

下面就逐个解释配置项名含义:  

Port 实例侦听的端口,不允许与其他实例相同;  
backlog 侦听队列的最大等待连接数目;  
MaxKeepAliveRequests 该实例的服务线程数目,该数字的大小涉及到消耗的内存多少  
DocumentRoot Web服务器的根目录  
DefaultType 默认返回给浏览器的数据类型  
DirectoryIndex 默认的页面  
AddHandler 从第二个值开始,是被当作CGI处理的脚本文件的类型  
ScriptAlias 第一个值是 CGI 的影射路径,第二个值是CGI实际路径  



----
网易广州社区Java版
XYZ个人主页,提供一个公开源代码的WEB服务器+聊天室
冗談の言葉は无用だ…俺は最强だ!あんた ゃるじゃないか.だが...,世界じゃ二番目だ. 
手机号码归属地查询系统,可查出手机所属省份,所属城市,SIM卡类型,网友做的。 


[关闭][返回]