`
jarfield
  • 浏览: 200515 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

[How Tomcat Works]第1章 一个简单的Web服务器

阅读更多

译者 jarfield

博客 http://jarfield.javaeye.com      

    本章解释了Java Web 服务器是如何工作的。Web 服务器又被称为超文本传输 协议(Hypertext Transport  Protocol , HTTP )服务器,因为它和客户端(通常是浏览器)使用HTTP 协议进行通信。基于Java 开 发的Web 服务器都使用到两个重要的类java.net.Socket java.net.ServerSocket , 并通过HTTP 消息完成通信。因此,本章的开头就开始讨论HTTP 和这两个类。然后,继续介绍 本章附带的应用程序。

超文本传输协议 (HTTP

    HTTP 协议,允许Web 服务器和浏览器在Internet 上发送和接受数据。HTTP 是一种基于“请求-响应”模式的协议。客户端请求一 个文件(file ),服务器针对该请求给出响应。HTTP 使用可靠的TCP 连 接——默认端口是80 。HTTP的最初版本是HTTP/0.9 ,后来被HTTP/1.0 重 写。HTTP/1.0 的替代者是当前的HTTP/1.1HTTP/1.1 定 义在RFC 2612 中,可以从 http://www.w3.org/Protocols/HTTP/1.1/rfc2616.pdf 下载。


    提示:本节只是简短地介绍HTTP ,目的是帮助你理解Web 服 务器发送的HTTP 消息。如果你想更深入得了解HTTP ,可以读读RFC 2616

    HTTP 的 通信总是由客户端主动初始化:建立连接并发送HTTP 请求。Web 服务器从来不主动联系(contact ) 客户端,或者建立到客户端的回调(callback )连接。无论客户端还是服务器,都可以随时(prematurely )中断连接。例如,当你在下载文件时,点击浏览器的“停止”按钮,就关闭了浏览器和服务器之间的HTTP 连 接。

HTTP 请求

    HTTP 请求包含3 个组成部分:

  • Method-Uniform Resource Identifier (URI)-Protocol/Version
  • Request headers (请求头部)
  • Entity body (实体主体)

    下面是HTTP 请求的一个例子:

    POST /examples/default.jsp HTTP/1.1
    Accept: text/plain; text/html
    Accept-Language: en-gb
    Connection: Keep-Alive
    Host: localhost
    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 

 

    Method-URI-Protocol/Version 是请求的第一行

POST /examples/default.jsp HTTP/1.1 

 

    POSTMethod/examples/default.jspURIHTTP/1.1 就是Protocol/Version

    每个HTTP 请求都可以使用HTTP 标 准中众多Method 中的一个。HTTP/1.1 共支持7Method : GET , POST , HEAD , OPTIONS , PUT , DELETETRACEGETPOST 是互联网应用使用最普遍的Method

     URI 标 识了互联网上的资源。URI 的解析通常都是相对与服务器根目录的。因此,URI 总是从正斜线/ 开 始。统一资源定位器(Uniform Resource Locator , URL )实际上是一种URI (参 见 http://www.ietf.org/rfc/rfc2396.txt )。Protocol version 表示使用了哪个版本的HTTP 协议。

    Request header 包含了关于客户端环境和entity body的有用信息 。例如,headers 可能包括浏览器的语言,entity body 的 长度等等。Header 之间通过回车/换行符(CRLF )分隔。

    在headersentity body 之 间,是一个空行(CRLF )。这个CRLF 对于HTTP 请求内容的格式是相当重要的,它告诉HTTP 服务器:entify body 从哪开始。在一些介绍互联网编程的书中,该CRLF 被认为是HTTP 请求的第4 个组成部分。

    在前面的HTTP 请求中,entify body 仅仅只有这一行:

    lastName=Franks&firstName=Michael 


    这里只是一个例子,实际的HTTP 请求中,entity body 当然可以更长一些。

HTTP 响应

    和HTTP请求一样,HTTP 响应也包含3 个组成部分:

  • Protocol—Status code—Description  
  • Response headers (响应头部)
  • Entity body (实 体主体)

    下面是HTTP 响 应的一个例子:

    HTTP/1.1 200 OK
    Server: Microsoft-IIS/4.0
    Date: Mon, 5 Jan 2004 13:13:33 GMT
    Content-Type: text/html
    Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT
    Content-Length: 112
 
    <html>
    <head>
    <title>HTTP Response Example</title>
    </head>
    <body>
    Welcome to Brainy Software
    </body>
    </html> 

 

    HTTP 响应的第一行类似于HTTP 请求的第一行。第一行告诉你:使用的HTTP 版本是HTTP/1.1 ,请求处理成功了(200 = 成功),一切运行正常。

    响应headers 和请求headers 类似,包含了很多有用的信息。响应数据中的entity body 是响应本身的HTML 内容。Headersentity body 之间通过CRLF 分隔。

Socket

    套接字是网络连接的一个端点(endpoint )。 应用程序使用套接字从网络上读取数据、向网络写入数据。两台不同机器上的应用,在同一个连接上以字节流的格式向(从)对方发送(接收)数据。 为了向一个应用发送消息,你需要知道该应用的IP 地址和端口。在Java 中,套接字用 java.net.Socket 类来表示。

    你可以使用 Socket 类众多 构造函数中的一个来创建 套接字 对象。其中一个构造函数接收主机名和端口作为参数:

public Socket (java.lang.String host, int port)


    其中,host 是远程机器的名称或IP 地 址,port 是远程应用的端口号。例如,要连接上 yahoo.com 80 端口,你可以创建下面的 Socket 对象:

new Socket("yahoo.com", 80); 


    只要你成功创建了 Socket 的一个实例,就可以使用它发送和读取字节流。如果要发送字节流,你可以调用 Socket 类的 getOutputStream 方法获取一个 java.io.OutputStream 对象。如果要发送文本信息,你可以将上述 java.io.OutputStream 对象包装成一个 java.io.PrintWriter 对象。如果要读取字节流,你可以调用Socket 类的 getInputStream 方法获取一个 java.io.InputStream 对象

    下面的代码片段创建了一个能够与本地HTTP 服务器(127.0.0.1 表 示本地主机)通信的 Socket 对象,发送了一个HTTP 请 求,并 从服务器 接收了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);
    BufferedReader 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 = 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 协议。如果你读了 前一节,超文本传输协议(HTTP ),应该就会理解上面代码中的HTTP 请求。

    提示:你可以使用本书源代码中的com.brainysoftware.pyrmont.util.HttpSniffer 类发送HTTP 请求和显示HTTP 响 应。为了使用这个Java 程序,你必须连接到Internet 。不过,提醒一句,如果你在防火墙后面,那么这个类可能不能正常工作。

ServerSocket

    前面介绍的Socket 类,代表的是客户端套接字,即当你为了连接到远程服务程序而创建的 套接字 对象。现在,如果你想要实现一个服务器程序,比如HTTP 服 务器或FTP 服务器,你需要一种不同的做法。因为,服务器程序必须一直驻守, 它不知道客户端何时会连接过来。为了使你的程序能够驻守,你需要使用 java.net.ServerSocket 类。这是服务器端socket 的一个实现类。

    ServerSocket 类和 Socket 类并不相同。服务器套接字的职责是等待来自客户端的连接请求。当服务器套接字收到一个连接请求后,创建一个 Socket 对象来 与客户端通信。 为了创建服务器套接字,你需要使用 ServerSocket 类提供的4 个 构造函数之一。你需要指定服务器套接字将要监听的IP 地址和端口。通常,IP 地址是127.0.0.1 ,表示服务器套接字将监听本地机器。服务器套接字监听的IP 地址被称为绑定地址 (binding address )。服务器套接字的另一个重要属性是backlog ,服务器套接字有一个保存尚未处理的连接请求的队列,backlog 就是该队列的的最大长度。如果达到最大长度,服务器套接字将拒绝新的连接请 求。

    下面是 ServerSocket 类的一个构造函数原型:

public ServerSocket(int port, int backLog, InetAddress bindingAddress); 


    注意这个构造函数,binding address 必须是一个 java.net.InetAddress 对象。创建 InetAddress 对象的一个简单方法就是调用该类的静态方法 getByName , 并把主机名作为 String 对象传给该方法,就像下面这样:

InetAddress.getByName("127.0.0.1"); 


    下面这行代码创建了一个监听本地8080 端 口的、backlog1ServerSocket 对象。

new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));

 

    有了ServerSocket 对象后,你就可以告诉它:在指定的端口上监听绑定地址的连接请求吧。告 诉的办法就是调用 ServerSocket accept 方法。当有一个连接请求到达时,该方法就会返回,返回值是一个 Socket 对 象。这个Socket 对象就像 前一节 Socket 类”,描述的那样,可以用来向(从)客户端发送(读取)数据。实际上,accept 方法也是本章应用程序唯一使用的( ServerSocket 类的)方法。

应用程序

    本章的应用程序是一个Web服务器程序,放在 ex01.pyrmont 包中,由3 个类组成:

  • HttpServer
  • Request
  • Response  

    本章应用程序的入口(静态的 main 方法)在 HttpServer 类中。 main 方法创建了一个 HttpServer 对象,并调用了它的 await 方法。人如其名, await 方法在指定端口上等待HTTP 请 求,然后处理HTTP 请求,最后将HTTP 响应发送回客户端。而且, await 方法保持等待,只有接收到 shutdown 命令,才退出运行。该应用只能发 送静态资源,诸如特性目录下的HTTP 文件和图像文件。同时,还在控制台上显 示HTTP 请求的字节流。然而,该应用不向浏览器发送任何headerdatecookies 等)。

    下面各小节,我们将会看一看这3 个类。

HttpServer

    HttpServer 类 表示了一个Web 服务器,代码在 Listing 1.1 中。需要注意的是,为了节省篇幅,await 方法没被列在 Listing 1.1 中,可以在 Listing 1.2 中找到。

Listing 1.1: The HttpServer class    

package ex01.pyrmont;
 
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.File;
 
public class HttpServer {
 
  /** WEB_ROOT is the directory where our HTML and other files reside.
   *  For this package, WEB_ROOT is the "webroot" directory under the
   *  working directory.
   *  The working directory is the location in the file system
   *  from where the java command was invoked.
   */
  public static final String WEB_ROOT =
    System.getProperty("user.dir") + File.separator  + "webroot";
 
  // shutdown command
  private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
 
  // the shutdown command received
  private boolean shutdown = false;
 
  public static void main(String[] args) {
    HttpServer server = new HttpServer();  
    server.await();
  }
 
  public void await() {
    ...
  }
} 

 
Listing 1.2: The HttpServer class's await method

public void await() {
  ServerSocket 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 = 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);
      response.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;
    }
  }
}

 

    这个Web 服务器可以提供静态资源服务,可访问的资源位于 public static final WEB_ROOT 表示的 目录及子 目录下。WEB_ROOT 是这样初始化的:

public static final String WEB_ROOT =  System.getProperty("user.dir") + File.separator + "webroot";


    这段代码中有一个名为webroot 的目录,该目录 包含了可以用于测试该应用的的静态资源。你还可以从该目录下找到几个用于测试下一章应用的servlet

    如果要请求一个静态资源,你可以在浏览器的地址栏中敲入以下URL

    http://machineName:port/staticResource 


    如果你从另一台机器上发送请求, machineName 应该是该应用所在机器的主机名或IP 地址。如果你的浏览器运行在同一台机器上,可以使用 localhost 作为 machineName 。端口是8080 staticResource 是被请求的文件(静态资源)名,该文件必须位于 WEB_ROOT 下。

    例如,你在同一台机器上测试该应用,想让HttpServer 发送文件index.html ,你可以使用下面的URL  

    如果要停止服务器,你可以通过特定的URL从浏览器发送shutdown命令:host:port的后面加上预先定义的、表示shutdown的字符串即可。 HttpServer 类的静态常量 SHUTDOWN 定义了 shutdown命令:

private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";


    因此,如果要停止服务器,你可以使用下面的URL

    http://localhost:8080/SHUTDOWN 


    现在,我们看一看 Listing 1.2 中的await 方 法。
   
    方法名使用await 而不用wait ,是因为waitjava.lang.Object 类中一个重要的、与多线程紧密相关的方法。

    await 方法首先创建了一个ServerSocket 对 象,然后进入一个 while 循环。

    serverSocket =  new ServerSocket(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 = socket.getInputStream();
output = socket.getOutputStream(); 

 

    await 方法然后创建了一个 ex01.pyrmont.Request 对象,并调用它的 parse 方法来解析HTTP请求的原始数据(raw data )。

    // create Request object and parse
    Request request = new Request(input);
    request.parse (); 


    之后, await 方法创建了一个 Response 对象,将上面的 Request 对象设置成 Response 对象的成员,并调用 Response 对象的 sendStaticResponse 方法。

   // create Response object
    Response response = new Response(output);
    response.setRequest(request);
    response.sendStaticResource();

 

    最后, await 方法关闭 了 Socket 对象,并调用了 Request 对象的 getUri 方法,检查本次HTTP 请 求的URI 似乎否是shutdown 命 令。如果是(shutdown 命令)的话, shutdown 变 量会被设置成 true ,从而程序将退出 while 循环。

    // Close the socket
    socket.close ();
 
    //check if the previous URI is a shutdown command
    shutdown = request.getUri().equals(SHUTDOWN_COMMAND); 

Request

    ex01.pyrmont.Request 类代表了HTTP 请 求。要创建 Reuqest 对象,我们可以先从处理客户端通信的 Socket 对象中 获得的 InputStream 对象,然后将其作为参数调用 Request 类的构造函数。通过调用 InputStream 对象的 read 方法簇之一,就可以获取HTTP 请求的原始数据。

    Listing 1.3 列出了 Request 类的代码。 Request 类有两个public 方法, parse getUri Listing 1.4 Listing 1.5 分别列出了这两个方法的代码。
 
Listing 1.3: The Request class

package ex01.pyrmont;
 
import java.io.InputStream;
import java.io.IOException;
 
public class Request {
   private InputStream input;
   private String uri;
 
   public Request(InputStream input) {
     this.input = input;
   }
 
   public void parse() {
     ...
   }
 
   private String parseUri(String requestString) {     
     ...
   }
 
   public String getUri() {
     return uri;
   }
} 

 

Listing 1.4: The Request class's parse method    

public void parse() {
   // Read a set of characters from the socket
   StringBuffer request = new StringBuffer(2048);
   int i;
   byte[] buffer = new byte[2048];
   try {
     i = input.read(buffer);
   }
   catch (IOException e) {
     e.printStackTrace();
     i = -1;
   }
   for (int j=0; j<i; j++) {
     request.append((char) buffer[j]);
   }
   System.out.print(request.toString());
   uri = parseUri(request.toString());
} 

 

Listing 1.5: the Request class's parseUri method

private String parseUri(String requestString) {
   int index1, index2;
   index1 = requestString.indexOf(' ');
   if (index1 != -1) {
     index2 = requestString.indexOf(' ', index1 + 1);
     if (index2 > index1)
       return requestString.substring(index1 + 1, index2);
   }
   return null;
}

 
    除了解析HTTP 请求的原始数据, parse 方法也 没做更多的事情。该方法从HTTP 请求中获取的唯一信息,就是通过调用private 方法 parseUri 解析出的URI parseUri 方 法将HTTP 请求的URI 存 储在成员变量 uri 中。调用public 方法 getUri 可以返 回HTTP 请求的URI

    提示:第3 章及后续章节将对HTTP 请求原始数据进行更多的处理。

    为了理解 parse parseUri 方法是如何工作的,你需要知道HTTP 请求的协议格式,这在 前面小节,“超文本传输协议(HTTP )”,已经讨论过。本章,我们只对HTTP 请求的第一部分,请求行(request line ),感兴趣。请求行以method 标记开头,后面是请求URI 和协议版本,最后以回车换行符(CRLF )结束。请求行中的元素是以空格分隔的。例如,使用GET 方法请求index.html 的请求行如下所示:

    GET /index.html HTTP/1.1


    parse 方法从传给 Request 对象的 InputStream 对象中读取整个字节流,并保存在字节数组 buffer 中。然后,使用 buffer 中的字节数据创建一个名为 request StringBuffer 对象,并将 StringBuffer 对象的 String 表示(representation ) 传给 parseUri 方法。

    parse 方法的代码列在 Listing 1.4 中。

    parseUri 方法负责从请求行中获取URI Listing 1.5 列出了 parseUri 方法的代码。 parseUri 方法在请求行中搜索第一个和第二个空格,获取(两个空格)之间的URI

Response

    ex01.pyrmont.Response 类代表了HTTP 响应数据,Listing 1.6 列出了其代码。

Listing 1.6: The Response class

package ex01.pyrmont;
 
import java.io.OutputStream; import java.io.IOException;
import java.io.FileInputStream;
import java.io.File;
 
/*
   HTTP Response = Status-Line
     *(( general-header | response-header | entity-header ) CRLF)
     CRLF
     [ message-body ]
     Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
*/
 
public class Response {
 
   private static final int BUFFER_SIZE = 1024;
   Request request;
   OutputStream output;
 
   public Response(OutputStream output) {
     this.output = output;
   }
 
   public void setRequest(Request request) {
     this.request = request;
   }
 
   public void sendStaticResource() throws IOException {
     byte[] bytes = new byte[BUFFER_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_SIZE);
         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 Found\r\n" +
           "Content-Type: text/html\r\n" +
           "Content-Length: 23\r\n" +            "\r\n" +
 
          "<h1>File Not Found</h1>";
        output.write(errorMessage.getBytes());
      }
     }
     catch (Exception e) {
       // thrown if cannot instantiate a File object
       System.out.println(e.toString() );
     }
     finally {
       if (fis!=null)
         fis.close();
     }
   }
}

 

    我们首先注意到,该类的构造函数接收一个 java.io.OutputStream 对象作为参数,如下所示。

    public Response(OutputStream output) {
        this.output = output;
    }


    HttpServer 类的 await 方法从 Socket 对象中获取 OutputStream 对象,将其作为参数构造了一个 Response 对象。 Response 类 有两个 public 方法: setRequest sendStaticResource setRequest 方法用来将 Request 对象设置成 Response 对象的成员变量。

    sendStaticResource 方法用来发送静态资源,比如HTML 文件。该方法首先将父路径和子路径传递给 java.io.File 的构造函数,创建一个File 对 象。

    File file = new File(HttpServer.WEB_ROOT, request.getUri()); 

 

    然后检查该文件是否存在。如果存在,那么 sendStaticResource 方法以File 对 象为参数构造一个 java.io.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);
        }
    } 
 

    如果文件不存在,那么 sendStaticResource 方法就将错误信息发送给浏览器。

    String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
        "Content-Type: text/html\r\n" +
        "Content-Length: 23\r\n" +
        "\r\n" +
        "<h1>File Not Found</h1>";
    output.write(errorMessage.getBytes()); 

运行应用程序

     要从工作目录运行该应用,需要敲入下面的命令:

java ex01.pyrmont.HttpServer 

 

    要测试该应用,可以打开浏览器,在地址栏敲入下面的URL

http://localhost:8080/index.html 

  
    正如Figure 1.1 所示,你会看到index.html 显示在浏览器(原图是IE6 , 这里是译者的IE8 )中。

 

Figure 1.1: The output from the web server

                                                                                          Figure 1.1: The output from the web server

    在控制台上,你可以看到类似于下面的HTTP 请求:

    GET /index.html HTTP/1.1
    Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, application/vnd.ms- powerpoint, application/x-shockwave-flash, application/pdf, */*
    Accept-Language: en-us
    Accept-Encoding: gzip, deflate
    User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)
    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; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)
    Host: localhost:8080
    Connection: Keep-Alive

总结

    从本章你已经看到了简单的Web 服务器是如 何工作的。本章附带的应用只包括3 个类,功能还不完整。无论如何,该应用仍是 一个很好的学习工具。下一章,我们将讨论对动态内容(dynamic content ) 的处理。

分享到:
评论
6 楼 JMS_Exception 2011-07-17  
HTTP空格
JMS_Exception 写道


  在index.html 中写入
引用

    <title>HTTP Response Example</title>

lz   今天看你的实例  。今天发现html  里面有个问题。在titl 中如果写HTTP空格,这样的话body内容是不会显示的。
Response 中读取文件问题?

不是如果不是HTTP开头 加空格就没有问题的。
5 楼 JMS_Exception 2011-07-17  


  在index.html 中写入
引用

    <title>HTTP Response Example</title>

lz   今天看你的实例  。今天发现html  里面有个问题。在titl 中如果写HTTP空格,这样的话body内容是不会显示的。
Response 中读取文件问题?
4 楼 anranran 2010-12-03  
String errorMessage = "HTTP/1.1 200 File Not Found\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 25\r\n" + "\r\n" +
"<h1>File Not Foundsa</h1>";
如果改成:
errorMessage = "it is error!!";

IE会显示 "it is error!!",也就是说没有reponse报文头,浏览器也是可以显示的.

我用httpWatch看到,返回的报文的确没有头部.

可以给个QQ之类的联系方式吗,最近在研究TOMCAT,想多多请教.我们公司就在你们前面,呵呵

3 楼 jarfield 2010-12-01  
anranran 写道
out.println("Connection: Close");
在这里起什么作用,为什么去除之后就收不到结果,服务端如何判断请求头已发送完毕?

Connection: Close 是一个头部,告诉服务端:不要保持连接,处理完这个请求后直接关闭连接即可。
服务端通过最后一个空行来判断请求头发送完毕。
2 楼 anranran 2010-12-01  
out.println("Connection: Close");
在这里起什么作用,为什么去除之后就收不到结果,服务端如何判断请求头已发送完毕?
1 楼 lodachi 2010-06-23  
看了第一章感觉收获颇多,也正适合我的水平,在这里谢谢博主了

相关推荐

Global site tag (gtag.js) - Google Analytics