本章的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:
若从另一台机器(不是运行应用程序的那台机器)上向该应用程序发出请求,则machinename是应用程序所在计算机的名称或ip地址;若在同一台机器上发出的请求,则可以将machinename替换为localhost,此外,连接请求使用的端口为8080。staticresource是请求的文件的名字,该文件必须位于web_root指向的目录下。
例如,如果你正使用同一台机器来测试该应用程序,你想让httpserver对象发送index.html文件,就可以使用如下的url:
若要关闭服务器,可以通过web浏览器的地址栏或url框,在url的host:port部分后面输入预先定义好的字符串,从web浏览器发送一条关闭命令,这样服务器就会收到关闭命令了。关闭命令定义在httpserver类的shutdown静态final变量中:
因此,若要关闭服务器,需要使用如下的url:
接下来,看一下代码清单1-2中的await()方法。
该方法名之所以称为await(),而不是wait(),是因为wait()方法是java.lang.object类中与使用线程相关的重要方法。
await()方法会先创建一个serversocket实例,然后进入一个while循环:
当从8080端口接收到http请求后,serversocket类的accept()方法返回,等待结束:
接收到请求后,await()方法会从accept()方法返回的socket实例中获取java.io.inputstream对象和java.io.outputstream对象:
然后,await()方法会创建一个ex01.pyrmont.request对象,并调用其parse()方法来解析http请求的原始数据:
然后,await()方法会创建一个response对象,并分别调用其setrequest()方法和sendstaticresource()方法:
最后,await()方法关闭套接字,调用request类的geturi()方法来测试http请求的uri是否是关闭命令。若是,则将变量shutdown设置为true,程序退出while循环。
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文件的请求行如下所示:
parse()方法从传入到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()会发送错误消息到浏览器:
若要运行应用程序,需要在工作目录中执行下面的命令:
若要测试应用程序,可以打开浏览器,在地址栏或url框中输入如下url:
<a href="http://localhost:8080/index.html">http://localhost:8080/index.html</a>
然后,就可以在浏览器中看到如下的index.html页面,如图1-1所示:
在控制台中,可以看到类似于如下的http请求信息: