關(guān)于這篇文章也確實籌劃了很久,今天決定開篇寫第一篇,說起 tomcat 首先很容易聯(lián)想到 IIS ,因為我最開始使用的就是 .net 技術(shù),我第一次使用 asp 寫學生成績管理系統(tǒng)后,很茫然如何讓別人都能看到或者說使用這個系統(tǒng)呢?由此認識了 IIS ,它是一個 web 容器,天生的多線程,及時響應用戶提交的請求返回 html 頁面,這就是我了解的最初的 web 容器的功能,由此我們來認識 tomcat 也并不困難,可以的話,在了解完 tomcat 后我們可以繼續(xù)了解 jboss 、 jetty 等,好我們進入主題。
我們在平時開發(fā)的過程中是在使用 eclipse 時候才啟動 tomcat ,對于一個 web 容器而言,簡而言之,它是系統(tǒng)的一個守護進程,守護著對這臺服務器某個端口發(fā)起的請求,基于這一點,它就需要一個監(jiān)聽程序,這個監(jiān)聽程序來獲取來自這個端口的特定請求的數(shù)據(jù), ok ,直接點講,我們這里使用 Socket 來獲取某個端口,通常是 80 端口的 http 請求,通過簡單的 Java
程序的死循環(huán)(粗糙的做法,后面逐步優(yōu)化)來實現(xiàn)不斷的獲取 80 端口 http 請求,來達到監(jiān)聽 80 端口 http 請求的目的。j ava.net包 下面的 Socket 和 ServerSocket 兩個類就能實現(xiàn)我們對 8080 端口的監(jiān)聽,去除中間的邏輯代碼,我們只看這個兩個類的演繹的話如下:
?
1 ServerSocket serverSocket = new ServerSocket(8080, 1, InetAddress.getByName("10.10.10.106"));
?
對本機的 8080 端口進行監(jiān)聽
?
1 socket = serverSocket.accept(); 2 input = socket.getInputStream(); 3 output = socket.getOutputStream();
?
以上代碼就是獲取監(jiān)聽結(jié)果。
這是最簡單和最精簡的Socket 通訊原理,基于這個核心點我們來開發(fā)一個簡易的,可以提供靜態(tài)頁面訪問的? custom?tomcat ,準備一個 index.html 文件放到 /home/webroot 目錄下,那么除去拓展上面代碼外,我們還需要一個 Response 和一個 Request 。
類設(shè)計如下:
HttpServer :?主函數(shù)所在類,負載啟動 ServerSocket 和?操作整合 Socket 監(jiān)聽到的數(shù)據(jù),以及返回結(jié)果,即操作 Response 和 Request 。
Request:? 封裝 Socket 監(jiān)聽到的用戶端請求,包括請求的 http?uri 信息。
Response :?封裝需要推送到客戶端的結(jié)果數(shù)據(jù),即我們需要根據(jù) http?uri? 去本機尋找相應的資源,寫給客戶端。
言簡意賅,進入代碼,首先? Request 類代碼:
?
1 public class Request 2 { 3 private InputStream input; 4 private String uri; 5 6 public Request(InputStream input) { 7 this .input = input; 8 } 9 10 public void parse() 11 { 12 StringBuffer request = new StringBuffer(2048 ); 13 int i; 14 byte [] buffer = new byte [2048 ]; 15 try 16 { 17 i = input.read(buffer); 18 } 19 catch (IOException e) 20 { 21 e.printStackTrace(); 22 i = -1 ; 23 } 24 25 for ( int j=0; j<i; j++ ) 26 { 27 request.append(( char ) buffer[j]); 28 } 29 System.out.print(request.toString()); 30 uri = parseUri(request.toString()); 31 } 32 33 private String parseUri(String requestString) 34 { 35 int index1, index2; 36 index1 = requestString.indexOf(' ' ); 37 if (index1 != -1 ) { 38 index2 = requestString.indexOf(' ', index1 + 1 ); 39 if (index2 > index1) 40 return requestString.substring(index1 + 1 , index2); 41 } 42 return null ; 43 } 44 45 public String getUri() 46 { 47 return uri; 48 } 49 }
?
代碼解釋:類包括一個屬性和兩個方法, input 屬性即是從 Socket 監(jiān)聽到的信息, Socket 會將監(jiān)聽到的信息放入一個 InputStream 中,我們使用 Reqeust 類的 Input 屬性來接受。接收到輸入流后,在 parse 中對這個輸入流進行解析成字符串,即對 Http 請求進行拆解,得到完整的 Http?URL ,所以這個方法是私有的,是類存在意義的核心所在,而提供的對外方法 parseUri 是負載將 parse 解析的 url 結(jié)果提供給外界,即,客戶端發(fā)來請求那個文件,具體的是最終提供給 Response 類, Response 類得到這個文件名稱后,去本地制定目錄讀取文件。 Tomcat 中通常就是 webapps 目錄啦,很熟悉了吧,哈哈。
Response 類如何實現(xiàn)這個讀取文件的歷史使命呢,代碼如下 :
?
1 public class Response { 2 3 private static final int BUFFER_SIZE = 1024 ; 4 Request request; 5 OutputStream output; 6 7 public Response(OutputStream output) 8 { 9 this .output = output; 10 } 11 12 public void setRequest(Request request) 13 { 14 this .request = request; 15 } 16 17 public void sendStaticResource() throws IOException 18 { 19 byte [] bytes = new byte [BUFFER_SIZE]; 20 FileInputStream fis = null ; 21 try 22 { 23 File file = new File(HttpServer.WEB_ROOT, request.getUri()); 24 if (file.exists()) 25 { 26 fis = new FileInputStream(file); 27 int ch = fis.read(bytes, 0 , BUFFER_SIZE); 28 while (ch!=-1 ) { 29 output.write(bytes, 0 , ch); 30 ch = fis.read(bytes, 0 , BUFFER_SIZE); 31 } 32 } 33 else 34 { 35 String errorMessage = "HTTP/1.1 404 File Not Found\r\n" + 36 "Content-Type: text/html\r\n" + 37 "Content-Length: 23\r\n" + 38 "\r\n" + 39 "<h1>File Not Found</h1>" ; 40 output.write(errorMessage.getBytes()); 41 } 42 } 43 catch (Exception e) 44 { 45 System.out.println(e.toString()); 46 } 47 finally { 48 fis.close(); 49 } 50 51 } 52 }
?
代碼解釋: Response 一共三個屬性,一個方法。三個屬性,一個是設(shè)置屬性, BUFFER_SIZE 設(shè)置讀寫字節(jié)流大小,關(guān)于讀寫文件,我個人覺得和服務器的性能和程序性能息息相關(guān),不宜設(shè)定過大或過小(此處有不同見解的同仁歡迎來噴,我對這塊理解目前限于此)。 Reqeust 屬性,對照前文呼應, Response 需要獲取 Request 類的 uri 結(jié)果信息,所以這里放了一個 Request 屬性,獲取 uri 。 Output ,就不用說了,也是這個類存在的核心意義,依照 Request 類提供的 uri 信息,在本地讀寫文件后,形成一個輸出來,存放到 output 中,那么這項工作就由 sendStaticResource 這個共有方法完成啦。
好,代碼到這個,可以說我們大家已經(jīng)看到一個 tomcat 模型了,有點萬事俱備,只欠東風的感覺,客戶端發(fā)起請求, Response 和 Reqeust 有了,那么繼續(xù)往上游考慮, Reqeust 依賴于客戶端的請求,自然以來于 Socket 數(shù)據(jù)。我們在這里做得簡便一點,將 ServerSocket 和 Socket 封裝到一個 HttpServer 類中來,代碼如下:
?
1 public class HttpServer { 2 3 public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot" ; 4 private static final String SHUTDOWN_COMMAND = "/SHUTDOWN" ; 5 private boolean shutdown = false ; 6 public static void main(String[] args) 7 { 8 HttpServer httpServer = new HttpServer(); 9 httpServer.await(); 10 } 11 12 public void await() 13 { 14 ServerSocket serverSocket = null ; 15 Integer port = 8080 ; 16 try 17 { 18 serverSocket = new ServerSocket(port, 1, InetAddress.getByName("10.10.10.106" )); 19 } 20 catch (IOException e) 21 { 22 e.printStackTrace(); 23 System.exit(1 ); 24 } 25 26 while (! shutdown) 27 { 28 Socket socket = null ; 29 InputStream input = null ; 30 OutputStream output = null ; 31 try 32 { 33 socket = serverSocket.accept(); 34 35 input = socket.getInputStream(); 36 output = socket.getOutputStream(); 37 Request request = new Request(input); 38 request.parse(); 39 Response response = new Response(output); 40 response.setRequest(request); response.sendStaticResource(); socket.close(); 41 shutdown = request.getUri().equals(SHUTDOWN_COMMAND); 42 } 43 catch (Exception e) 44 { 45 e.printStackTrace(); continue ; 46 } 47 } 48 } 49 }
?
代碼解釋:我們知道啟動 tomcat 之后,只要服務正常,客戶端任意時候發(fā)起一個 http 請求, tomcat 就會響應,那么這里我們肯定需要一個 while 循環(huán)來模擬不間斷的監(jiān)聽,類 await 方法就是負責不斷的獲取 socket 監(jiān)聽到的結(jié)果,有立刻調(diào)動 Reqeust 和 Response 進行響應,加入主函數(shù),為的是我們這個是模擬的控制臺程序,需要一個程序入口, main 函數(shù)就是程序入口。此外, HttpServer 類包括一個靜態(tài)屬性 SHUTDOWN_COMMAND ,輸入為 true 則停止這個 main 函數(shù),變量初始值為 false ,當客戶端也就是 Request 響應得到客戶端輸入? http://10.10.10.108:8080/SHUTDOWN 時候,則變量在 while 中會置成 true ,緊接著停止 main ,結(jié)束應用程序進程。
在 eclipse 中或者在命令行中啟動這個 main 函數(shù),命令行則是輸入? java?HttpServer.java 。 eclipse 則是在 main 函數(shù)中右鍵? run?as?application 啟動。 我們打開瀏覽器,輸入? http://10.10.10.108:8080/index.html, 回車結(jié)果如下:
本地文件:
好了,夜深啦,就此擱筆了,拋磚引玉,歡迎提議和討論,這個系列會繼續(xù)下去,直到一個完整的可以響應一個java action請求的custom tomcat產(chǎn)品出來。
最后附上我的源代碼:http://files.cnblogs.com/aspnetdream/Project.zip
參考:《How tomcat works》?作者:Budi Kurniawan & Paul Deck
?
更多文章、技術(shù)交流、商務合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
