java tomcat原理图,浅谈tomcat工作原理
一個web服務器也叫做HTTP服務器,因為它使用HTTP協議同客戶端(即瀏覽器)通信。一個基于Java的web服務器用到的兩個重要類:java.net.Socket和java.net.ServerSocket,通信協議采用HTTP。因此,很自然的接下來我們就以HTTP和java的這兩個類來談談web服務器。隨后我們再介紹一個簡單的web服務器應用。
一、HTTP(The Hypertext Transfer Protocol):
Http是允許web服務端和瀏覽器之間通過Internet發送/接收的協議,它是一個請求/響應的協議。瀏覽器請求一個文件,服務器會響應這個請求。Http用Tcp連接方式----默認端口是80.Http的第一個發布版本是Http/0.9,目前一般用的是Http1.1.
通過Http協議,通常是瀏覽器通過建立連接并且發送請求來發起一個會話事務,服務器端會響應或者給瀏覽器一個響應的連接,瀏覽器端或者服務器端可以在會話中提前終止一個連接。例如,當用一個瀏覽器作為客戶端,可以點擊停止按鈕就可以終止正在下載的文件,從而有效的關閉與web服務器端的Http連接。
1.HTTP請求:
一個Http請求包含以下3個部分:
·Method-URI-Protocal/Version
·Request headers
·Entity body
一個HTTP請求的例子如下:
POST /examples/default.jsp HTTP/1.1
Accept:text/plain;text/html
Accept-Language:en-gb
Connection:Keep-Alive
Host:localhost
User-Agent:Mozilla/4.0(compatible;MSIE 4.01;windows 98)
Content-Length:33
Content-Type:application/x-www-form-urlencoded
Accept-Encoding:gzip,deflate
lastName=Franks&firstName=Michael
Method-URI-Protocal/Version出現在請求的第一行,即:
POST /examples/default.jsp HTTP/1.1
其中POST表示是請求的方法,/examples/default.jsp代表URI,HTTP/1.1表示Protocol/Version.
HTTP1.1支持七種請求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE和TRACE。其中GET和POST是最常用的。
URI完全地說明了源文件類型,一個URI通常是相對于服務器端的根目錄。這樣一來,URI的路徑通常是這樣的/*。URL統一資源定位符通常就是URI。協議版本代表所用的HTTP版本。
請求的文件頭request header可以體現出請求的瀏覽器信息和實體等重要信息。例如,它包含瀏覽器所用的編碼方式,實體的長度等等。每一個header被CRLF(carriage return/linefeed)分開,CRLF即回車換行。
在headers和entity實體之間,會有一個CRLF來分隔,這個對于HTTP請求格式非常重要。
CRLF告訴HTTP服務器請求的內容從哪里開始。
在上面的HTTP請求中,請求實體如下:
lastName=Franks&firstName=Michael
2.HTTP響應:
與Http請求相似,一個Http響應也由以下三部分組成:
·Protocol-Status code-Description
·Response headers
·Entity body
下面是一個HTTP響應的例子:
HTTP/1.1 200 OK
Server:Microsoft-IIS/4.0
Date:Mon,5 Jan 2004 13:13:33 GMT
Content-Type:text/html
Last-Modified:Mon,5 Jan 2004 13:13:12 GMT
Content-Length:112
HTTP Response ExampleWelcome to Brainy Software
響應的第一行與請求的第一行格式有些相似。它告訴協議是HTTP1.1,請求成功標志200.并且一切正常OK。響應的報文頭與請求的報文頭相似,也包含了一些環境參數。同樣響應報文也以CRLF來分隔開。
二、Socket類:
Socket是網絡連接的一個端口。Socket可以使應用程序在網絡中讀/寫到數據。分別位于不同計算機的兩款應用軟件可以依靠Socket相互進行接收/讀取數據,為使一臺計算機上的應用軟件發送信息給另一臺電腦,需要知道其IP地址和端口號。在Java中,socket類即是java.net.Socket類。
創建socket對象,可以用該類眾多構造方法中的一種來構造對象,其中一個是這樣的,需要host名字和端口號:public Socket (java.lang.String host,int port) .例如要連接端口號為80的yahoo.com,可以這樣來寫:new Socket(“yahoo.com”,80)。
下面的代碼片斷創建了一個socket類,不過是同本機127.0.0.1通信。
Socket socket = new Socket(“127.0.0.1”,”8080”);
OutputStream os = socket.getOutputStream();
boolean autoflush = true;
PrintWriter out = new PrintWriter(socket.getOutputStream(),autoflush);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//發送一個HTTP請求到web 服務器端
out.println(“GET /index.jsp HTTP/1.1”);
out.println(“Host:localhost:8080”);
outprintln(“Connection:Close”);
out.println();
//讀取響應
boolean loop = true;
StringBuffer sb = new StringBuffer(8096);
while(loop){
if(in.ready){
int i=0;
while(i!=-1){
i = in.read();
sb.append((char)i);
}
}
loop = false;
}
Thread.currentThread().sleep(50);
//響應結果到顯示平臺
System.out.println(sb.toString());
Socket.close();
三、ServerSocket類
Socket類代表“客戶端”的socket,也就是說無論什么時候要連接遠端服務器時,創建一個socket對象即可。現在,如果要想創建一個服務器應用,比如HTTP server或者FTP server,則需要用不同的方式。這是因為server端要實時接收客戶端的請求。
ServerSocket與Socket不同。ServerSocket的角色是一直在等待客戶端的請求。一旦ServerSocket接收到客戶端的請求,則會創建一個Socket對象來進行通信。
要創建一個服務器套接字,你需要使用ServerSocket類提供的四個構造方法中的一個。你需要指定IP地址和服務器套接字將要進行監聽的端口號。通常,IP地址將會是127.0.0.1,也就是說,服務器套接字將會監聽本地機器。服務器套接字正在監聽的IP地址被稱為是綁定地址。服務器套接字的另一個重要的屬性是backlog,這是服務器套接字開始拒絕傳入的請求之前,傳入的連接請求的****隊列長度。
其中一個ServerSocket類的構造方法如下所示:
public ServerSocket(int port, int backLog, InetAddress bindingAddress);
對于這個構造方法,綁定地址必須是java.net.InetAddress的一個實例。一種構造InetAddress對象的簡單的方法是調用它的靜態方法getByName,傳入一個包含主機名稱的字符串,就像下面的代碼一樣。
InetAddress.getByName("127.0.0.1");
下面一行代碼構造了一個監聽的本地機器8080端口的ServerSocket,它的backlog為1。
new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
一旦你有一個ServerSocket實例,你可以讓它在綁定地址和服務器套接字正在監聽的端口上等待傳入的連接請求。你可以通過調用ServerSocket類的accept方法做到這點。這個方法只會在有連接請求時才會返回,并且返回值是一個Socket類的實例。Socket對象接下去可以發送字節流并從客戶端應用中接受字節流,就像前一節"Socket類"解釋的那樣。實際上,這章附帶的程序中,accept方法是****用到的方法
四、實例運用
我們這個應用包含如下三個類:
·HttpServer
·Request
·Response
該應用程序的入口(即main方法)在HttpServer類中,main方法創建一個HttpServer對象,然后調用其await方法,該方法正如其名,一直在監聽給定的端口,等待HTTP請求,一旦有請求,則進行接收,然后返回response對象。這個方法一直在監聽客戶端的請求,直到有shutdown命令關閉之。
這個應用不僅僅能發送靜態資源,例如HTML文件、image圖片以及某一文件夾下的文件,而且能處理動態傳送而來的字節。但是它不傳送任何的header,比如dates、cookies等。
下面我們來詳細的看看這三個類。
HttpServer類
HttpServer類是服務器端代碼,如清單1.1。清單1.2詳細展現await方法,在清單1.1中不再顯示。
list1.1:HttpServer類
public class HttpServer{
/**
*WEB_ROOT這個路徑下放置HTML文件和其他一些文件。
*在這個包中,WEB_ROOT就是工作路徑”webroot”。
*工作路徑是文件系統中java命令所調用的位置。
*/
public static final String WEB_ROOT =
System.getProperty(“user.dir”)+File.separator + “webroot”;
//關閉命令
private static final String SHUTDOWN_COMMAND = “/SHUTDOWN”;
//接收shutdown命令
private boolean shutdown = false;
public static void main(String[] args){
HttpServer server = new HttpServer();
server.await();
}
public void await(){
…
}
}
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);
}
//循環等待請求
while(!shutdown){
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try{
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream();
//創建Request對象,并且parse
Request request = new Request(input);
request.parse();
//創建Response對象
Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource();
//關閉socket
socket.close();
//查看URI是否為關閉uri
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
}catch(Exception e){
e.printStackTrace();
continue;
}
}
}
這個web服務器可以訪問到所有WEB_ROOT目錄以及子目錄下的靜態資源,WEB_ROOT的初始化如下:public static final String WEB_ROOT =
System.getProperty(“user.dir”)+File.separator +”webroot”;
代碼包含了一個叫做webroot的目錄,該目錄下有一些靜態資源。要訪問服務器下的靜態資源,URL可以這樣寫:http://machineName:port/staticResource.如果是跨機器訪問,那么machineName就是計算機的名字或者IP地址,如果是同一臺機器,則為localhost或著計算機名,端口就是8080,staticResource就是你將要訪問的文件名,但是該文件必須在WEB_ROOT目錄下。
例如,如果你在同一機器通過服務器訪問,要訪問該服務的index.html文件,URL為:http://localhost:8080/index.html.
如果要關閉服務,則可以在瀏覽器上輸入預先在程序中設置好的url路徑。比如現在要停止當前正在運行的服務,我們這個例子中的關閉命令是通過HttpServer類中靜態常量SHUTDOWN來控制,private static final String SHUTDOWN_COMMAND = “/SHUTDOWN”,因此我們要關閉該例子的服務,url可以這樣來寫:http://localhost:8080/SHUTDOWN.
現在,讓我們來看看await方法。這個方法名是await而不是wait,主要是因為java.lang.Object這個超類中有個關于多線程的方法名叫做wait。await方法一開始就創建了一個ServerSocket對象,然后進行一個while循環。
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
//循環等待request請求
while (!shutdown) { ... }
循環代碼中當在端口8080上有HTTP的請求時,serverSocket便會通過accept方法返回一個socket對象,即:socket = serverSocket.accpet().
Request類:
該類代表一個HTTP request對象。該request對象實例化需要傳遞一個socket對象的inputstream對象。可以調用InputStream對象的read方法來獲取HTTP request對象所傳輸的數據。
該類的具體內容如下listing 1.3.該類含有兩個方法,parse方法(listing 1.4)和getUri(listing 1.5)方法。
listing 1.3:
public class Request {
private InputStream input;
private String uri;
public Request(InputStream input) {
this.input = input;
}
public String getUri() {
return uri;
}
public void parse() {
……
}
private String parseUri(String requestString) {
……
}
}
Listing 1.4:
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
request.append((char) buffer[j]);
}
System.out.print(request.toString());
uri = parseUri(request.toString());
}
Parse方法解析了request對象傳輸而來的數據。這個方法別無它途,僅僅要獲取該http請求中的url路徑。下面的getUri方法則返回了該url路徑。
Listing 1.5:
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;
}
Response類:
該類如下:
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 {
// 找不到文件
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"
File Not Found
";output.write(errorMessage.getBytes());
}
}catch (Exception e) {
// 如果不能實例化File對象,則會拋出一個異常
System.out.println(e.toString() );
}finally {
if (fis!=null)
fis.close();
}
}
}
首先注意到的是,通過構造函數獲取到java.io.OutputStream對象,如下:
public Response(OutputStream output) {
this.output = output;
}
運行這個應用:
在瀏覽器地址欄上輸入地址:http://localhost:8080/index.html,可以看到測試頁面。
如果輸入一個不存在的文件地址,http://localhost:8080/indexs.html 如下,則返回404錯誤。
總結
以上是生活随笔為你收集整理的java tomcat原理图,浅谈tomcat工作原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php 0-1,PHP 动态解决0-1背
- 下一篇: Weave是什么