zoukankan      html  css  js  c++  java
  • How Tomcat Works A Simple Web Server

    1. Http协议

    1.1 Http 请求

    一个http请求包含了三个部分:

    1)方法 - 统一资源定位符(uri) - 协议

    2)请求头

    3)请求实体

    一个例子:

    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
    

    这里的方法是POST,其他支持的方法还有GET,OPTIONS,HEAD,PUT,DELETE。URI是 /examples/default.jsp表示请求资源的路径。协议是HTTP/1.1

    第二部分就是请求头,各项之间用回车/换行(CRLF)分割。

    最后一部分是请求实体,这里就是

    lastName=Franks&firstName=Michael
    

    请求实体和请求头之间是用一个只包含CRLF的空行分割。

    1.2 Http响应

    和http请求类似,http响应也包含了三部分:

    1)协议-状态码-描述

    2)响应头

    3)响应实体

    一个例子:

    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/1.1,状态码200,描述是OK。响应头各项之间也是用CRLF分割。响应头和响应实体之间也是一个包含CRLF的空行分割。

    2. 一个简单的web服务器

    这个例子实现了一个简单的web服务器,包含HttpServer,Response和Request三个java类和一些静态的资源文件。

    HttpServer.java负责监听请求:

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

    Request.java:

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

    Response.java:

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

    HttpServer 接受web请求然后将封装成一个request对象,request对请求的uri进行解析,在webroot里面找到对应的静态文件,然后写到socket的outputstream。如果文件不存在,则写回一个404错误信息。

    测试会发现在各个浏览器上测试结果不一样。

    IE上如果访问的页面没有找到,会直接显示404,不会显示我们响应的错误信息。如果是访问localhost:8080/index.html 可以正常显示。

    Firefox如果访问的页面没有找到,会显示我们响应的404错误信息,如果访问localhost:8080/index.html会显示HTML文件的源码。

    注意Response sendStaticResource 这个方法是有问题的,因为根据http协议,响应体之前还有方法,状态码,响应头等信息。但是这里是直接把文件内容给写回去了。为了能把这部分内容加上去,对HttpServer做了一些修改;

    import java.io.File;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.nio.file.Files;
    
    /*
      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
    */
    
    class Response {
        Request request;
        OutputStream output;
    
        public Response(OutputStream output) {
            this.output = output;
        }
    
        public void setRequest(Request request) {
            this.request = request;
        }
    
        public void sendStaticResource() throws IOException {
            try {
                File file = new File(HttpServer.WEB_ROOT, request.getUri());
                if (file.exists()) {
                    StringBuilder response = new StringBuilder();
                    response.append("HTTP/1.1 200 OK\r\n");
    
                    // Hack it
                    if (request.getUri().startsWith("/images")) {
                        response.append("Content-Type: image/png\r\n");
                    } else {
                        response.append("Content-Type: text/html\r\n");
                    }
                    response.append("Content-Length: ").append(file.length()).append("\r\n");
                    response.append("\r\n");
    
                    output.write(response.toString().getBytes());
                    // Write file
                    output.write(Files.readAllBytes(file.toPath()));
                } 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());
            }
        }
    }
    

     现在在firfox和chrome上都可以正常工作。

  • 相关阅读:
    linux使用secureCRT连接(没有rsa的时候)
    linux删除某个php程序进程的组合命令
    Redis介绍以及安装(Linux)
    PHP Warning: date() [function.date]: It is not safe to rely on the system's timezone
    linux下生成rsa密钥的方法
    mysql 的 infobright 数据库的 mediumblob 显示不了数据
    PHP中的Libevent学习
    PHP函数getopt详解
    php_sapi_name详解
    PHP使用mysqldump备份数据库(以及还原)
  • 原文地址:https://www.cnblogs.com/likely/p/7283754.html
Copyright © 2011-2022 走看看