• 从零开始写一个Tomcat(叁)--请求解析


      挖坑挖了这么长时间也该继续填坑了,上文书讲到从零开始写一个Tomcat(贰)--建立动态服务器,讲了如何让服务器解析请求,分离servlet请求和静态资源请求,读取静态资源文件输出或是通过URLClassLoader找到我们请求的servlet,反射生成对应的实例,调用其service方法,传递初级解析的request和response,完成请求.

    这很tomcat,but too simple

    阅读本文,你将了解

    • 连接器(connector),处理器(processor)逻辑分离
    • 如何高效的解析请求中的header,parameters
    • 生成更加完善的ServletRequest和ServletResponse
    • Tomcat经典的facade模式

    本章的代码部分是自己写的,更多的是从Tomcat源码上扒下来的

    HttpConnector / HttpProcessor

      之前的AsServer的主类中实现了所有接收请求,解析request,生成response,调用ServletProcessor的方法.我们首先将逻辑分离.

      HttpConnector只负责接收请求,将SocketServer调用accept方法获得的socket传递给processor实例,本次请求中HttpConnector的工作结束,接下来的任务由HttpProcessor继续

      HttpProcessor总体的逻辑和之前没有大的变化,基本上就是创建Request,创建Response,解析request,解析header,判断请求的是静态资源还是动态资源,并交给相应的处理器处理.

    public void process(Socket socket){
            SocketInputStream is;
            OutputStream os;
            try{
                is=new SocketInputStream(socket.getInputStream(),2048);
                os=socket.getOutputStream();
                request = new HttpRequest(is);
                response = new HttpResponse(os);
                response.setRequest(request);
                response.setHeader("Server", "Container");
                parseRequest(is, os);
                parseHeaders(is);
                if (request.getRequestURI().startsWith("/servlet/")) {
                    ServletProcessor processor = new ServletProcessor();
                    processor.process(request, response);
                }
                else {
                    response.getWriter().write("no servlet");
                    ((HttpResponse) response).finishResponse();
                }
    
                socket.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }

    新加入SocketInputStream类,SocketInputStream中主要有2个方法

    public void readRequestLine(HttpRequestLine requestLine) throws IOException
    
    
    public void readHeader(HttpHeader header) throws IOException

      readRequestLine负责读取request的第一行,分别得到method,path和protocol

      之前的方法也能解析出这些信息,为什么还要额外去解析呢,因为效率.

      之前的方法是读取出全部流的字节码,转换成字符串,再用indexOf和substring等方法解析,而SocketInputStream并不会去把得到的字节码装换成字符串而是保持字节码的形态,逐位的读取字节码,转化成char,填充到method,path和protocol对应的数组中,最后从method,path和protocol对应的数组中得到其对应的字符串.readHeader中也是相似的方法.节省了字符串的"繁重"的操作.

      解析得到各项信息之后开始填充ServletRequest接口中的各种方法,为了加快速度,对于请求的各种参数,并不是在生成request的过程中,而是发生在第一次调用时

    public String getParameter(String name) {
        parseParameters();
        String values[] = (String[]) parameters.get(name);
        if (values != null)
          return (values[0]);
        else
          return (null);
      }

      然后生成response,填充了部分新的方法,就不一一列举了,值得一提的是,为了保证response.getWriter().write/print/println能正确的写出,创建了一个ResponseWriter,对于PrintWriter的写的方法之前全加上了一层flush(),就像这样,并且在最后调用finishResponse()保证全部刷出以及关闭writer

    public void print(char c) {
        super.print(c);
        super.flush();
      }
    
      public void print(char ca[]) {
        super.print(ca);
        super.flush();
      }
    
      public void print(double d) {
        super.print(d);
        super.flush();
      }

      现在得到生成的HttpRequest和HttpResponse,但是并不能直接的将这个传递给servlet,里面有属于Tomcat自身的方法,tomcat并不希望开发者去调用这些方法,因此使用了外观模式,

    新建HttpRequestFacade和HttpResponseFacade,实现ServletRequest和ServletRespons接口,将HttpRequest和HttpResponse传递进来,仅实现HttpRequest和HttpResponse中属于ServletRequest和ServletRespons接口的方法,这样servlet就只能调用这些方法了.

    源码地址:https://github.com/Asens/AsServer/tree/master/AsServerV1.1 

    附上测试:

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
            String aa=req.getParameter("username");
            System.out.println(aa);
            res.getWriter().write(aa);
        }

       本来准备把多线程也写进来的,但是多线程这个概念相对独立,与之前的内容关系不大,就下一章再写吧,demo已经写好了,有兴趣的同学可以先看一下

     https://github.com/Asens/AsServer/tree/master/AsServerThreadDemo

    下集预告:引入线程池和容器的基础概念,服务器能够同时处理多个请求
  • 相关阅读:
    SSAS的维度表之间的关系只能有一个不能有多个
    SqlServer 在创建数据库时候指定的初始数据库大小是不能被收缩的
    SQL Server数据库的三种恢复模式:简单恢复模式、完整恢复模式和大容量日志恢复模式(转载)
    HttpHandler和ashx要实现IRequiresSessionState接口才能访问Session信息(转载)
    Jquery Ajax调用aspx页面方法 (转载)
    linux查找目录下的所有文件中是否含有某个字符串 <zhuan>
    ubuntu下使用sdk manager 安装sdk 其他版本
    Ubuntu更新命令 <转>
    sudo:must be setuid root 解决方法 <转>
    Ubuntu 查看磁盘空间大小命令<转>
  • 原文地址:https://www.cnblogs.com/asens/p/6110455.html
Copyright © 2020-2023  润新知