服务器理解(tomcat)

一、tomcat本质上是一个HTTP服务器,核心是servlet容器

二、原理

       Connector是用来“连接”容器里边的请求的。Connector是为接收到每一个 HTTP 请求构造一个 request 和 response 对象。然后它把流程传递给容器。容器从连接器接收到 requset 和 response 对象,之后调用 servlet 的 service 方法用于响应。例如,在它调用 servlet 的 service 方法之前,它必须加载这个 servlet,验证用户(假如需要的话),更新用户会话等等。

三、服务器功能点

     

     1. 需要有一个类去接收http请求;

  2. 需要一个自定义Request类和Response类,把接收到的请求构造成这两个类;

  3. 根据请求的格式来确定处理方式:返回静态资源 or 进入Servlet ?

  4. 需要一个Servlet类执行业务逻辑

四、构造HttpServer类

      

public class HttpServer {
       private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
       private static boolean shutdown = false;
 
     public static void main(String[] args) {
        HttpServer server = new HttpServer();
         server.await();
     }
 
     public static 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);
         }
                  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.parseUrl();
                 // create Response object
                 Response response = new Response(output);
                 response.setRequest(request);
 
                 if (request.getUri().startsWith("/v2/")) {
                     ServletProcessor processor = new ServletProcessor();
                     processor.process(request, response);
                 }
                 else {
                     StaticResourceProcessor processor =
                             new StaticResourceProcessor();
                     processor.process(request, response);
                 }
                 // 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();
                 System.exit(1);
             }
         }
     }
 }
    

服务器启动入口放在了HttpServer里面。await()方法负责接收Socket连接,只有当用户输入了代表shutdown的URL时,服务器才会停止运行。Request类提供了解析请求的功能,根据请求的url,来决定是返回静态资源,或者进入对应的servlet类执行service逻辑。

    ServerSocket这个类的用法。Socket 类代表一个客户端套接字,即任何时候你想连接到一个远程服务器应用的时候,你都会第一时间想到这个类。而ServerSocket 和 Socket 不同,服务器套接字的角色是等待来自客户端的连接请求。一旦服 务器套接字获得一个连接请求,它创建一个 Socket 实例来与客户端进行通信。 ServletSocket套接字的其中一个构造函数为

public ServerSocket(int port, int backLog, InetAddress bindingAddress); 

    port代表端口号,backLog代表这个套接字可支持的最大连接数量,bindingAddress代表服务器绑定的地址。一旦你有一个 ServerSocket 实例,你可以通过调用 ServerSocket 类的 accept 方法j。这个监听当前地址的当前端口上的请求,方法只会在有连接请求时才会返回,并且返回值是一个 Socket 类的实例。

五、request and response

servlet 的 service 方法从 servlet 容器中接收一个 javax.servlet.ServletRequest 实例 和一个 javax.servlet.ServletResponse 实例。这就是说对于每一个 HTTP 请求,servlet 容器 必须构造一个 ServletRequest 对象和一个 ServletResponse 对象并把它们传递给正在服务的 servlet 的 service 方法。

public class Request implements ServletRequest {

     private InputStream input;
     private String uri;
  
       public Request(InputStream input) {
          this.input = input;
     }
  
     public String getUri(){
          return uri;
      }
  
     public void parseUrl() {
          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 static 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;
      }
  
      @Override
      public Object getAttribute(String name) {
          return null;
      }
  
      @Override
      public Enumeration getAttributeNames() {
          return null;
      }
  
      @Override
      public String getCharacterEncoding() {
          return null;
      }
  
      @Override
      public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
 
      }
  
      @Override
      public int getContentLength() {
          return 0;
      }
  
      @Override
      public String getContentType() {
          return null;
      }
  
      @Override
      public ServletInputStream getInputStream() throws IOException {
          return null;
      }
  
      @Override
      public String getParameter(String name) {
          return null;
      }
  
      @Override
      public Enumeration getParameterNames() {
          return null;
      }
  
      @Override
      public String[] getParameterValues(String name) {
          return new String[0];
      }
  
      @Override
      public Map getParameterMap() {
          return null;
      }
  
     @Override
     public String getProtocol() {
         return null;
     }
 
     @Override
     public String getScheme() {
         return null;
     }
 
     @Override
     public String getServerName() {
         return null;
     }
 
     @Override
     public int getServerPort() {
         return 0;
     }
 
     @Override
     public BufferedReader getReader() throws IOException {
         return null;
     }
 
     @Override
     public String getRemoteAddr() {
         return null;
     }
 
     @Override
     public String getRemoteHost() {
         return null;
     }
 
    @Override
     public void setAttribute(String name, Object o) {
 
     }
 
     @Override
     public void removeAttribute(String name) {
 
     }

     @Override
     public Locale getLocale() {
         return null;
     }
 
     @Override 
    public Enumeration getLocales() {
         return null;
     }
 
     @Override
     public boolean isSecure() {
         return false;
     }
 
     @Override
     public RequestDispatcher getRequestDispatcher(String path) {
         return null;
     }
 
     @Override
     public String getRealPath(String path) {
         return null;
     }
 
     @Override
     public int getRemotePort() {
         return 0;
     }
 
     @Override
     public String getLocalName() {
         return null;
     }
 
     @Override
     public String getLocalAddr() {
         return null;
     }
 
     @Override
     public int getLocalPort() {
         return 0;
     }
 }
Request类代表一个 request 对象并被传递给 servlet 的 service 方法。就本身而言,它必须实现 javax.servlet.ServletRequest 接口。这个类必须提供这个接口所有方法的实现。不过,我们想要让它非常简单并且仅仅提供实现其中一些方法,比如解析url的功能。在Request初始化时初始化成员变量inputStream,并且用parseUrl()方法创建了一个字节数组来读入输入流,并转化为成一个StringBuffer对象,进而解析url。

public class Response implements ServletResponse {
  
       private static final int BUFFER_SIZE = 1024;
      Request request;
       OutputStream output;
       PrintWriter writer;
   
       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 {
                  String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
                          "Content-Type: text/html\r\n" + "Content-Length: 23\r\n" + "\r\n" +
                          "

File Not Found

"; output.write(errorMessage.getBytes()); } } catch (Exception e) { System.out.println(e.toString()); } finally { if (fis != null) fis.close(); } } @Override public String getCharacterEncoding() { return null; } @Override public String getContentType() { return null; } @Override public ServletOutputStream getOutputStream() throws IOException { return null; } @Override public PrintWriter getWriter() throws IOException { writer = new PrintWriter(output, true); return writer; } @Override public void setCharacterEncoding(String charset) { } @Override public void setContentLength(int len) { } @Override public void setContentType(String type) { } @Override public void setBufferSize(int size) { } @Override public int getBufferSize() { return 0; } @Override public void flushBuffer() throws IOException { } @Override public void resetBuffer() { } @Override public boolean isCommitted() { return false; } @Override public void reset() { } @Override public void setLocale(Locale loc) { } @Override public Locale getLocale() { return null; } 1 }
复制代码

     Response类则提供了发送静态资源的功能。sendStaticResource()方法根据request内解析过的url,在本地寻找指定文件。如果找得到,把文件内容读出到浏览器,如果找不到,那么返回404的错误码


六、PrimitveServlet

public class PrimitiveServlet implements Servlet {
  
      @Override
      public void init(ServletConfig config) throws ServletException {
          System.out.println("init");
      } 
     @Override
     public ServletConfig getServletConfig() {
         return null;
     }
 
     @Override
     public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("from service");
        PrintWriter out = res.getWriter();
         out.println("Hello. Roses are red.");
         out.print("Violets are blue.");
     } 
     @Override
     public String getServletInfo() {
         return "this is v2 info";
     }
 
     @Override
     public void destroy() {
         System.out.println("destroy");
     }
 }
PrimitiveServlet实现了标准的Servlet接口。我们简单的实现了Servlet生命周期的其他方法,并在service()方法我们做了最简单的向浏览器吐文字的操作

七、ServletProcessor 和 StaticResourceProcessor

public class StaticResourceProcessor {
  
     public void process(Request request, Response response){
         try{
             response.sendStaticResource();
          }catch (IOException e){
            e.printStackTrace();
          }
      }
 }
复制代码

  StaticResourceProcessor则简单的调用response的sendStaticResource()方法来返回静态资源。

    最后我们还需要建一个辅助类Constants指定静态资源的存放路径

 public class Constants {
     public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
 }
启动main函数来启动我们的http服务器。在浏览器输入http://localhost:8080/test

这个url访问的是我的电脑中的一个文件test,它的存储路径为 /Users/wangyu/Documents/workspace/Tomcat/webroot/test,即当前项目的webroot目录下。

我们还可以输入一个servlet地址查看效果。在浏览器输入http://localhost:8080/v2/PrimitiveServlet


你可能感兴趣的:(服务器理解(tomcat))