Tomcat應該都不陌生,咱們常常會把寫好的代碼打包放在Tomcat裏並啓動,而後在瀏覽器裏就能愉快的調用咱們寫的代碼來實現相應的功能了,那麼Tomcat是如何工做的?html
咱們啓動Tomcat時雙擊的startup.bat文件的主要做用是找到catalina.bat,而且把參數傳遞給它,而catalina.bat中有這樣一段話:web
Bootstrap.class是整個Tomcat 的入口,咱們在Tomcat源碼裏找到這個類,其中就有咱們常用的main方法:瀏覽器
這個類有兩個做用 :1.初始化一個守護進程變量、加載類和相應參數。2.解析命令,並執行。服務器
源碼不過多贅述,咱們在這裏只須要把握總體架構,有興趣的同窗能夠本身研究下源碼。Tomcat的server.xml配置文件中能夠對應構架圖中位置,多層的表示能夠配置多個:多線程
即一個由 Server->Service->Engine->Host->Context 組成的結構,從裏層向外層分別是:架構
好比如今有如下網址,根據「/」切割的連接就會定位到具體的處理邏輯上,且每一個容器都有過濾功能。app
下面只是簡單實現效果,當瀏覽器訪問對應地址時:socket
實現以上效果總體思路以下:網站
1.ServerSocket佔用8080端口,用while(true)循環等待用戶發請求。this
2.拿到瀏覽器的請求,解析並返回URL地址,用I/O輸入流讀取本地磁盤上相應文件。
3.讀取文件,不存在構建響應報文頭、HTML正文內容,存在則寫到瀏覽器端。
工程文件結構和pom.xml文件:
1.HttpServer核心處理類,用於接受用戶請求,傳遞HTTP請求頭信息,關閉容器:
public class HttpServer { // 用於判斷是否須要關閉容器 private boolean shutdown = false; public void acceptWait() { ServerSocket serverSocket = null; try { //端口號,最大連接數,ip地址 serverSocket = new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1")); } catch (IOException e) { e.printStackTrace(); System.exit(1); } // 等待用戶發請求 while (!shutdown) { try { Socket socket = serverSocket.accept(); InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); // 接受請求參數 Request request = new Request(is); request.parse(); // 建立用於返回瀏覽器的對象 Response response = new Response(os); response.setRequest(request); response.sendStaticResource(); //關閉一次請求的socket,由於http請求就是採用短鏈接的方式 socket.close(); //若是請求地址是/shutdown 則關閉容器 if(null != request){ shutdown = request.getUrL().equals("/shutdown"); } } catch (Exception e) { e.printStackTrace(); continue; } } } public static void main(String[] args) { HttpServer server = new HttpServer(); server.acceptWait(); } }
2.建立Request類,獲取HTTP的請求頭全部信息並截取URL地址返回:
public class Request { private InputStream is; private String url; public Request(InputStream input) { this.is = input; } public void parse() { //從socket中讀取一個2048長度字符 StringBuffer request = new StringBuffer(Response.BUFFER_SIZE); int i; byte[] buffer = new byte[Response.BUFFER_SIZE]; try { i = is.read(buffer); } catch (IOException e) { e.printStackTrace(); i = -1; } for (int j=0; j<i; j++) { request.append((char) buffer[j]); } //打印讀取的socket中的內容 System.out.print(request.toString()); url = parseUrL(request.toString()); } private String parseUrL(String requestString) { int index1, index2; index1 = requestString.indexOf(' ');//看socket獲取請求頭是否有值 if (index1 != -1) { index2 = requestString.indexOf(' ', index1 + 1); if (index2 > index1) return requestString.substring(index1 + 1, index2); } return null; } public String getUrL() { return url; } }
3.建立Response類,響應請求讀取文件並寫回到瀏覽器
public class Response { public static final int BUFFER_SIZE = 2048; //瀏覽器訪問D盤的文件 private static final String WEB_ROOT ="D:"; private Request request; private 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(WEB_ROOT, request.getUrL()); //若是文件存在,且不是個目錄 if (file.exists() && !file.isDirectory()) { 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 { //文件不存在,返回給瀏覽器響應提示,這裏能夠拼接HTML任何元素 String retMessage = "<h1>"+file.getName()+" file or directory not exists</h1>"; String returnMessage ="HTTP/1.1 404 File Not Found\r\n" + "Content-Type: text/html\r\n" + "Content-Length: "+retMessage.length()+"\r\n" + "\r\n" + retMessage; output.write(returnMessage.getBytes()); } } catch (Exception e) { System.out.println(e.toString() ); } finally { if (fis!=null) fis.close(); } } }
1.在WEB_INF文件夾下讀取web.xml解析,經過請求名找到對應的類名,經過類名建立對象,用反射來初始化配置信息,如welcome頁面,Servlet、servlet-mapping,filter,listener,啓動加載級別等。
2.抽象Servlet類來轉碼處理請求和響應的業務。發過來的請求會有不少,也就意味着咱們應該會有不少的Servlet,例如:RegisterServlet、LoginServlet等等還有不少其餘的訪問。能夠用到相似於工廠模式的方法處理,隨時產生不少的Servlet,來知足不一樣的功能性的請求。
3.使用多線程。本文的代碼是死循環,且只能有一個連接,而現實中的狀況是每每會有不少不少的客戶端發請求,能夠把每一個瀏覽器的通訊封裝到一個線程當中。