源码学习-Tomcat-01-一个简单的Web服务器

本文会介绍如何实现一个简单处理HTTP请求的Web服务器。

预备知识:

1. 简单了解HTTP协议的基本内容,及简单的请求、处理流程;

2. 会使用到Socket类、ServerSocket类;

3. HTTP, TCP, Socket 之间的区别与联系

1.

HTTP  的请求格式:

方法—统一资源标识符(URI)—协议/版本

请求的头部

主体内容

比如:

GET /index.html HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0) QQBrowser/8.2.4258.400
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive

name=shouhui

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 !
</body>
</html >

2.

使用的Socket类方法:

Open Declaration InputStream java.net.Socket.getInputStream() throws IOException

得到servlet调用对象的输入字节流,用于从中取从socket连接另一端传递过来的字节流数据。

Open Declaration OutputStream java.net.Socket.getOutputStream() throws IOException

得到servlet调用对象的输出字节流,用于向socket连接的另一端传递过去字节流数据。

Open Declaration void java.net.Socket.close() throws IOException

关闭servlet调用对象,同时也会关闭该socket对应的InputStream, OutputStream对象(该方法被synchronized 修饰)

使用的ServerSocket类方法:

Open Declaration java.net.ServerSocket.ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException

ServerSocket类的一个构造方法:新建的ServerSocket对象监听bindAddr对应的IP地址,port对应的端口号,且请求的socket连接数最多为backlog个。

Open Declaration Socket java.net.ServerSocket.accept() throws IOException

一直处于阻塞状态监听socket连接请求,直到有建立连接请求到达,成功建立连接则返回对应的socket对象。

代码如下:

 1 public class HttpServer {
 2 
 3   /** WEB_ROOT is the directory where our HTML and other files reside.
 4    *  For this package, WEB_ROOT is the "webroot" directory under the working
 5    *  directory.
 6    *  The working directory is the location in the file system
 7    *  from where the java command was invoked.
 8    */
 9   public static final String WEB_ROOT =
10     System.getProperty("user.dir") + File.separator  + "webroot";
11 
12   // shutdown command
13   private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
14 
15   // the shutdown command received
16   private boolean shutdown = false;
17 
18   public static void main(String[] args) {
19     HttpServer server = new HttpServer();
20     server.await();
21   }
22 
23   public void await() {
24     ServerSocket serverSocket = null;
25     int port = 8080;
26     try {
27       serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
28     }
29     catch (IOException e) {
30       e.printStackTrace();
31       System.exit(1);
32     }
33 
34     // Loop waiting for a request
35     while (!shutdown) {
36       Socket socket = null;
37       InputStream input = null;
38       OutputStream output = null;
39       try {
40         socket = serverSocket.accept();
41         input = socket.getInputStream();
42         output = socket.getOutputStream();
43 
44         // create Request object and parse
45         Request request = new Request(input);
46         request.parse();
47 
48         // create Response object
49         Response response = new Response(output);
50         response.setRequest(request);
51         response.sendStaticResource();
52 
53         // Close the socket
54         socket.close();
55 
56         //check if the previous URI is a shutdown command
57         shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
58       }
59       catch (Exception e) {
60         e.printStackTrace();
61         continue;
62       }
63     }
64   }
65 }
public class Request {

  private InputStream input;
  private String uri;

  public Request(InputStream input) {
    this.input = input;
  }

  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());
  }

  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;
  }

  public String getUri() {
    return uri;
  }

}
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();
    }
  }
}

 

参考:

Budi Kurniawan, Paul Deck. 深入剖析Tomcat (M). 北京: 机械工业出版社华章公司, 2011.

posted @ 2015-08-25 19:22  huangshouhui  阅读(222)  评论(0编辑  收藏  举报