65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Java 中实现 Web 服务器

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (2投票s)

2010 年 6 月 8 日

CPOL

4分钟阅读

viewsIcon

79079

downloadIcon

4340

一篇关于如何在 Java 中实现功能性 Web 服务器的文章

什么是 Web 服务器?

Web 服务器是负责接收浏览器请求、检索指定文件(或执行指定的 CGI、PHP、ASP 脚本)并返回其内容(或脚本结果)的软件。如今,互联网上大多数 Web 服务器运行在 UNIX 机器上,尽管其他平台(如 Windows 95、Windows NT 和 Macintosh)上的服务器比例正在稳步增长。

Web 服务器首先通过*套接字*(一种通过网络通信的机制)检索请求。Web 服务器监听服务器机器上的特定端口,通常是端口 80。默认情况下,Web 浏览器使用端口 80 进行请求。

一旦服务器收到请求,它就会定位所请求的文档。它在*文档根目录*下查找文件。例如,如果根目录是*C:\AssWebSrv\htdocs*,而客户端请求文档* /public/docs.html*,则服务器将检索*C:\AssWebSrv\htdocs\public\docs.html*。

如果 URL 没有指定文件而只是指定了一个目录,服务器将返回*目录索引*。

服务器将文件内容连同一些*HTTP 响应头*一起发送回客户端。响应头中的数据包括*媒体类型*(也称为*内容类型*或*MIME 类型*),即文件的格式。确定格式的方式取决于服务器,但通常来自文档的后缀:*.html*被认为是 HTML 文档,*.pdf*被假定为 Adobe Acrobat 文档,依此类推。

Web 服务器主要有四种类型:

  • NCSA 服务器,由伊利诺伊大学厄巴纳-香槟分校的超级计算应用国家中心维护。
  • Apache,NCSA 的一个变体,已发展成为当今最受欢迎的 Web 服务器。
  • CERN 服务器,由万维网联盟维护。
  • Netscape 系列服务器,尽管 Netscape 服务器是商业产品。

AssoudiWebServer1.5 机制如下:

既然我们已经对 Web 服务器有了很好的描述,现在让我们开始探索 AssoudiWebServer1.5 及其机制。

AssoudiWebServer1.5 是一个完全用 Java 开发的 Web 服务器,而几乎所有 Web 服务器(如 Apache Web 服务器)都是用 C 语言开发的。

图 1:AssoudiWebSrv1.5 启动...

客户端(通常是浏览器)向服务器发出 HTTP 请求时,会为每个打开的文件打开许多套接字。例如,一个包含 10 张图片的 HTML 文件,服务器必须创建 10 个套接字来处理每个文件。所以您可能会问服务器如何同时处理许多文件。答案自然是使用线程。对于每个接受的请求,服务器必须创建一个新线程,该线程处理请求然后发送结果。

//
try{
	//creating a new ServerSocket Listing on SERVER_PORT 
	ServerSocket S=new ServerSocket(SERVER_PORT);
		
	try{
	while(true){
	//returning an established socket via the ServerSocket accept method 
	Socket sock=S.accept();
	try {
        System.out.println("[REMOTE HOST]: "+sock.getInetAddress().toString());
		System.out.println("[LISTNING ON PORT]: "+sock.getPort());              
            
		//calling the ServeurWeb constructor
		new ServeurWeb(sock,SERVER_ROOT,SERVER_HOMEPAGE,
				SERVER_ICONS,SERVER_LOG);
			 
		}catch ( IOException e ) {
        sock.close();//always close the socket
        }
        }
        }finally {
        S.close();//always close the ServerSocket
		}
		}catch(BindException B){
	//handling exception generated if they are already running server 
		System.out.println("SERVER Already Running");
		System.exit(0);
}
//	

ServeurWeb()ServeurWeb 类的一个实例,该类实现了 Runnable 接口。ServeurWeb 类型的对象都有一个 run() 方法,处理套接字的代码位于该方法的正文中。代码位于*AssoWebSrv.java*文件中。

//
//constructor ServeurWeb
class ServeurWeb implements Runnable {
public ServeurWeb(Socket s,String Sroot,String Shome,
	String Sicons,String Slog)throws UnknownHostException,IOException{
	...
	run();//invoking method run() to execute the thread code
	...
	}
}
//

图 2:服务器处理多个请求文件。

如前所述,一旦服务器收到请求,如果浏览器请求包含一个请求的文件,它就可以发送文件内容;如果 URL 只指定了一个目录,服务器就会返回*目录索引*。

图 3:主机 Zenith 下 /public 目录的索引。
//
public void Listdir(String directory,OutputStream pr,
	String HOST_NAME_LINK,String Icons_Path)throws IOException  {

 File DIR_FILE=new File(directory);

 String File_Separ_String=System.getProperty("file.separator");
 String ActualDir=this.DirectoryToList(this.getDirectory_Name());

  if(DIR_FILE.isDirectory()){
  pr.write(new String
	(""+HOST_NAME_LINK+"- /"+this.getDirectory_Name()+"").getBytes());
  String[] File_List=new String[DIR_FILE.list().length];
		
  /** File_List contain the List of files and sub directories 
contained in the current folder **/
  File_List=DIR_FILE.list();		
		...		
	}
}
//

其余代码可以在 WebServer 包中的*FileRead.java*文件中找到。

此时,我们的服务器不是动态的。它只能处理一些文本、HTML、图像文件。当 Web 服务器能够为用户提供交互式内容时,就称其为动态的。为此,AssoudiWebServer1.5 除了 cgi 程序外,还支持强大的脚本语言 PHP。

图 4:AssoudiWebServer1.5 执行 PHP 脚本。
//
public void ProcessCgi(String CGI_PHPFile,OutputStream ToBrowser,int CGI_PHP )
	throws IOException{

/**
  * Method ProcessCgi according to the CGI_PHP variable ,either directly execute
  * the file if it is a cgi program or call the PHP program which 
  *executes the PHP script 
  * then send the result to the server
  */	

 Runtime r=Runtime.getRuntime();	//creating an object Runtime by calling 
				//the getRuntime Method
 String cgiContent="";		//cgiContent contain the program STDOUT
 Process p=null;			//win32 process initialised to null

 switch(CGI_PHP){
	
 case CGI_PROG:
               p=r.exec(CGI_PHPFile);
               break;
 case PHP_PROG:
			   //the php program must be under the path c:\php
               p=r.exec("C:\\php\\php.exe "+CGI_PHPFile);
               break;
	}
/*we redirect the program STDOUT  to a bufferedReader */
	
	BufferedReader brcgi=
	
	
	newBufferedReader(newInputStreamReader(p.getInputStream()));
	while((cgiContent=brcgi.readLine())!=null){
		
/**Eliminate useless data generated by the program STDOUT */	
		
		if(cgiContent.startsWith("Status")||
		   cgiContent.startsWith("Content")||
		   cgiContent.startsWith("X-Powered-By"))
		{
		
		ToBrowser.write("".getBytes());
		ToBrowser.flush();
		
		}else
		{
		//we send the data redirected from the program STDOUT to the client
		ToBrowser.write((cgiContent+"\r\n").getBytes());
		ToBrowser.flush();
		}
}
//

此代码来自 package WebServer. 中的*FileRead.java*文件。

您可以看到,这里的端口号与通常的端口号 80 不同。这样做的原因之一是防止两个或多个服务器监听同一个端口号。

端口号是 AssoudiWebServer1.5 配置文件中定义的其他指令之一,以便由服务器管理员进行配置。

  • # 指令 ServerRoot 保存发布目录的路径
  • # 指令 Port 包含服务器监听的端口号
  • # 指令 Welcome 用于包含欢迎页面
  • # 指令 Icons 包含服务器图标所在的虚拟目录名称
  • # 指令 LogFile 指示服务器日志文件的路径

结论

即使 AssoudiWebServer1.5 似乎能够完成高效 Web 服务器所需的大部分工作,但它仍然不完整。我在下一版本中寻找添加对原生 servlets 和一些安全功能的支持。

AssoudiWebServer1.5 的 Linux 版本(即将推出...)。

如需更多信息或建议,请通过 a_othmane@hotmail.com 联系我。

作者:Assoudi Othmane

© . All rights reserved.