我們這些可憐蟲,只有沿著大神的思路,這樣我們才能進步得更快;因為我們不是跟大神處于同一級別上。所以我這里是參考《How Tomcat Works》這本英文版的大作來理解tomcat的工作原理
本人認為,Tomcat容器源碼是學習java編程以及設計模式等的絕佳范例,深入理解其源碼對我輩開發人員的編程水平提高大有裨益!
我們可以從該書指定的官方網址下載相關示例源碼 http://www.brainysoftware.com,本文順著作者的思路,介紹一個簡單的web服務器
我們知道,web服務器是使用http協議與客戶端進行通信,所以讀者有必要先了解http協議格式;基于java的web服務器會使用兩個重要的類 java.net.Socket與java.net.ServerSocket(服務器端與客戶端通過Socket通信)
關于http協議,網上的資料汗牛充棟,本人在這里加上簡略的描述(http協議基于tcp協議)
http客戶端請求包括如下部分:
Method-URI-Protocol/Version 方法-地址-版本
Request header 請求頭
Entity body 請求實體
比如 http://www.outletss.com/ ?是本人以前幫客戶做的網站,如果我們在瀏覽器打開該url地址,實際上客戶端向服務器發送了如下格式的消息
GET http://www.outletss.com/ HTTP/1.1 Host: www.outletss.com Connection: keep-alive Accept: text/html ,application/xhtml+xml,application/xml ; q=0.9,*/*;q=0.8 User-Agent: Mozilla/5.0 (Windows NT 5.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36 Accept-Encoding: gzip ,deflate, sdch Accept-Language: zh-CN ,zh ; q=0.8 Cookie: lzstat_uv=12863370662865423613|2989608 ; CKFinder_Path=Images%3A%2F%3A1; JSESSIONID=D7F9EC74149CB674D19A253B46273A77; lzstat_ss=1366758708_0_1375562495_2989608
http服務器端響應包括如下部分:
Protocol-Status code-Description 協議狀態 描述代碼
Response headers 響應頭
Entity body 響應實體
然后服務器端向客戶端響應了如下格式的消息
HTTP/1.1 200 OK Connection: close Date: Sat , 03 Aug 2013 15:00:30 GMT Server: Microsoft-IIS/ 6.0 X-UA-Compatible: IE = EmulateIE7 X-Powered-By: ASP.NET Set-Cookie: JSESSIONID =0A5B07FF5661CA6F8D87937A54B4EEF5 ; Path=/; HttpOnly Content-Type: text/html ; charset=UTF-8 Content-Language: zh-CN <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" > <html xmlns ="http://www.w3.org/1999/xhtml" > //這里省略了html代碼 </html>
基于java的Socket編程,可以參考java網絡編程相關資料,Socket服務器端與客戶端的網絡交互與本地文件系統I/O具有一致的編程模型,基本上也是輸入流與輸出流的概念(如果你不懂輸入流輸出流的概念,基本上還要去修煉)
Socket分為客戶端與服務器端,Socket表示客戶端套接字,ServerSocket表示服務器端套接字,我們參考書中示例,看一個簡單的服務器怎么實現
HttpServer類表示一個web服務器,示例代碼如下:
public class HttpServer { /** WEB_ROOT is the directory where our HTML and other files reside. * For this package, WEB_ROOT is the "webroot" directory under the working * directory. * The working directory is the location in the file system * from where the java command was invoked. */ public static final String WEB_ROOT = System.getProperty( "user.dir") + File.separator + "webroot" ; // shutdown command private static final String SHUTDOWN_COMMAND = "/SHUTDOWN" ; // the shutdown command received private boolean shutdown = false ; public static void main(String[] args) { HttpServer server = new HttpServer(); server.await(); } public 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 ); } // Loop waiting for a request 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.parse(); // create Response object Response response = new Response(output); response.setRequest(request); response.sendStaticResource(); // 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(); continue ; } } } }
在上面代碼里面,首先創建一個ServerSocket實例,然后用一個while循環監聽客戶端請求,接收到客戶端請求后,通過ServerSocket實例的accept方法返回Socket實例,將該Socket實例的輸入流與輸出流封裝成Request實例與Response實例,并調用Response實例的void sendStaticResource()方法響應請求。
Request類代碼如下:
public class Request { private InputStream input; private String uri; public Request(InputStream input) { this .input = input; } 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 ; } public String getUri() { return uri; } }
上面的void parse()方法是解析客戶端的請求參數,這里是解析客戶端請求的URL地址
Response類的代碼如下:
/* HTTP Response = Status-Line *(( general-header | response-header | entity-header ) CRLF) CRLF [ message-body ] Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ public class Response { private static final int BUFFER_SIZE = 1024 ; Request request; 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(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(); } } }
這里最重要的是void sendStaticResource()方法,用于向輸出流寫入數據(這里是靜態文件),響應客戶端請求
本文介紹的是一個最簡單的web服務器,Tomcat容器的復雜性遠不止如此簡單,待后文接著分析
---------------------------------------------------------------------------?
本系列How Tomcat Works系本人原創?
轉載請注明出處 博客園 刺猬的溫馴?
本人郵箱: chenying998179 # 163.com ( #改為@ )
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

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