本章的Web服务器应用程序位于ex01.pyrmont包下,包括三个类: HttpServer Request Response应用程序的入口点(静态main()方法)在HttpServer类中。main()方法创建一个HttpServer实例,然后,调用其await()方法。顾名思义,await()方法会在指定端口上等待HTTP请求,对其进行处理,然后发送响应信息回客户端。在接收到关闭命令前,它会保持等待状态。该应用程序仅发送位于指定目录的静态资源的请求,如HTML文件和图像文件。它也可以将传入到的HTTP请求字节流显示到控制台上。但是,它并不发送任何头信息到浏览器,如日期或cookies等。下面几节将分别展示三个类的具体实现。1.3.1 HttpServer类HttpServer类表示一个Web服务器,具体实现如代码清单1-1所示。注意,为了节省空间,await方法在代码清单1-2中列出,并没有在代码清单1-1中重复。
这个Web服务器可以处理对指定目录中的静态资源的请求,该目录包括由公有静态变量final WEB_ROOT指明的目录及其所有子目录。WEB_ROOT的初始值为:
该代码清单包含一个名为webroot的目录,用于测试该应用程序的一些静态资源都位于该目录下。在该目录下还可以找到用于测试后续章节中应用程序的几个servlet程序。若要请求静态资源,可以在浏览器的地址栏或URL框中输入如下的URL:
http://machineName:port/staticResource若从另一台机器(不是运行应用程序的那台机器)上向该应用程序发出请求,则machineName是应用程序所在计算机的名称或IP地址;若在同一台机器上发出的请求,则可以将machineName替换为localhost,此外,连接请求使用的端口为8080。staticResource是请求的文件的名字,该文件必须位于WEB_ROOT指向的目录下。例如,如果你正使用同一台机器来测试该应用程序,你想让HttpServer对象发送index.html文件,就可以使用如下的URL:
http://localhost:8080/index.html若要关闭服务器,可以通过Web浏览器的地址栏或URL框,在URL的host:port部分后面输入预先定义好的字符串,从Web浏览器发送一条关闭命令,这样服务器就会收到关闭命令了。关闭命令定义在HttpServer类的SHUTDOWN静态final变量中:
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";因此,若要关闭服务器,需要使用如下的URL:
http://localhost:8080/SHUTDOWN接下来,看一下代码清单1-2中的await()方法。
该方法名之所以称为await(),而不是wait(),是因为wait()方法是java.lang.Object类中与使用线程相关的重要方法。await()方法会先创建一个ServerSocket实例,然后进入一个while循环:
当从8080端口接收到HTTP请求后,ServerSocket类的accept()方法返回,等待结束:
socket = serverSocket.accept();接收到请求后,await()方法会从accept()方法返回的Socket实例中获取java.io.InputStream对象和java.io.OutputStream对象:
input = socket.getInputStream(); output = socket.getOutputStream();然后,await()方法会创建一个ex01.pyrmont.Request对象,并调用其parse()方法来解析HTTP请求的原始数据:
// create Request object and parse Request request = new Request(input); request.parse();然后,await()方法会创建一个Response对象,并分别调用其setRequest()方法和sendStaticResource()方法:
// create Response object Response response = new Response(output); response.setRequest(request); response.sendStaticResource();最后,await()方法关闭套接字,调用Request类的getUri()方法来测试HTTP请求的URI是否是关闭命令。若是,则将变量shutdown设置为true,程序退出while循环。
// Close the socket socket.close (); //check if the previous URI is a shutdown command shutdown = request.getUri().equals(SHUTDOWN_COMMAND);ex01.pyrmont.Request类表示一个HTTP请求。可以传递InputStream对象(从通过处理与客户端通信的Socket对象中获取的),来创建Request对象。可以调用InputStream对象中的read()方法来读取HTTP请求的原始数据。Request类在代码清单1-3中给出。Request类有两个公共方法(parse()和getUri())和一个私有方法parseUri(),parse()方法和parseUri()方法分别在代码清单1-4和代码清单1-5中给出。
parse()方法用于解析HTTP请求中的原始数据。parse()方法会调用私有方法parseUri()来解析HTTP请求的URI,除此之外,并没有做太多的工作。parseUri()方法将URI存储在变量uri中。调用公共方法getUri()会返回HTTP请求的URI。注意 处理HTTP请求原始数据的方法会在第3章和随后章节的应用程序中给出。为了理解parse()和parseUri()方法是如何工作的,需要了解HTTP请求的结构,这在1.1节已经介绍过。在本章中,我们仅仅对HTTP请求的第一部分—请求行—感兴趣。请求行以请求方法开始,接着是请求的URI和请求所使用的协议及其版本,并以CRLF符结束。请求行中的元素以空格分开。例如,使用GET方法请求index.html文件的请求行如下所示:
GET /index.html HTTP/1.1parse()方法从传入到Request对象中的套接字的InputStream对象中读取整个字节流,并将字节数组存储在缓冲区中。然后,它使用缓冲区字节数组中的数组填充StringBuffer对象request,并将StringBuffer的String表示传递给parseUri()方法。parse()方法已经在代码清单1-4中给出。
parseUri()方法从请求行中获取URI。代码清单1-5展示了parseUri()方法的具体实现。parseUri()方法在请求中搜索第一个和第二个空格,从中找出URI。
ex01.pyrmont.Response类表示HTTP响应,其定义如代码清单1-6所示。
首先要注意的是Response类的构造函数会接收一个java.io.OutputStream对象,如下所示:
Response对象在HttpServer类的await()方法中通过传入从套接字中获取的OutputStream来创建。Response类有两个公共方法:setRequest()和sendStaticResource()。setRequest()方法会接收一个Reuqest对象为参数。sendStaticResource()方法用于发送一个静态资源到浏览器,如HTML文件。它首先会通过传入父路径和子路径到File类的构造函数中来实例化java.io.File类:File file = new File(HttpServer.WEB_ROOT, request.getUri());然后,它检查该文件是否存在。若存在,sendStaticResource()方法会使用File对象创建一个java.io.FileInputStream对象。然后它调用FileInputStream类的read()方法,并将字节数组写入到OutputStream输出中。注意,这种情况下,静态资源的内容是作为原始数据发送到浏览器的:
若文件不存在,sendStaticResource()会发送错误消息到浏览器:
若要运行应用程序,需要在工作目录中执行下面的命令:
java ex01.pyrmont.HttpServer若要测试应用程序,可以打开浏览器,在地址栏或URL框中输入如下URL:http://localhost:8080/index.html 然后,就可以在浏览器中看到如下的index.html页面,如图1-1所示:
在控制台中,可以看到类似于如下的HTTP请求信息:
相关资源:《深入剖析Tomcat(中文版 英文版)》.rar