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的服務器