從零開始寫一個Tomcat(壹)

  Tomcat是一個免費的開放源代碼的Web 應用服務器,屬於輕量級應用服務器,也是一個servlet容器的優秀解決方案,作Java web開發的基本上都使用過,可是tomcat大多時間對於咱們是一個黑盒,出了問題無所適從,配置文件知道怎麼寫,但不知道爲何這麼寫,原理是什麼.
html

      本系列文章可讓你:java

      1.瞭解tomcat內部原理,好比tomcat怎麼接收請求,怎麼處理,怎麼封裝request,怎麼開始,怎麼使用lifeCycle,日誌是怎麼生成的,session的原理,爲何session會過時,什麼是                                engine,host,context,wrapper,tomcat是怎麼加載你寫的servlet的等等等等等等等等git

      2.理解設計模式,tomcat使用了外觀模式,責任鏈模式,線程池等設計github

      3.不編了..本身挖掘吧web

      本着開源的思想,每章的代碼均可以訪問個人gitHub獲取,每章的結尾會放上地址,個人環境爲idea+maven,不過並無多少依賴設計模式

      本文的部分代碼和思想來源於《深刻剖析Tomcat》即《How Tomcat works》和Tomcat4源碼瀏覽器

 

     1.http協議tomcat

       瞭解http協議是必須的,這裏我只簡單介紹一下,更多的東西能夠參考《深刻體驗Java Web開發內幕--核心基礎》或者更專業的資料服務器

        客戶端(瀏覽器,之後統稱客戶端)會向服務器創建鏈接,而後發出請求,請求包括一個請求行,0或若干請求頭、請求實體,多是這樣的session

          

請求行:GET getPrice.jspx HTTP/1.1

請求頭:Accept:*/*

Referer:www.asens.cn

Host:locathost

  

                    

 

        也多是這樣的

                     

POST getPrice.jspx HTTP/1.1

Host:locathost

Connection:Keep-Alive
 

name=user&pass=123

  

        服務器接到這樣的請求後通過一系列的處理,生成響應返回給客戶端,響應多是這樣的

HTTP/1.1 200 OK

Content-length:3242

Content-Type:text.html

我是實體內容

  客戶端接收到響應後,瀏覽器就會出現這幾個字:我是實體內容,一般狀況下響應會返回一個html頁面供人瀏覽

 

   2.最簡單的靜態server

        這個服務器只能訪問靜態文件,很是簡單,只須要3個類

       HttpServer,Request,Response,而後在項目根目錄建立webroot文件夾做爲資源路徑

 

        在客戶/服務器通訊模式中, 服務器端須要建立監聽端口的 ServerSocket, ServerSocket 負責接收客戶鏈接請求,先建立一個ServerSocket

 ServerSocket serverSocket = null;
    int port = 80;
    try {
      serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
    }
    catch (IOException e) {
      e.printStackTrace();
      System.exit(1);
    }

   爲了方便測試,我設置接聽了localhost的80端口,socketServer能時刻待命,接收來自客戶端的鏈接請求,socketServer的實現使用了鏈接池和線程池,不過具體的實現不在本文討論範圍,有須要的朋友能夠自行研究.

       

 socket = serverSocket.accept();
 input = socket.getInputStream();
 output = socket.getOutputStream();

    接收到客戶請求後serverSocket會返回一個socket實例,而後獲取InputStream和OutPutStream

    

        Request request = new Request(input);
        request.parse();

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

        socket.close();

  而後件inputStream流解析成request,固然這個request仍是很原始的request,獲取請求裏面的uri,解析以後將request傳入response

 

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

  request從流中讀取信息,並在請求行中解析出請求的uri

     如:GET index.html HTTP/1.1 ,uri就會被設置成index.html,在項目的根目錄建立一個webroot的文件夾,建立一個index.html文件,uri和這個文件名要對應

     在response中,將讀取本地的webroot文件夾的request的uri對應名字的文件,經過socket的outputStream發送給客戶端

   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的main方法,而後打開瀏覽器,輸入localhost/index.html

   

 

 

    這個純靜態的服務器仍是很簡單的,可是若是你是初學者的話,仍是但願你手動實現一遍

    GitHub地址:https://github.com/Asens/AsServer/tree/master/AsServerV1.0

    在下一章我將會實現一個支持簡單servlet的服務器

相關文章
相關標籤/搜索