Tomcat(1)一个简单的web server

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/PacosonSWJTU/article/details/51058791

【0】README

0.1)本文部分描述转自“深入剖析tomcat”, 旨在学习  一个简单的web server  的基础知识;

0.2)for complete source code, please visit https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter1

【1】HTTP

【1.1】HTTP请求

1)一个HTTP请求包括以下3部分(parts):(干货——一个HTTP请求包括以下3部分(parts))

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

p2)请求头;

p3)实体;

2)HTTP 请求的示例如下所示:(干货——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
// 这里是空行(CRLF)
lastName=Yun&firstName=Lin

对以上HTTP请求的分析(Analysis):(干货——对HTTP请求的分析)

A1)第一行:请求方法——URI——协议/版本(Post /examples/default.jsp HTTP/1.1)

A2)HTTP1.1 支持的请求方法有:GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE;

A3)URI:指定internet 资源的完整路径;而统一资源定位符(URL) 是 URI 的一种类型;

A4)在请求头和请求实体间有一个空行:该空行只有 CRLF 符;CRLF 告诉HTTP 服务器请求实体正文从哪里开始;正文(lastName=Yun&firstName=Lin)

【1.2】HTTP响应

1)HTTP响应也包括3部分(parts):

part1)协议——状态码——描述;

part2)响应头;

part3)响应实体段;

2)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
    //空行(CRLF)
    <html>
    <head>
    <title>hello, world</title>
    </head>
    <body>
    hello, world
    </body>
    </html>

对上述HTTP响应代码的分析(Analysis):

A1)第一行的200,表示状态码(请求发送成功);

A2)响应头和响应实体正文间由只包含 CRLF 的一个空行分隔;

【1.3】Socket类

1)看个荔枝:(创建一个套接字,用于与本地HTTP Server 进行通信(127.0.0.1 表示一个本地主机),发送HTTP请求接收server 的响应信息);

// 创建一个套接字,用于与本地HTTP Server 进行通信(127.0.0.1 表示一个本地主机),发送HTTP请求接收server 的响应信息
// 采用tomcat 开启8080 端口
public class SocketTest {
	public static void main(String[] args) throws Exception {
		try(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();
		}
	}
}

【3】应用程序

0)intro:web服务器应用程序包括3个类: HttpServer, Request, Response;

1)我们先看运行结果(显然发出了两个HTTP 请求,浏览器发出一个获取静态资源的 HTTP请求, 而该静态资源又 发出获取图像资源的 HTTP 请求)(干货——显然发出了两个HTTP 请求):

E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java com.tomcat.chapter1.HttpServer
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src
GET /index.html HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
GET /images/psu.jpg HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Accept: image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Referer: http://localhost:8080/index.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
 
GET /SHUTDOWN HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

2)HttpServer, Request, Response 的源码如下所示:

public class HttpServer { // 接受HTTP 请求的web server
 
  /** 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) {
	System.out.println(System.getProperty("user.dir"));
    HttpServer server = new HttpServer();
    server.await();// 会在指定端口上等待HTTP请求。
  }
 
  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,创建 HTTP请求对象
        Request request = new Request(input);
        request.parse(); // 解析HTTP请求字符串
 
        // create Response object,创建HTTP 响应对象
        Response response = new Response(output);
        response.setRequest(request);
        response.sendStaticResource(); // 发送静态资源到client
 
        // 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;
      }
    }
  }
}
public class Request { // 封装 HTTP 请求字符串的类
 
  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 {  <span style="font-family: 宋体;">// 封装 HTTP 响应字符串的类</span>
 
  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();
    }
  }
}

 

 

补充)本文总结了一张上述应用程序的调用流程图

对上图的分析(Analysis):

A1)HttpServer:

step1)创建服务器套接字,等待接收 client 发出HTTP 连接请求;

step2)连接成功后,利用套接字创建输入输出流;

step3)创建Request对象, 向Request构造函数传入输入流,并利用request实例对象解析  HTTP  请求;

step4)创建Response对象,向Response构造函数传入输出流,且设置 Response中的request变量引用,调用response对象的sendStaticResource方法发送静态资源到 client;

A2)Request:HTTP请求对象,其parse方法用于读取HTTP请求头;其parseUri方法用于解析client 请求的 uri;

A3)Response:HTTP响应对象,其sendStaticResource方法 读取request解析出的uri对应的资源文件,并发送该文件数据到client端;

3)静态资源html

<html>
<head>
<title>How Tomcat Works</title>
</head>
<body>
<img src="./images/psu.jpg">
<br>
hello, my name is xiao tangtang.
</body>
</html>

4)最后po 出 整体的文件目录架构

猜你喜欢

转载自blog.csdn.net/hchhan/article/details/82355617