《java实现web服务器.doc》由会员分享,可在线阅读,更多相关《java实现web服务器.doc(30页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、java实现web服务器.txt明骚易躲,暗贱难防。佛祖曰:你俩就是大傻B!当白天又一次把黑夜按翻在床上的时候,太阳就出生了一个 Web 服务器也被称为 HTTP 服务器,它通过 HTTP 协议与客户端通信。这个客户端通常指的是 Web 浏览器。一个基于 Java 的 Web 服务器用到二个重要的类,.Socket 与 .ServerSocket ,并通过 HTTP 消息通信。因此,本文从讨论 HTTP 与这二个类开始,然后我将解释一个与本文相关的简单的 Web 应用。 The Hypertext Transfer Protocol(HTTP) HTTP 是一种让 Web 服务器与浏览器(客户
2、端)通过 Internet 发送与接收数据的协议。它是一个请求、响应协议-客户端发出一个请求,服务器响应这个请求。HTTP 运用可靠的 TCP 连接,通常用的 TCP 80 端口。它的第一个版本是 HTTP/0.9 ,然后被 HTTP/1.0 取代。当前的版本是 HTTP/1.1 ,由 RFC2616(.pdf) 定义。 本节主要对应 HTTP 1.1 ,足够使你充分理解由 Web 服务器程序发出的消息。如果你对更加详细的知识有兴趣,可以参考 RFC2616 。 在 HTTP 中,客户端总是通过建立一个连接与发送一个 HTTP 请求来发起一个事务。服务器不能主动去与客户端联系,也不能给客户端发
3、出一个回叫连接。客户端与服务器端都可以提前中断一个连接。例如,当用一个浏览器下载一个文件时,你可以通过点击“停止”键来中断文件的下载,关闭与服务器的 HTTP 连接。 HTTP 请求 一个 HTTP 请求包含三个部分: Method-URI-Protocol/Version 方法-地址-版本 Request header 请求头 Entity body 请求实体 下面是一个 HTTP 请求实例: POST /servlet/default.jsp HTTP/1.1 Accept: text/plain; text/html Accept-Language: en-gb Connection:
4、Keep-Alive Host: localhost Referer: http:/localhost/ch8/SendDetails.htm User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98) Content-Length: 33 Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate LastName=Franks&FirstName=Michael The Method-URI-Protocol/Version 在这个请
5、求的第一行: POST /servlet/default.jsp HTTP/1.1 其中 POST 是请求的类型。每个客户端 HTTP 请求可以是 HTTP 规范中指定的许多请求类型中的一种。HTTP 1.1 支持七种类型的请求,它们是 GET,POST,HEAD,OPTIONS,PUT,DELETE,TRACE。其中 GET 与 POST 是 Internet 应用中经常用到的二种请求类型。 URI 完整地指定了 Internet 资源。一个 URI 通常被解析为相对服务器的根目录。这样,它应该总是以一个 / 前缀开始。一个 URL 实际上是 URI 的一种类型。 Version 指的是该
6、HTTP 请求所用到的 HTTP 协议版本。 请求头包含了客户端环境与请求实体的一些有用的信息。例如它包含浏览器设定的语言、实体的长度等等。每条请求头用回车换行符(CRLF)分开。 一个非常重要的空行分开了请求头与实体,它标志着实体内容的开始。一些 Internet 开发书籍认为这个 CRLF 空行是 HTTP 请求的第四个部分。 在上面的 HTTP 请求中,实体只是简单以下的一行: LastName=Franks&FirstName=Michael 在一个典型的 HTTP 请求中,请求实体内容会长得多。 HTTP 响应 与请求相似,HTTP 响应也由三部分组成: Protocol-Statu
7、s code-Description 协议状态 描述代码 Response headers 响应头 Entity body 响应实体 以下是一个 HTTP 响应的实例: HTTP/1.1 200 OK Server: Microsoft-IIS/4.0 Date: Mon, 3 Jan 1998 13:13:33 GMT Content-Type: text/html Last-Modified: Mon, 11 Jan 1998 13:23:42 GMT Content-Length: 112 Welcome to Brainy Software 响应头的第一行类似请求头的第一行,告诉你所用
8、的协议是 HTTP 1.1 ,请求成功(200=success),以及没有任何问题。 响应头类似请求头也包含了一些有用的信息。响应的实体响应本身的 HTML 内容。头与实体之间由回车换行的空行(CRLF)分开。 Socket 类 一个 socket 是一个网络连接的端点,它使得一个应用可以从网络读与写。在不同电脑上的二个应用软件能够通过收发字节流而彼此通信。要发一个信息到另一个应用程序,你需要知道它的IP地址,以及它的 socket 端口号。在 Java 中,一个 socket 用 .Socket 来实现。 要创建一个 socket ,你可以用 Socket 类中几个构建方法中的一个。其中一个
9、接受主机名与端口号作为参数: new Socket(, 80); 一旦你成功地创建了一个 Socket 类的实例,你就可以用它去发送与接收字节流了。要发送字节流,你需要呼叫 Socket 类的 getOutputStream 方法来得到一个 java.io.OutputSteam 对象。要发送文本到远程的程序,你通常需要从返回的 OutputStream 创建一个 java.io.PrintWriter 对象。要从连接的另一端接收字节流,你需要呼叫 Socket 类的 getInputStream 方法,它返回一个 java.io.InputStream 对象。 以下代码创建一个可以与本地 H
10、TTP 服务器通信的 socket (127.0.0.1 表示一个本地的主机),发送一个 HTTP 请求,并接收从服务器的响应。它还创建一个 StringBuffer 对象来接受响应,并打印到控制台。 Socket socket = new Socket(127.0.0.1, 8080); OutputStream os = socket.getOutputStream(); boolean autoflush = true; PrintWriter out = new PrintWriter( socket.getOutputStream(), autoflush ); BufferedRe
11、ader in = new BufferedReader( new InputStreamReader( socket.getInputStream() ); / send an HTTP request to the web server out.println(GET /index.jsp HTTP/1.1); out.println(Host: localhost:8080); out.println(Connection: Close); out.println(); / read the response boolean loop = true; StringBuffer sb =
12、new StringBuffer(8096); while (loop) if ( in.ready() ) int i=0; while (i!=-1) i = in.read(); sb.append(char) i); loop = false; Thread.currentThread().sleep(50); / display the response to the out console System.out.println(sb.toString(); socket.close(); 注意要从 web 服务器得到正确的响应,你必须要发送用 HTTP 协议编译了的 HTTP 请求
13、。如果你看了上面的 HTTP 部分,你应该能够理解上面代码中的 HTTP 请求。 编者注:这篇文章节选自 budi 自己出版的书Tomcat 内幕。你可以在他的网站得到更多的相关资料。 基于Java的Web服务器工作原理2 作者:fajaven译 发文时间:2003.09.12 17:00:38 ServerSocket 类 Socket 类描述的是“客户端” socket,当你需要创建与远程服务程序连接时需要用到它。如果你想实现一个服务程序,如 HTTP 服务器或者 FTP 服务器,则需要另外不同的方法。这是因为你的服务器必须随时服务,它不知道什么时候会有一个客户端程序需要连接它。 因为这个
14、目的,你需要用到 .ServerSocket 这个类,它是服务器端 socket 的一个实现。服务器端 socket 等待来自客户端的连接请求。一旦它收到一个连接请求,它创建一个 socket 实例来与客户端进行通信。 要创建服务器端 socket ,需要用到 ServerSocket 类提供的四个构建方法中的一个。你需要指定服务器端 socket 侦听的 IP 地址与端口号。比较典型地,这个 IP 地址可以是 127.0.0.1 ,意思是该服务器端 socket 侦听的是本地机器。服务器端 socket 侦听的 IP 地址指的是绑定地址。服务器端 socket 另一个重要的属性是队列长度,即
15、它拒绝请求前所接受的最大请求排队长度。 ServerSocket 类的构建方法之一如下: public ServerSocket(int port,int backLog,InetAddress bindingAddress); 对于这个构建方法,绑定地址必须是 .InetAddress 类的实例。创建一个 InetAddress 类的对象的简单方法是呼叫其静态方法 getByName ,传递一个包含主机名的字符串。 InetAddress.getByName(127.0.0.1); 以下行的代码创建了一个服务器端 socket ,它侦听本地机器的 8080 端口,限制队列长度为 1 。 ne
16、w ServerSocket(8080,1,InetAddress.getByName(127.0.0.1); 一旦有了一个 ServerSocket 实例,就可以通过呼叫其 accept 方法来让它等待进来的链接请求。这个方法只有当接收到请求时才返回,它返回的是 Socket 类的实例。这个 Socket 对象就可以用来从客户端应用程序发送与接收字节流,正如上节据说的那样。实际上,accept 方法是本文例子中用到的唯一方法。 应用实例 我们的 web 服务器程序是 ex01.pyrmont 包的一部分,它包含三个类:HttpServer;Request;Response。 整个程序的入口(
17、静态 main 方法)是 HttpServer 类。它创建一个 HttpServer 的实例,并呼叫其 await 方法。正如名字表达的,await 在一个特定的端口等待 HTTP 请求,处理它们,并返回响应给客户端。它保持等待状态,直到收到停止命令。(用方法名 await 代替 wait ,是因为 System 中有一个重要的与线程相关的方法) 这个程序只从一个特定的目录发送静态资源,如 HTML 与图像文件。它只支持没有文件头(如日期与 cookie)的情况。现在我们将在如下的几节中看一下这三个类。 HttpServer 类 HttpServer 实现了一个 web 服务器,它可以提供(s
18、erve)特定目录及其子目录下的静态资源。这个特定的目录由 public static final WEB_ROOT 指定。 WEB_ROOT 初始化如下: public static final String WEB_ROOT = System.getProperty(user.dir) + File.separator + webroot; 代码列表中包含了一具叫做 webroot 的目录,里面有一些静态的资源,你可以用来测试本应用。你也可以看到一个 servlet ,在我的下一篇文章将会被用到:“Servlets 容器是怎样工作的”。 为了请求一个静态的资源,在浏览器的地址栏输入如是地址
19、:http:/machinename:port/staticResources 如果你从不同的机器上发送请求到运行本应用的机器,则 machinename 是运行应用机器的机器名或 IP 地址,port 是 8080 ,staticResources 是被请求的文件名称,它必须包含在 WEB_ROOT 目录内。 例如,如果你用同一台电脑来测试这个应用,你想要 HttpServer 发送 index.html 这个文件,用以下的地址:http:/localhost:8080/index.html 要停止服务,只需要从浏览器发送一个停止(shutdown)命令,即在浏览器的地址栏输入 host:p
20、ort 字段后,加上预先定义好的字符串。在我们的 HttpServer 类中,停止命令被定义为 SHUTDOWN ,一个 static final 变量。 private static final String SHUTDOWN_COMMAND = /SHUTDOWN; 因此,要停止服务,你可以这样:http:/localhost:8080/SHUTDOWN 现在,让我们看一下列表 1.1 中给出的 await 方法。代码列表后面将对这段代码做一些解释。 Listing 1.1. The HttpServer class await method public void await() Ser
21、verSocket serverSocket = null; int port = 8080; try serverSocket = new ServerSocket(port, 1, InetAddress.getByName(127.0.0.1); catch (IOException e) e.printStackTrace(); System.exit(1); / Loop waiting for a request while (!shutdown) Socket socket = null; InputStream input = null; OutputStream output
22、 = null; try socket = serverSocket.accept(); input = socket.getInputStream(); output = socket.getOutputStream(); / create Request object and parse Request request = new Request(input); request.parse(); / create Response object Response response = new Response(output); response.setRequest(request); r
23、esponse.sendStaticResource(); / Close the socket socket.close(); /check if the previous URI is a shutdown command shutdown = request.getUri().equals(SHUTDOWN_COMMAND); catch (Exception e) e.printStackTrace(); continue; await 方法以创建一个 ServerSocket 实例开始,然后进入一个 while 的循环。 serverSocket = new ServerSocket
24、( port, 1, InetAddress.getByName(127.0.0.1); . / Loop waiting for a request while (!shutdown) . 在 while 循环中的代码,运行到 ServerSocket 的 accept 方法即停止。这个方法只有在 8080 端口接收到 HTTP 请求才返回: socket = serverSocket.accept(); 收到请求后,await 方法从 accept 方法返回的 Socket 实例中等到 java.io.InputStream 与 java.io.OutputStream: input =
25、socket.getInputStream(); output = socket.getOutputStream(); 然后 await 方法创建一个 Request 对象,呼叫它的 parse 方法来解析这个原始的 HTTP 请求: / create Request object and parse Request request = new Request(input); request.parse(); 下一步,await 方法创建一个 Response 对象并把 Request 对象设置给它,呼叫它的 sendStaticResource 方法: / create Response o
26、bject Response response = new Response(output); response.setRequest(request); response.sendStaticResource(); 最后,await 方法关闭 Socket ,呼叫 Request 的 getUri 方法来检查 HTTP 请求的地址是否是一个停止命令。如果是,则 shutdown 变量被设置为 true ,程序退出 while 循环: / Close the socket socket.close(); /check if the previous URI is a shutdown comm
27、and shutdown = request.getUri().equals(SHUTDOWN_COMMAND); 基于Java的Web服务器工作原理3 作者:fajaven 发文时间:2003.09.12 17:11:54 Request 类 Request 类对应 HTTP 请求。创建这个类的实例,并传给它从 Socket 获得的 InputStream 对象,从而捕获与客户端的通信。呼叫 InputStream 对象的 read 方法中的一个就可以得到 HTTP 请求的原始数据。 Request 类有二个 public 方法 parse 与 getUri。parse 方法解析 HTTP
28、请求的原始数据。它做的事情不多唯一它使之有效的信息是 HTTP 请求的 URI ,这个通过呼叫私有方法 parseUri 来获得。parseUri 方法把 URI 作为一个变量。调用 getUri 方法可以得到 HTTP 请求的 URI 。 要明白 parse 与 parseUri 的工作原理,你需要知道 HTTP 请求的结构,由 RFC2616 定义。 一个 HTTP 请求包括三个部分:Request line;Headers;Message body 。 现在,我们只需要关注 HTTP 请求的第一部分请求行。请求行以方法记号开始,接着是请求的 URI 与协议版本,以回车换行符结束。请求行的
29、元素之间以空格分开。例如,一个用 GET 方法的 index.html 文件的请求行如下: GET /index.html HTTP/1.1 parse 方法从 socket 的 InputStream 传递给 Request 对象中读取字节流,把这个字节数组存在缓冲里。然后,它把 buffer 字节数组里的字节放入叫做 request 的 StringBuffer 对象中,再把 StringBuffer 替换成 String 传递给 parseUri 方法。 parse 方法的代码如列表 1.2 Listing 1.2. The Request class parse method publ
30、ic void parse() / Read a set of characters from the socket StringBuffer request = new StringBuffer(2048); int i; byte buffer = new byte2048; try i = input.read(buffer); catch (IOException e) e.printStackTrace(); i = -1; for (int j=0; jrequest.append(char) bufferj); System.out.print(request.toString(
31、); uri = parseUri(request.toString(); parseUri 方法查找请求行的第一个与第二个空格,从而从请求行获得了 URI 。列表 1.3 展示了 parseUri 方法的代码。 Listing 1.3. The Request class parseUri method private String parseUri(String requestString) int index1, index2; index1 = requestString.indexOf( ); if (index1 != -1) index2 = requestString.inde
32、xOf( , index1 + 1); if (index2 index1) return requestString.substring(index1 + 1, index2); return null; Response 类 Response 类描述 HTTP 响应。它的构建方法接受 OutputStream 对象,如下: public Response(OutputStream output) this.output = output; Response 对象通过传递从 socket 获得的 OutputStream 对象到 HttpServer 类的 await 方法而创建。 Resp
33、onse 类有二个公共方法 setRequest 与 setStaticResource 。setRequest 用来传递 Request 对象到 Response 对象。它比较简单,代码如列表 1.4 所示: Listing 1.4. The Response class setRequest method public void setRequest(Request request) this.request = request; sendStaticResource 方法用来发送静态的资源,例如 HTML 文件。它的实现如列表 1.5 所示: Listing 1.5. The Respo
34、nse class sendStaticResource method public void sendStaticResource() throws IOException byte bytes = new byteBUFFER_SIZE; FileInputStream fis = null; try File file=new File(HttpServer.WEB_ROOT, request.getUri(); if (file.exists() fis = new FileInputStream(file); int ch = fis.read(bytes, 0, BUFFER_SI
35、ZE); while (ch != -1) output.write(bytes, 0, ch); ch = fis.read(bytes, 0, BUFFER_SIZE); else / file not found String errorMessage=HTTP/1.1 404 File Not Foundrn+ Content-Type: text/htmlrn + Content-Length: 23rn + rn + File Not Found; output.write(errorMessage.getBytes(); catch (Exception e) / thrown
36、if cannot instantiate a File object System.out.println(e.toString() ); finally if (fis != null) fis.close(); SendStaticResource 方法非常简单。它首先通过传递父与子目录到 File 类的构建方法从而实例化 java.io.File 类。 File file new File(HttpServer.WEB_ROOT, request.getUri(); 然后检查这个文件是否存在。如果存在,则 sendStaticResource 方法传递 File 对象创建 java.i
37、o.FileInputStream 对象。然后调用 FileInputStream 的 read 方法,并把字节数组写到 OutputStream 对象 output 。就这样,静态资源的内容作为原始数据被发送到浏览器。 if (file.exists() fis = new FileInputStream(file); int ch = fis.read(bytes, 0, BUFFER_SIZE); while (ch != -1) output.write(bytes, 0, ch); ch = fis.read(bytes, 0, BUFFER_SIZE); 如果文件不存在,sendS
38、taticResource 发送一个错误信息到浏览器。 String errorMessage = HTTP/1.1 404 File Not Foundrn + Content-Type: text/htmlrn + Content-Length: 23rn + rn + File Not Found; output.write(errorMessage.getBytes(); 编译与运行应用程序 要编辑与运行本文的应用,首先你需要解压源码 zip 文件。直接解压出来的目录被称为工作目录,它有三个子目录:src/,classes/,lib/。要编译应用,从工作目录输入如下命令: javac
39、-d . src/ex01/pyrmont/*.java -d 选项把结果写到当前目录,而不是 src/ 目录。 要运行应用,在当前工作目录输入如下命令: java ex01.pyrmont.HttpServer 测试这个应用,打开你的浏览器,在地址栏输入如下地址:http:/localhost:8080/index.html 你将在你的浏览器看到 index.html 显示出来,如图1所示。 图1:web 服务器的输出显示 在控制台,你看到如下的内容: GET /index.html HTTP/1.1 Accept: */* Accept-Language: en-us Accept-Enc
40、oding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98) Host: localhost:8080 Connection: Keep-Alive GET /images/logo.gif HTTP/1.1 Accept: */* Referer: http:/localhost:8080/index.html Accept-Language: en-us Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; M
41、SIE 4.01; Windows 98) Host: localhost:8080 Connection: Keep-Alive 总结 在这篇文章中(分为三个部分),你看到了一个简单的 web 服务器的工作原理。本文相关的应用只包括了三个类,功能是不全面的。然而,它仍不失为一个好的学习工具。 (责任编辑:张明燕) Java的网络编程:用Java实现Web服务器 作者:谷和启 发文时间:2002.12.26 15:35:57 HTTP协议 超文本传输协议(HTTP)是位于TCP/IP 协议的应用层,是最广为人知的协议,也是互连网中最核心的协议之一,同样,HTTP 也是基于 C/S 或 B/S
42、模型实现的。事实上,我们使用的浏览器如Netscape 或IE 是实现HTTP 协议中的客户端,而一些常用的Web 服务器软件如Apache、IIS 和iPlanet Web Server 等是实现HTTP 协议中的服务器端。Web 页由服务端资源定位,传输到浏览器,经过浏览器的解释后,被客户所看到。 Web 的工作基于客户机/服务器计算模型,由Web 浏览器(客户机)和Web服务器(服务器)构成,两者之间采用超文本传送协议(HTTP)进行通信。HTTP协议是Web浏览器和Web服务器之间的应用层协议,是通用的、无状态的、面向对象的协议。 一个完整的HTTP协议会话过程包括四个步骤: 连接,Web浏览器与Web服务器建立连接,打开一个称为Socket(套接字)的虚拟文件,此文件的建立标志着连接建立成功; 请求,Web浏览器通过Socket向Web服务器提交请求。HTTP的请求一般是GET或POST命令(POST用于FORM参数的传递); 应答,Web浏览器提交请求后,通过HTTP协议传送给Web服务器。Web服务器接到后,进行事务处理,处理结果又通过HTTP传回给Web浏览器,从而在Web浏览器上显示出所请求的页面; 关闭连接,应答结束后Web浏览器与Web服务器