手書きMini版Tomcatサーバ

10485 ワード

手書きMini版Tomcatサーバ
オリジナル:sean 9468プログラマー文章集錦原文アドレス
きほんげんり
Tomcatサーバはservletコンテナで、主にjavaWebプロジェクトの導入に使用されます.Tomcatは主にsocketを傍受し、入力ストリーム、出力ストリームを獲得し、httpパケットを解析してrequest、responseを作成し、servletを呼び出すことを実現した.doFilterとservlet.サービス関数はresponse情報を埋め込み、クライアント出力ストリームに書き込む.
コアコード
詳細コードは原文をクリックしてgiteeアドレスを表示します.
コアコード:
   :BootStrap

   :HttpConnector

http   :HttpProcessor

HttpRequest

HttpResponse

   :RequesFacade

   :ResponseFacade 

テストコード:サーブレットと静的HTMLファイル.
PrimitiveServle  

test.html

Bootstrap
起動クラスは1つの主関数であり,Webプロジェクト全体のエントリであり,通常開発用のwarパッケージにはプロジェクトエントリはなく,実際にはTomcatの起動クラスにある.すなわちspring bootを用いた開発もtomcatの依存を含みjarパッケージにパッケージ化されている.同じ理屈だ.
public class BootStrap {
    public static void main(String[] args) {
        HttpConnector connector = new HttpConnector();
        connector.start();
    }
}

コネクタ
コネクタHttpConnectorの主な目的は、同期ブロックを使用してポートをリスニングすることです.リスニングコードは、新しいスレッドが担当します.すべてのクライアントリクエストは同じスレッドで処理され、効率は最良ではありませんが、小さなトラフィックを安定させるには可能です.スレッドとIOモデルについても重要な技術面である.ここでは多く述べるにすぎない.
public class HttpConnector implements Runnable {
    boolean stopped;
    private String scheme = "http";

    public String getScheme() {
        return scheme;
    }

    public void start() {
        Thread thread = new Thread(this);
        thread.start();
    }

    @Override
    public void run() {
        ServerSocket serverSocket = null;
        int port = 8080;
        try {
            serverSocket = new ServerSocket(port, 100, InetAddress.getByName("127.0.0.1"));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        while (!stopped) {
            Socket socket = null;
            try {
                socket = serverSocket.accept();

            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
            HttpProcessor processor = new HttpProcessor();
            processor.process(socket);
        }
    }
}

Http処理クラス
これはコアクラスで、Tomcatでもコネクタの役割範囲に属し、主にRequestとResponseを生成します.ここで、RequestのHeader Cookie RequestLine RequestParaパラメータは、一つ一つ解析されます.この部分はHTTPプロトコルについてもっと深く理解します.
プロセスメソッド
クライアントのInputStreamをさらにSocketInputStreamにカプセル化し、HttpRequestを作成し、HTTPヘッダ情報を解析し、HttpResponseを作成する.ここでは主にGETリクエストです.解析したURIからサーブレット処理するか静的ファイル処理するかを判断する.サーブレットは、作成したテストサーブレットコードを呼び出し、静的リソースはローカルの静的htmlファイルを処理し、見つからない場合は404に戻ります.
public void process(Socket socket) {
    SocketInputStream input = null;
    OutputStream output = null;
    try {
        input = new SocketInputStream(socket.getInputStream(), 2048);
        output = socket.getOutputStream();
        request = new HttpRequest(input);
        response = new HttpResponse(output);
        response.setRequest(request);
        response.setHeader("Server", "sean servlet container");
        parseRequest(input, output);
        parseHeaders(input);

        if (request.getRequestURI().startsWith("/servlet/")) {
            ServletProcessor processor = new ServletProcessor();
            processor.process(request, response);
        } else {
            StaticResourceProcessor processor = new StaticResourceProcessor();
            processor.process(request, response);
        }
        socket.close();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ServletException e) {
        e.printStackTrace();
    }
}

RequestLineの解析
クライアントのInputStreamをさらにSocketInputStreamにカプセル化し、HTTPの最初の行を解析するHTTP method、HTTP URI、HTTP protocol、versionであるカプセル化されたRequestLineを実現する.次にRequestのmethod,URI,protocolに値を割り当てます.注意:RequestLineの解析は、Headerの解析よりも優先されます.

        input.readRequestLine(requestLine);
        String method = new String(requestLine.method, 0, requestLine.methodEnd);
        String uri;
    String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd);
    if (method.length() < 1) {
        throw new ServletException("Missing Http request method");
    } else if (requestLine.uriEnd < 1) {
        throw new ServletException("Missing Http request URI");

    }

    int question = requestLine.indexOf("?");
    if (question >= 0) {
        request.setQueryString(new String(requestLine.uri, question + 1, requestLine.uriEnd - question - 1));
        uri = new String(requestLine.uri, 0, question);
    } else {
        request.setQueryString(null);
        uri = new String(requestLine.uri, 0, requestLine.uriEnd);
    }
    if (!uri.startsWith("/")) {
        int pos = uri.indexOf("://");
        if (pos == -1) {
            uri = "";
        } else {
            uri = uri.substring(pos);
        }
    }
    request.setUri(uri);
    request.setMethod(method);
    request.setProtocol(protocol);

}

解析Headers
Headers情報は、実はname-valueキー値ペアのみであり、cookies、content-length、user-agentなど、異なるnameに基づいて異なる要素を抽出することができる.抽出ヘッドはループであり、読み取りが完了するまでループごとにヘッドが作成されます.
private void parseHeaders(SocketInputStream input) throws IOException, ServletException {
    while (true) {
        httpHeader = new HttpHeader();
        input.readHeader(httpHeader);
        if (httpHeader.nameEnd == 0) {
            if (httpHeader.valueEnd == 0) {
                return;
            } else {
                throw new ServletException("parseHeaders exceptions");
            }
        }
        request.addHeader(httpHeader);
        if (httpHeader.name.equals("cookie")) {
            Cookie cookies[] = RequestUtil.parseCookieHeader(new String(httpHeader.value));
            for (int i = 0; i < cookies.length; i++) {
                request.addCookie(cookies[i]);
            }
        } else if (httpHeader.name.equals("content-length")) {
            int n = Integer.parseInt(new String(httpHeader.value));
            request.setContentLength(n);
        }
    }
}

サーブレット処理クラス
このサーブレット処理クラスは、URI要求の名前に従ってサーブレットClassをロードし、サービス関数をインスタンス化し、呼び出す.サービス参照についてはRequestとResponseのファセットクラスが実際に伝達されている.主な目的は,不要なインタフェースがサーブレットビジネスプログラマーに漏れることを安全に回避することである.
public class ServletProcessor {
    public void process(HttpRequest request, HttpResponse response) {
        String uri = request.getRequestURI();
        String servletName = uri.substring(uri.lastIndexOf("/") + 1);
        URLClassLoader loader = null;
        try {
            URL[] urls = new URL[1];
            URLStreamHandler streamHandler = null;
            File classPath = new File(Constants.WEB_ROOT);
            String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString();
            loader = new URLClassLoader(urls);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    Class myClass = null;
    try {
        servletName = "tomcat.step3.servlet." + servletName;

        myClass = loader.loadClass(servletName);

    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    Servlet servlet = null;
    try {
        servlet = (Servlet) myClass.newInstance();
        RequestFacade requestFacade = new RequestFacade(request);
        ResponseFacade responseFacade = new ResponseFacade(response);
        servlet.service(requestFacade, responseFacade);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (ServletException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}
HttpResponse
ここでは静的ファイルを処理するコードをHttpResponse,sendStaticResourceメソッドに格納する.
public void sendStaticResource() throws IOException {
    byte[] bytes = new byte[BUFFER_SIZE];
    try {
        File file = new File(HttpServer.WEB_ROOT, httpRequest.getRequestURI());
        System.out.println(file.getAbsolutePath());
        if (file.exists()) {
            System.out.println("    ");
            InputStreamReader reader = new InputStreamReader(new FileInputStream(file));
            BufferedReader reader1 = new BufferedReader(reader);

            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output));
        String line = null;
        while ((line = reader1.readLine()) != null) {
            System.out.println(line);
            writer.write(line);
        }
        writer.flush();
        writer.close();
        reader.close();
        reader1.close();
    } else {
        String erroMessage = "HTTP/1.1 404 Fie Not Found\r
" + "Content-Type: text/html\r
" + "Content-length:23\r
" + "\r
" + "

File Not Found

"; output.write(erroMessage.getBytes()); } } catch (Exception e) { System.out.println(e.toString()); } finally { } }

デバッグ運転
PrimitiveServiceのリクエスト
手写Mini版Tomcat服务器_第1张图片リクエストtest.html手写Mini版Tomcat服务器_第2张图片要求test-notfound.html
手写Mini版Tomcat服务器_第3张图片 手写Mini版Tomcat服务器_第4张图片