試解析Tomcat運行原理(一)--- socket通信

  關於這篇文章也確實籌劃了好久,今天決定開篇寫第一篇,提及tomcat首先很容易聯想到IIS,由於我最開始使用的就是.net技術,我第一次使用asp寫學生成績管理系統後,很茫然如何讓別人都能看到或者說使用這個系統呢?由此認識了IIS,它是一個web容器,天生的多線程,及時響應用戶提交的請求返回html頁面,這就是我瞭解的最初的web容器的功能,由此咱們來認識tomcat也並不困難,能夠的話,在瞭解完tomcat後咱們能夠繼續瞭解jbossjetty等,好咱們進入主題。html

  咱們在平時開發的過程當中是在使用eclipse時候才啓動tomcat,對於一個web容器而言,簡而言之,它是系統的一個守護進程,守護着對這臺服務器某個端口發起的請求,基於這一點,它就須要一個監聽程序,這個監聽程序來獲取來自這個端口的特定請求的數據,ok,直接點講,咱們這裏使用Socket來獲取某個端口,一般是80端口的http請求,經過簡單的Javajava

  程序的死循環(粗糙的作法,後面逐步優化)來實現不斷的獲取80端口http請求,來達到監聽80端口http請求的目的。java.net包下面的SocketServerSocket兩個類就能實現咱們對8080端口的監聽,去除中間的邏輯代碼,咱們只看這個兩個類的演繹的話以下:web

 

1 ServerSocket serverSocket = new ServerSocket(8080, 1, InetAddress.getByName("10.10.10.106"));

 

  對本機的8080端口進行監聽瀏覽器

 

1 socket = serverSocket.accept();             
2 input = socket.getInputStream(); 
3 output = socket.getOutputStream(); 

 

  以上代碼就是獲取監聽結果。tomcat

  這是最簡單和最精簡的Socket通信原理,基於這個核心點咱們來開發一個簡易的,能夠提供靜態頁面訪問的 custom tomcat,準備一個index.html文件放到/home/webroot目錄下,那麼除去拓展上面代碼外,咱們還須要一個Response和一個Request服務器

  類設計以下:多線程

  HttpServer: 主函數所在類,負載啓動ServerSocket和 操做整合Socket監聽到的數據,以及返回結果,即操做ResponseRequestapp

  Request: 封裝Socket監聽到的用戶端請求,包括請求的http uri信息。eclipse

  Response: 封裝須要推送到客戶端的結果數據,即咱們須要根據http uri 去本機尋找相應的資源,寫給客戶端。webapp

  言簡意賅,進入代碼,首先 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監聽到的信息,Socket會將監聽到的信息放入一個InputStream中,咱們使用Reqeust類的Input屬性來接受。接收到輸入流後,在parse中對這個輸入流進行解析成字符串,即對Http請求進行拆解,獲得完整的Http URL,因此這個方法是私有的,是類存在乎義的核心所在,而提供的對外方法parseUri是負載將parse解析的url結果提供給外界,即,客戶端發來請求那個文件,具體的是最終提供給Response類,Response類獲得這個文件名稱後,去本地制定目錄讀取文件。Tomcat中一般就是webapps目錄啦,很熟悉了吧,哈哈。

  Response類如何實現這個讀取文件的歷史使命呢,代碼以下:

 

 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一共三個屬性,一個方法。三個屬性,一個是設置屬性,BUFFER_SIZE設置讀寫字節流大小,關於讀寫文件,我我的以爲和服務器的性能和程序性能息息相關,不宜設定過大或太小(此處有不一樣看法的同仁歡迎來噴,我對這塊理解目前限於此)。Reqeust屬性,對照前文呼應,Response須要獲取Request類的uri結果信息,因此這裏放了一個Request屬性,獲取uriOutput,就不用說了,也是這個類存在的核心意義,依照Request類提供的uri信息,在本地讀寫文件後,造成一個輸出來,存放到output中,那麼這項工做就由sendStaticResource這個共有方法完成啦。

  好,代碼到這個,能夠說咱們你們已經看到一個tomcat模型了,有點萬事俱備,只欠東風的感受,客戶端發起請求,ResponseReqeust有了,那麼繼續往上游考慮,Reqeust依賴於客戶端的請求,天然以來於Socket數據。咱們在這裏作得簡便一點,將ServerSocketSocket封裝到一個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以後,只要服務正常,客戶端任意時候發起一個http請求,tomcat就會響應,那麼這裏咱們確定須要一個while循環來模擬不間斷的監聽,類await方法就是負責不斷的獲取socket監聽到的結果,有馬上調動ReqeustResponse進行響應,加入主函數,爲的是咱們這個是模擬的控制檯程序,須要一個程序入口,main函數就是程序入口。此外,HttpServer類包括一個靜態屬性SHUTDOWN_COMMAND,輸入爲true則中止這個main函數,變量初始值爲false,當客戶端也就是Request響應獲得客戶端輸入 http://10.10.10.108:8080/SHUTDOWN時候,則變量在while中會置成true,緊接着中止main,結束應用程序進程。

  在eclipse中或者在命令行中啓動這個main函數,命令行則是輸入 java HttpServer.javaeclipse則是在main函數中右鍵 run as application啓動。咱們打開瀏覽器,輸入 http://10.10.10.108:8080/index.html,回車結果以下:

  本地文件:

  

  好了,夜深啦,就此擱筆了,拋磚引玉,歡迎提議和討論,這個系列會繼續下去,直到一個完整的能夠響應一個java action請求的custom tomcat產品出來。

  最後附上個人源代碼:http://files.cnblogs.com/aspnetdream/Project.zip

  參考:《How tomcat works》 做者:Budi Kurniawan & Paul Deck

相關文章
相關標籤/搜索