JavaWeb工作原理
[JavaWeb工作原理][第一章]一什么是JavaWeb?
JavaWeb是用Java技術(shù)來解決相關(guān)web互聯(lián)網(wǎng)領(lǐng)域的技術(shù)總和。web包括:web服務(wù)器和web客戶端兩部分,在第一個(gè)項(xiàng)目(貸款計(jì)息查詢服務(wù)器)的時(shí)候已經(jīng)知道在Web服務(wù)器的作用是接受客戶端請(qǐng)求,然后向客戶端返回一些結(jié)果.瀏覽器的作用是允許用戶請(qǐng)求服務(wù)器上的某個(gè)資源,并且向用戶顯示請(qǐng)求的結(jié)果,HTML用于告訴瀏覽器怎樣向用戶顯示內(nèi)容,HTTP是WEB上客戶端和服務(wù)器之間通信所用的協(xié)議
二HTTP協(xié)議
Http是一種超文本傳輸協(xié)議(HyperTextTransferProtocol),他是一套計(jì)算機(jī)在網(wǎng)中通信的一種規(guī)則,在TCP/IP體系結(jié)構(gòu)中,HTTP屬于應(yīng)用層協(xié)議,位于TCP/IP協(xié)議的頂層.HTTP是一種無狀態(tài)的協(xié)議,意思是指在WEB瀏覽器和WEB服務(wù)器之間不需要建立持久的連接,HTTP定義的事物處理由以下四步組成:
1.客戶端和WEB服務(wù)器建立連接
a)客戶端和服務(wù)器的連接就是與客戶端與服務(wù)器的一個(gè)TCPSocket套接字連接2.客戶端發(fā)送HTTP請(qǐng)求
a)請(qǐng)求包含:請(qǐng)求行--請(qǐng)求行是一個(gè)ACCII文本行,由請(qǐng)求的HTTP方法,請(qǐng)求的
URL,HTTP版本組成,中間用空格分開,
b)請(qǐng)求頭---HTTP協(xié)議使用HTTP頭來傳遞請(qǐng)求的元信息,
c)空行---發(fā)送回車符和退行,通知服務(wù)器以下不再有請(qǐng)求頭
d)消息體--HTTP請(qǐng)求中帶有查詢字符串時(shí),如果是GET方法,查詢字符串或表單數(shù)據(jù)附加在請(qǐng)求行中,那么消息體就沒有內(nèi)容,如果是POST方法,查詢字符串或表單數(shù)據(jù)就添加在消息體重
3.服務(wù)器端接收客戶端的HTTP請(qǐng)求,生成HTTP相應(yīng)回發(fā)
a)響應(yīng)包含:狀態(tài)行---每個(gè)HTTP響應(yīng)以一個(gè)狀態(tài)行開頭,它由HTTP協(xié)議版本,
響應(yīng)狀態(tài)碼,響應(yīng)描述組成,中間用空格分開b)響應(yīng)頭---響應(yīng)頭與請(qǐng)求頭一樣,也是一個(gè)用冒號(hào)分隔符的名稱/值對(duì),冒號(hào)前面
是HTTP頭得名稱,后面是HTTP頭得值c)空行---發(fā)送回車符和退行,通知服務(wù)器以下不再有響應(yīng)頭
d)消息體---要發(fā)送回客戶端的HTML文檔或其他要顯示的內(nèi)容等,WEB服務(wù)器
把要發(fā)送給客戶端的文檔信息放在消息體中
4.服務(wù)器端關(guān)閉連接,客戶端解析并回發(fā)響應(yīng),恢復(fù)頁面
a)HTTP響應(yīng)到達(dá)客戶端后,瀏覽器先解析HTTP響應(yīng)中的狀態(tài)行,查看請(qǐng)求是否
成功的狀態(tài)代碼,然后開始一步步解析響應(yīng)
三WEB服務(wù)器缺陷與CGI
WEB服務(wù)是被設(shè)計(jì)用來向客戶端提供HTTP服務(wù)的,它只是能向客戶端提供靜態(tài)網(wǎng)頁內(nèi)容.我們知道,靜態(tài)網(wǎng)頁只是原封不動(dòng)的待在WEB服務(wù)器目錄中,服務(wù)器知道靜態(tài)頁面,并把它原樣傳回到客戶端,每個(gè)客戶端看到的頁面都是一樣的,所以WEB服務(wù)器它本身并不具備動(dòng)態(tài)頁面,所以在最早有了解決辦法CGI,
CGI即通用網(wǎng)關(guān)接口(CommonGateWayInterface),是最早用于創(chuàng)建動(dòng)態(tài)服務(wù)器端內(nèi)容的一種技術(shù),使用CGI,WEB服務(wù)器可以將客戶端的請(qǐng)求傳遞給一個(gè)外部程序,這個(gè)外部程序可以執(zhí)行,創(chuàng)建內(nèi)容,并且將響應(yīng)傳遞給客戶端,
但是他在處理一個(gè)請(qǐng)求的時(shí)候還行,但是在處理對(duì)個(gè)請(qǐng)求的時(shí)候是沒有一個(gè)用戶請(qǐng)求就新開一個(gè)進(jìn)程,我們都知道進(jìn)程是有獨(dú)立的內(nèi)存空間的,如果請(qǐng)求用戶多的時(shí)候那么就會(huì)導(dǎo)致服務(wù)器的崩潰,
四Java的解決方案
在java里,Servlet以及web容器,被設(shè)計(jì)用來解決CGI的問題,為WEB開發(fā)者創(chuàng)建一個(gè)健壯的服務(wù)器環(huán)境,
一、Servlet
a)Servlet是J2EE規(guī)范,
b)它是一個(gè)普通的JAVA類,集成于HttpServlet,用于處理動(dòng)態(tài)頁面的響應(yīng)
c)它是與平臺(tái)無關(guān)的JAVA類,能夠編譯成平臺(tái)中立的字節(jié)碼,從而被基于JAVA技術(shù)的WEB服務(wù)器動(dòng)態(tài)裝載和運(yùn)行
d)Servlet在服務(wù)器端得工作主要是執(zhí)行如下任務(wù)
i.讀取客戶端發(fā)送的顯示和隱式數(shù)據(jù)
ii.生成結(jié)果
iii.向客戶端發(fā)送顯示數(shù)據(jù)(文檔)和隱式數(shù)據(jù)(HTTP響應(yīng)數(shù)據(jù))
二、WEB容器
a)WEB容器主要是如Tomcat,Jboss,WebLogic,WebSphere,Oracle9iAS等,其中Tomcat是一種用的很廣泛的Web容器,它是一個(gè)開放源代碼的免費(fèi)的中間件產(chǎn)品b)WEB容器必須對(duì)Servlet支持以下幾點(diǎn)
i.通信支持
ii.生命周期管理
iii.多線程支持iv.JSP支持v.處理安全性
三、Servlet與WEB容器配合處理請(qǐng)求和響應(yīng)
a)與GUI類似,Servlet允許一個(gè)請(qǐng)求被一個(gè)程序處理.并且使用同樣的程序產(chǎn)生動(dòng)態(tài)的
響應(yīng),此外,Servlet特別定義了一個(gè)有效的生命周期,使得用單個(gè)進(jìn)程管理所有請(qǐng)求成為可能,它消除了CGI的多進(jìn)程缺陷,允許主進(jìn)程在多個(gè)Servlet和多個(gè)請(qǐng)求之間共享內(nèi)存資源.b)Servlet運(yùn)行在一個(gè)主進(jìn)程或者一個(gè)父進(jìn)程中,每個(gè)用戶發(fā)送請(qǐng)求信息到服務(wù)器,就會(huì)在Servlet進(jìn)程里創(chuàng)建一個(gè)線程,它們共享一個(gè)資源c)最后,WEB容器和Servlet對(duì)請(qǐng)求和響應(yīng)的處理如下
i.客戶端向WEB服務(wù)器發(fā)起一個(gè)HTTP請(qǐng)求
ii.HTTP請(qǐng)求被WEB服務(wù)器幾首,如果請(qǐng)求的是靜態(tài)頁面,則由WEB服務(wù)器負(fù)責(zé)
處理,如果請(qǐng)求的是JAVAWEB組件(Servlet或者JSP),則移交給WEB容器,iii.WEB容器根據(jù)Servlet的配置文件(web.xml),確定調(diào)用的具體Servlet類,并把request對(duì)象,response對(duì)象傳給它
iv.Servlet通過request對(duì)象知道客戶端的使用者是誰,客戶的請(qǐng)求信息是什么和其
他的一些信息
v.一旦Servlet完成了請(qǐng)求的處理,WEB容器就會(huì)刷新response,把控制權(quán)返回給
WEB服務(wù)器
五JavaWeb應(yīng)用程序的組成
1.配置文件---每個(gè)WEB應(yīng)用程序包括一個(gè)配置文件,WEB.XML2.靜態(tài)文件和JSP
3.類文件和包---WEB應(yīng)用程序裝載和管理自定義的JAVA代碼
4.網(wǎng)頁可以放在WEB應(yīng)用程序的根目錄下,根據(jù)動(dòng)態(tài)網(wǎng)頁或者靜態(tài)網(wǎng)頁放在不同的目
錄里
5.圖像一般會(huì)放在images子目錄中,不過這是習(xí)慣,不是必須的
6.Servlet類,JavaBean類---編譯為Class文件后是防在WEB-INF/classes目錄的7.Lib目錄用來包含應(yīng)用程序任何所需要的jar文件8.標(biāo)記描述其放在WEB-INF目錄下
9.Applet程序放在應(yīng)用的目錄下
10.WEB-INF目錄下存放web.xml部署描述文件器
剩下的WEB容器Tomcat主要是在實(shí)踐中應(yīng)用,在這里不做總結(jié)
擴(kuò)展閱讀:JavaWeb工作原理
淺析基于Java的Web服務(wù)器工作原理
一個(gè)Web服務(wù)器也被稱為HTTP服務(wù)器,它通過HTTP協(xié)議與客戶端通信。這個(gè)客戶端通常指的是Web瀏覽器。一個(gè)基于Java的Web服務(wù)器用到二個(gè)重要的類,java.net.Socket與java.net.ServerSocket,并通過HTTP消息通信。因此,本文從討論HTTP與這二個(gè)類開始,然后我將解釋一個(gè)與本文相關(guān)的簡(jiǎn)單的Web應(yīng)用。
TheHypertextTransferProtocol(HTTP)
HTTP是一種讓W(xué)eb服務(wù)器與瀏覽器(客戶端)通過Internet發(fā)送與接收數(shù)據(jù)的協(xié)議。它是一個(gè)請(qǐng)求、響應(yīng)協(xié)議--客戶端發(fā)出一個(gè)請(qǐng)求,服務(wù)器響應(yīng)這個(gè)請(qǐng)求。HTTP運(yùn)用可靠的TCP連接,通常用的TCP80端口。它的第一個(gè)版本是HTTP/0.9,然后被HTTP/1.0取代。當(dāng)前的版本是HTTP/1.1,由RFC2616(.pdf)定義。
本節(jié)主要對(duì)應(yīng)HTTP1.1,足夠使你充分理解由Web服務(wù)器程序發(fā)出的消息。如果你對(duì)更加詳細(xì)的知識(shí)有興趣,可以參考RFC2616。
在HTTP中,客戶端總是通過建立一個(gè)連接與發(fā)送一個(gè)HTTP請(qǐng)求來發(fā)起一個(gè)事務(wù)。服務(wù)器不能主動(dòng)去與客戶端聯(lián)系,也不能給客戶端發(fā)出一個(gè)回叫連接。客戶端與服務(wù)器端都可以提前中斷一個(gè)連接。例如,當(dāng)用一個(gè)瀏覽器下載一個(gè)文件時(shí),你可以通過點(diǎn)擊“停止”鍵來中斷文件的下載,關(guān)閉與服務(wù)器的HTTP連接。
HTTP請(qǐng)求
一個(gè)HTTP請(qǐng)求包含三個(gè)部分:
Method-URI-Protocol/Version方法-地址-版本
Requestheader請(qǐng)求頭
Entitybody請(qǐng)求實(shí)體
下面是一個(gè)HTTP請(qǐng)求實(shí)例:
POST/servlet/default.jspHTTP/1.1
Accept:text/plain;text/htmlAccept-Language:en-gb
Connection:Keep-Alive
Host:localhost
Referer:
User-Agent:Mozilla/4.0(compatible;MSIE4.01;Windows98)
Content-Length:33
Content-Type:application/x-www-form-urlencoded
Accept-Encoding:gzip,deflate
LastName=Franks&FirstName=Michael
TheMethod-URI-Protocol/Version在這個(gè)請(qǐng)求的第一行:
POST/servlet/default.jspHTTP/1.1
其中POST是請(qǐng)求的類型。每個(gè)客戶端HTTP請(qǐng)求可以是HTTP規(guī)范中指定的許多請(qǐng)求類型中的一種。HTTP1.1支持七種類型的請(qǐng)求,它們是GET,POST,HEAD,OPTIONS,PUT,DELETE,TRACE。其中GET與POST是Internet應(yīng)用中經(jīng)常用到的二種請(qǐng)求類型。
URI完整地指定了Internet資源。一個(gè)URI通常被解析為相對(duì)服務(wù)器的根目錄。這樣,它應(yīng)該總是以一個(gè)"/"前綴開始。一個(gè)URL實(shí)際上是URI的一種類型。
Version指的是該HTTP請(qǐng)求所用到的HTTP協(xié)議版本。
請(qǐng)求頭包含了客戶端環(huán)境與請(qǐng)求實(shí)體的一些有用的信息。例如它包含瀏覽器設(shè)定的語言、實(shí)體的長(zhǎng)度等等。每條請(qǐng)求頭用回車換行符(CRLF)分開。一個(gè)非常重要的空行分開了請(qǐng)求頭與實(shí)體,它標(biāo)志著實(shí)體內(nèi)容的開始。一些Internet開發(fā)書籍認(rèn)為這個(gè)CRLF空行是HTTP請(qǐng)求的第四個(gè)部分。
在上面的HTTP請(qǐng)求中,實(shí)體只是簡(jiǎn)單以下的一行:
LastName=Franks&FirstName=Michael
在一個(gè)典型的HTTP請(qǐng)求中,請(qǐng)求實(shí)體內(nèi)容會(huì)長(zhǎng)得多。
HTTP響應(yīng)
與請(qǐng)求相似,HTTP響應(yīng)也由三部分組成:
Protocol-Statuscode-Description協(xié)議狀態(tài)描述代碼
Responseheaders響應(yīng)頭
Entitybody響應(yīng)實(shí)體
以下是一個(gè)HTTP響應(yīng)的實(shí)例:
HTTP/1.1200OK
Server:Microsoft-IIS/4.0
Date:Mon,3Jan199813:13:33GMT
Content-Type:text/html
Last-Modified:Mon,11Jan199813:23:42GMT
Content-Length:1
WelcometoBrainySoftware
響應(yīng)頭的第一行類似請(qǐng)求頭的第一行,告訴你所用的協(xié)議是HTTP1.1,請(qǐng)求成功(200=success),以及沒有任何問題。
響應(yīng)頭類似請(qǐng)求頭也包含了一些有用的信息。響應(yīng)的實(shí)體響應(yīng)本身的HTML內(nèi)容。頭與實(shí)體之間由回車換行的空行(CRLF)分開。
Socket類
一個(gè)socket是一個(gè)網(wǎng)絡(luò)連接的端點(diǎn),它使得一個(gè)應(yīng)用可以從網(wǎng)絡(luò)讀與寫。在不同電腦上的二個(gè)應(yīng)用軟件能夠通過收發(fā)字節(jié)流而彼此通信。要發(fā)一個(gè)信息到另一個(gè)應(yīng)用程序,你需要知道它的IP地址,以及它的socket端口號(hào)。在Java中,一個(gè)socket用java.net.Socket來實(shí)現(xiàn)。
要?jiǎng)?chuàng)建一個(gè)socket,你可以用Socket類中幾個(gè)構(gòu)建方法中的一個(gè)。其中一個(gè)接受主機(jī)名與端口號(hào)作為參數(shù):
newSocket("yahoo.com",80);
一旦你成功地創(chuàng)建了一個(gè)Socket類的實(shí)例,你就可以用它去發(fā)送與接收字節(jié)流了。要發(fā)送字節(jié)流,你需要呼叫Socket類的getOutputStream方法來得到一個(gè)java.io.OutputSteam對(duì)象。要發(fā)送文本到遠(yuǎn)程的程序,你通常需要從返回的OutputStream創(chuàng)建一個(gè)java.io.PrintWriter對(duì)象。要從連接的另一端接收字節(jié)流,你需要呼叫Socket類的getInputStream方法,它返回一個(gè)java.io.InputStream對(duì)象。
以下代碼創(chuàng)建一個(gè)可以與本地HTTP服務(wù)器通信的socket(127.0.0.1表示一個(gè)本地的主機(jī)),發(fā)送一個(gè)HTTP請(qǐng)求,并接收從服務(wù)器的響應(yīng)。它還創(chuàng)建一個(gè)StringBuffer對(duì)象來接受響應(yīng),并打印到控制臺(tái)。
Socketsocket=newSocket("127.0.0.1","8080");OutputStreamos=socket.getOutputStream();
booleanautoflush=true;
PrintWriterout=newPrintWriter(socket.getOutputStream(),
autoflush);
BufferedReaderin=newBufferedReader(
newInputStreamReader(socket.getInputStream()));
//sendanHTTPrequesttothewebserver
out.println("GET/index.jspHTTP/1.1");
out.println("Host:localhost:8080");
out.println("Connection:Close");
out.println();
//readtheresponse
booleanloop=true;
StringBuffersb=newStringBuffer(8096);
while(loop){
if(in.ready()){inti=0;
while(i!=-1){
i=in.read();
sb.append((char)i);}
loop=false;}
Thread.currentThread().sleep(50);}
//displaytheresponsetotheoutconsole
System.out.println(sb.toString());
socket.close();
注意要從web服務(wù)器得到正確的響應(yīng),你必須要發(fā)送用HTTP協(xié)議編譯了的HTTP請(qǐng)求。如果你看了上面的HTTP部分,你應(yīng)該能夠理解上面代碼中的HTTP請(qǐng)求。
編者注:這篇文章節(jié)選自budi自己出版的書<Tomcat內(nèi)幕>。你可以在他的網(wǎng)站得到更多的相關(guān)資料。
基于Java的Web服務(wù)器工作原理2
作者:fajaven譯發(fā)文時(shí)間:201*.09.1217:00:38
ServerSocket類
Socket類描述的是“客戶端”socket,當(dāng)你需要?jiǎng)?chuàng)建與遠(yuǎn)程服務(wù)程序連接時(shí)需要用到它。如果你想實(shí)現(xiàn)一個(gè)服務(wù)程序,如HTTP服務(wù)器或者FTP服務(wù)器,則需要另外不同的方法。這是因?yàn)槟愕姆⻊?wù)器必須隨時(shí)服務(wù),它不知道什么時(shí)候會(huì)有一個(gè)客戶端程序需要連接它。
因?yàn)檫@個(gè)目的,你需要用到j(luò)ava.net.ServerSocket這個(gè)類,它是服務(wù)器端socket的一個(gè)實(shí)現(xiàn)。服務(wù)器端socket等待來自客戶端的連接請(qǐng)求。一旦它收到一個(gè)連接請(qǐng)求,它創(chuàng)建一個(gè)socket實(shí)例來與客戶端進(jìn)行通信。
要?jiǎng)?chuàng)建服務(wù)器端socket,需要用到ServerSocket類提供的四個(gè)構(gòu)建方法中的一個(gè)。你需要指定服務(wù)器端socket偵聽的IP地址與端口號(hào)。比較典型地,這個(gè)IP地址可以是127.0.0.1,意思是該服務(wù)器端socket偵聽的是本地機(jī)器。服務(wù)器端socket偵聽的IP地址指的是綁定地址。服務(wù)器端socket另一個(gè)重要的屬性是隊(duì)列長(zhǎng)度,即它拒絕請(qǐng)求前所接受的最大請(qǐng)求排隊(duì)長(zhǎng)度。
ServerSocket類的構(gòu)建方法之一如下:
publicServerSocket(intport,intbackLog,InetAddressbindingAddress);
對(duì)于這個(gè)構(gòu)建方法,綁定地址必須是java.net.InetAddress類的實(shí)例。創(chuàng)建一個(gè)InetAddress類的對(duì)象的簡(jiǎn)單方法是呼叫其靜態(tài)方法getByName,傳遞一個(gè)包含主機(jī)名的字符串。
InetAddress.getByName("127.0.0.1");
以下行的代碼創(chuàng)建了一個(gè)服務(wù)器端socket,它偵聽本地機(jī)器的8080端口,限制隊(duì)列長(zhǎng)度為1。
newServerSocket(8080,1,InetAddress.getByName("127.0.0.1"));
一旦有了一個(gè)ServerSocket實(shí)例,就可以通過呼叫其accept方法來讓它等待進(jìn)來的鏈接請(qǐng)求。這個(gè)方法只有當(dāng)接收到請(qǐng)求時(shí)才返回,它返回的是Socket類的實(shí)例。這個(gè)Socket對(duì)象就可以用來從客戶端應(yīng)用程序發(fā)送與接收字節(jié)流,正如上節(jié)據(jù)說的那樣。實(shí)際上,accept方法是本文例子中用到的唯一方法。
應(yīng)用實(shí)例
我們的web服務(wù)器程序是ex01.pyrmont包的一部分,它包含三個(gè)類:HttpServer;Request;Response。
整個(gè)程序的入口(靜態(tài)main方法)是HttpServer類。它創(chuàng)建一個(gè)HttpServer的實(shí)例,并呼叫其await方法。正如名字表達(dá)的,await在一個(gè)特定的端口等待HTTP請(qǐng)求,處理它們,并返回響應(yīng)給客戶端。它保持等待狀態(tài),直到收到停止命令。(用方法名await代替wait,是因?yàn)镾ystem中有一個(gè)重要的與線程相關(guān)的方法)
這個(gè)程序只從一個(gè)特定的目錄發(fā)送靜態(tài)資源,如HTML與圖像文件。它只支持沒有文件頭(如日期與cookie)的情況,F(xiàn)在我們將在如下的幾節(jié)中看一下這三個(gè)類。
HttpServer類
HttpServer實(shí)現(xiàn)了一個(gè)web服務(wù)器,它可以提供(serve)特定目錄及其子目錄下的靜態(tài)資源。這個(gè)特定的目錄由publicstaticfinalWEB_ROOT指定。
WEB_ROOT初始化如下:
publicstaticfinalStringWEB_ROOT=
System.getProperty("user.dir")+File.separator+"webroot";
代碼列表中包含了一具叫做webroot的目錄,里面有一些靜態(tài)的資源,你可以用來測(cè)試本應(yīng)用。你也可以看到一個(gè)servlet,在我的下一篇文章將會(huì)被用到:“Servlets容器是怎樣工作的”。
為了請(qǐng)求一個(gè)靜態(tài)的資源,在瀏覽器的地址欄輸入如是地址::port/staticResources
如果你從不同的機(jī)器上發(fā)送請(qǐng)求到運(yùn)行本應(yīng)用的機(jī)器,則machinename是運(yùn)行應(yīng)用機(jī)器的機(jī)器名或IP地址,port是8080,staticResources是被請(qǐng)求的文件名稱,它必須包含在WEB_ROOT目錄內(nèi)。
例如,如果你用同一臺(tái)電腦來測(cè)試這個(gè)應(yīng)用,你想要HttpServer發(fā)送index.html這個(gè)文件,用以下的地址::8080/index.html
要停止服務(wù),只需要從瀏覽器發(fā)送一個(gè)停止(shutdown)命令,即在瀏覽器的地址欄輸入host:port字段后,加上預(yù)先定義好的字符串。在我們的HttpServer類中,停止命令被定義為SHUTDOWN,一個(gè)staticfinal變量。
privatestaticfinalStringSHUTDOWN_COMMAND="/SHUTDOWN";
因此,要停止服務(wù),你可以這樣::8080/SHUTDOWN現(xiàn)在,讓我們看一下列表1.1中給出的await方法。代碼列表后面將對(duì)這段代碼做一些解釋。
Listing1.1.TheHttpServerclass"awaitmethod
publicvoidawait(){
ServerSocketserverSocket=null;
intport=8080;try{
serverSocket=newServerSocket(port,1,
InetAddress.getByName("127.0.0.1"));}
catch(IOExceptione){
e.printStackTrace();
System.exit(1);}
//Loopwaitingforarequest
while(!shutdown){
Socketsocket=null;
InputStreaminput=null;
OutputStreamoutput=null;try{
socket=serverSocket.accept();
input=socket.getInputStream();
output=socket.getOutputStream();
//createRequestobjectandparse
Requestrequest=newRequest(input);
request.parse();
//createResponseobject
Responseresponse=newResponse(output);
response.setRequest(request);
response.sendStaticResource();
//Closethesocket
socket.close();
//checkifthepreviousURIisashutdowncommand
shutdown=request.getUri().equals(SHUTDOWN_COMMAND);}
catch(Exceptione){
e.printStackTrace();
continue;}}}
await方法以創(chuàng)建一個(gè)ServerSocket實(shí)例開始,然后進(jìn)入一個(gè)while的循環(huán)。
serverSocket=newServerSocket(
port,1,InetAddress.getByName("127.0.0.1"));...
//Loopwaitingforarequest
while(!shutdown){...}
在while循環(huán)中的代碼,運(yùn)行到ServerSocket的accept方法即停止。這個(gè)方法只有在8080端口接收到HTTP請(qǐng)求才返回:
socket=serverSocket.accept();
收到請(qǐng)求后,await方法從accept方法返回的Socket實(shí)例中等到j(luò)ava.io.InputStream與java.io.OutputStream:
input=socket.getInputStream();
output=socket.getOutputStream();
然后await方法創(chuàng)建一個(gè)Request對(duì)象,呼叫它的parse方法來解析這個(gè)原始的HTTP請(qǐng)求:
//createRequestobjectandparse
Requestrequest=newRequest(input);
request.parse();
下一步,await方法創(chuàng)建一個(gè)Response對(duì)象并把Request對(duì)象設(shè)置給它,呼叫它的sendStaticResource方法:
//createResponseobject
Responseresponse=newResponse(output);
response.setRequest(request);
response.sendStaticResource();
最后,await方法關(guān)閉Socket,呼叫Request的getUri方法來檢查HTTP請(qǐng)求的地址是否是一個(gè)停止命令。如果是,則shutdown變量被設(shè)置為true,程序退出while循環(huán):
//Closethesocket
socket.close();
//checkifthepreviousURIisashutdowncommand
shutdown=request.getUri().equals(SHUTDOWN_COMMAND);
基于Java的Web服務(wù)器工作原理3
作者:fajaven發(fā)文時(shí)間:201*.09.1217:11:54
Request類
Request類對(duì)應(yīng)HTTP請(qǐng)求。創(chuàng)建這個(gè)類的實(shí)例,并傳給它從Socket獲得的InputStream對(duì)象,從而捕獲與客戶端的通信。呼叫InputStream對(duì)象的read方法中的一個(gè)就可以得到HTTP請(qǐng)求的原始數(shù)據(jù)。
Request類有二個(gè)public方法parse與getUri。parse方法解析HTTP請(qǐng)求的原始數(shù)據(jù)。它做的事情不多--唯一它使之有效的信息是HTTP請(qǐng)求的URI,這個(gè)通過呼叫私有方法parseUri來獲得。parseUri方法把URI作為一個(gè)變量。調(diào)用getUri方法可以得到HTTP請(qǐng)求的URI。
要明白parse與parseUri的工作原理,你需要知道HTTP請(qǐng)求的結(jié)構(gòu),由RFC2616定義。
一個(gè)HTTP請(qǐng)求包括三個(gè)部分:Requestline;Headers;Messagebody。
現(xiàn)在,我們只需要關(guān)注HTTP請(qǐng)求的第一部分--請(qǐng)求行。請(qǐng)求行以方法記號(hào)開始,接著是請(qǐng)求的URI與協(xié)議版本,以回車換行符結(jié)束。請(qǐng)求行的元素之間以空格分開。例如,一個(gè)用GET方法的index.html文件的請(qǐng)求行如下:
GET/index.htmlHTTP/1.1
parse方法從socket的InputStream傳遞給Request對(duì)象中讀取字節(jié)流,把這個(gè)字節(jié)數(shù)組存在緩沖里。然后,它把buffer字節(jié)數(shù)組里的字節(jié)放入叫做request的StringBuffer對(duì)象中,再把StringBuffer替換成String傳遞給parseUri方法。
parse方法的代碼如列表1.2
Listing1.2.TheRequestclass"parsemethod
publicvoidparse(){
//ReadasetofcharactersfromthesocketStringBufferrequest=newStringBuffer(2048);inti;
byte[]buffer=newbyte[2048];try{
i=input.read(buffer);}
catch(IOExceptione){
e.printStackTrace();i=-1;}
for(intj=0;jrequest.append((char)buffer[j]);}
System.out.print(request.toString());
uri=parseUri(request.toString());}
parseUri方法查找請(qǐng)求行的第一個(gè)與第二個(gè)空格,從而從請(qǐng)求行獲得了URI。列表1.3展示了parseUri方法的代碼。
Listing1.3.TheRequestclass"parseUrimethod
privateStringparseUri(StringrequestString){intindex1,index2;
index1=requestString.indexOf("");
if(index1!=-1){
index2=requestString.indexOf("",index1+1);
if(index2>index1)
returnrequestString.substring(index1+1,index2);}
returnnull;}
Response類
Response類描述HTTP響應(yīng)。它的構(gòu)建方法接受OutputStream對(duì)象,如下:
publicResponse(OutputStreamoutput){
this.output=output;}
Response對(duì)象通過傳遞從socket獲得的OutputStream對(duì)象到HttpServer類的await方法而創(chuàng)建。
Response類有二個(gè)公共方法setRequest與setStaticResource。setRequest用來傳遞Request對(duì)象到Response對(duì)象。它比較簡(jiǎn)單,代碼如列表1.4所示:
Listing1.4.TheResponseclass"setRequestmethod
publicvoidsetRequest(Requestrequest){
this.request=request;}
sendStaticResource方法用來發(fā)送靜態(tài)的資源,例如HTML文件。它的實(shí)現(xiàn)如列表1.5所示:
Listing1.5.TheResponseclass"sendStaticResourcemethod
publicvoidsendStaticResource()throwsIOException{
byte[]bytes=newbyte[BUFFER_SIZE];
FileInputStreamfis=null;try{
Filefile=newFile(HttpServer.WEB_ROOT,request.getUri());
if(file.exists()){
fis=newFileInputStream(file);
intch=fis.read(bytes,0,BUFFER_SIZE);
while(ch!=-1){
output.write(bytes,0,ch);
ch=fis.read(bytes,0,BUFFER_SIZE);}}else{
//filenotfound
StringerrorMessage="HTTP/1.1404FileNotFound\\r\\n"+
"Content-Type:text/html\\r\\n"+
"Content-Length:23\\r\\n"+"\\r\\n"+"
FileNotFound";
output.write(errorMessage.getBytes());}}
catch(Exceptione){
//thrownifcannotinstantiateaFileobject
System.out.println(e.toString());}finally{
if(fis!=null)
fis.close();}}
SendStaticResource方法非常簡(jiǎn)單。它首先通過傳遞父與子目錄到File類的構(gòu)建方法從而實(shí)例化java.io.File類。
FilefilenewFile(HttpServer.WEB_ROOT,request.getUri());
然后檢查這個(gè)文件是否存在。如果存在,則sendStaticResource方法傳遞File對(duì)象創(chuàng)建java.io.FileInputStream對(duì)象。然后調(diào)用FileInputStream的read方法,并把字節(jié)數(shù)組寫到OutputStream對(duì)象output。就這樣,靜態(tài)資源的內(nèi)容作為原始數(shù)據(jù)被發(fā)送到瀏覽器。
if(file.exists()){
fis=newFileInputStream(file);
intch=fis.read(bytes,0,BUFFER_SIZE);
while(ch!=-1){
output.write(bytes,0,ch);
ch=fis.read(bytes,0,BUFFER_SIZE);}}
如果文件不存在,sendStaticResource發(fā)送一個(gè)錯(cuò)誤信息到瀏覽器。
StringerrorMessage="HTTP/1.1404FileNotFound\\r\\n"+
"Content-Type:text/html\\r\\n"+
"Content-Length:23\\r\\n"+"\\r\\n"+"
FileNotFound";
output.write(errorMessage.getBytes());
編譯與運(yùn)行應(yīng)用程序
要編輯與運(yùn)行本文的應(yīng)用,首先你需要解壓源碼zip文件。直接解壓出來的目錄被稱為工作目錄,它有三個(gè)子目錄:src/,classes/,lib/。要編譯應(yīng)用,從工作目錄輸入如下命令:
javac-d.src/ex01/pyrmont/*.java
-d選項(xiàng)把結(jié)果寫到當(dāng)前目錄,而不是src/目錄。
要運(yùn)行應(yīng)用,在當(dāng)前工作目錄輸入如下命令:
javaex01.pyrmont.HttpServer
測(cè)試這個(gè)應(yīng)用,打開你的瀏覽器,在地址欄輸入如下地址::8080/index.html
你將在你的瀏覽器看到index.html顯示出來,如圖1所示。
圖1:web服務(wù)器的輸出顯示
在控制臺(tái),你看到如下的內(nèi)容:
GET/index.htmlHTTP/1.1
Accept:*/*Accept-Language:en-us
Accept-Encoding:gzip,deflate
User-Agent:Mozilla/4.0(compatible;MSIE4.01;Windows98)
Host:localhost:8080
Connection:Keep-Alive
GET/images/logo.gifHTTP/1.1
Accept:*/*
Referer::8080/index.html
Accept-Language:en-us
Accept-Encoding:gzip,deflate
User-Agent:Mozilla/4.0(compatible;MSIE4.01;Windows98)
Host:localhost:8080
Connection:Keep-Alive總結(jié)
在這篇文章中(分為三個(gè)部分),你看到了一個(gè)簡(jiǎn)單的web服務(wù)器的工作原理。本文相關(guān)的應(yīng)用只包括了三個(gè)類,功能是不全面的。然而,它仍不失為一個(gè)好的學(xué)習(xí)工具。
(責(zé)任編輯:張明燕)
Java的網(wǎng)絡(luò)編程:用Java實(shí)現(xiàn)Web服務(wù)器
作者:谷和啟發(fā)文時(shí)間:201*.12.2615:35:57
HTTP協(xié)議
超文本傳輸協(xié)議(HTTP)是位于TCP/IP協(xié)議的應(yīng)用層,是最廣為人知的協(xié)議,也是互連網(wǎng)中最核心的協(xié)議之一,同樣,HTTP也是基于C/S或B/S模型實(shí)現(xiàn)的。事實(shí)上,我們使用的瀏覽器如Netscape或IE是實(shí)現(xiàn)HTTP協(xié)議中的客戶端,而一些常用的Web服務(wù)器軟件如Apache、IIS和iPlanetWebServer等是實(shí)現(xiàn)HTTP協(xié)議中的服務(wù)器端。Web頁由服務(wù)端資源定位,傳輸?shù)綖g覽器,經(jīng)過瀏覽器的解釋后,被客戶所看到。
Web的工作基于客戶機(jī)/服務(wù)器計(jì)算模型,由Web瀏覽器(客戶機(jī))和Web服務(wù)器(服務(wù)器)構(gòu)成,兩者之間采用超文本傳送協(xié)議(HTTP)進(jìn)行通信。HTTP協(xié)議是Web瀏覽器和Web服務(wù)器之間的應(yīng)用層協(xié)議,是通用的、無狀態(tài)的、面向?qū)ο蟮膮f(xié)議。
一個(gè)完整的HTTP協(xié)議會(huì)話過程包括四個(gè)步驟:
◆連接,Web瀏覽器與Web服務(wù)器建立連接,打開一個(gè)稱為Socket(套接字)的虛擬文件,此文件的建立標(biāo)志著連接建立成功;
◆請(qǐng)求,Web瀏覽器通過Socket向Web服務(wù)器提交請(qǐng)求。HTTP的請(qǐng)求一般是GET或POST命令(POST用于FORM參數(shù)的傳遞);
◆應(yīng)答,Web瀏覽器提交請(qǐng)求后,通過HTTP協(xié)議傳送給Web服務(wù)器。Web服務(wù)器接到后,進(jìn)行事務(wù)處理,處理結(jié)果又通過HTTP傳回給Web瀏覽器,從而在Web瀏覽器上顯示出所請(qǐng)求的頁面;
◆關(guān)閉連接,應(yīng)答結(jié)束后Web瀏覽器與Web服務(wù)器必須斷開,以保證其它Web瀏覽器能夠與Web服務(wù)器建立連接。
Java實(shí)現(xiàn)Web服務(wù)器功能的程序設(shè)計(jì)
編程思路
根據(jù)上述HTTP協(xié)議的會(huì)話過程,本實(shí)例中實(shí)現(xiàn)了GET請(qǐng)求的Web服務(wù)器程序的方法,方法如下:
通過創(chuàng)建ServerSocket類對(duì)象,偵聽用戶指定的端口(為8080),等待并接受客戶機(jī)請(qǐng)求到端口。創(chuàng)建與Socket相關(guān)聯(lián)的輸入流和輸出流,然后讀取客戶機(jī)的請(qǐng)求信息。若請(qǐng)求類型是GET,則從請(qǐng)求信息中獲取所訪問的HTML文件名;如果HTML文件存在,則打開HTML文件,把HTTP頭信息和HTML文件內(nèi)容通過Socket傳回給Web瀏覽器,然后關(guān)閉文件,否則發(fā)送錯(cuò)誤信息給Web瀏覽器。最后關(guān)閉與相應(yīng)Web瀏覽器連接的Socket。
用Java編寫Web服務(wù)器httpServer.java文件的源代碼如下:
//httpServer.java
importjava.net.*;
importjava.io.*;
importjava.util.*;
importjava.lang.*;
publicclasshttpServer{
publicstaticvoidmain(Stringargs[]){intport;
ServerSocketserver_socket;
//讀取服務(wù)器端口號(hào)try{
port=Integer.parseInt(args[0]);}
catch(Exceptione){
port=8080;}try{
//監(jiān)聽服務(wù)器端口,等待連接請(qǐng)求
server_socket=newServerSocket(port);
System.out.println("httpServerrunningonport"+
server_socket.getLocalPort());
//顯示啟動(dòng)信息
while(true){
Socketsocket=server_socket.accept();
System.out.println("Newconnectionaccepted"+
socket.getInetAddress()+
":"+socket.getPort());
//創(chuàng)建分線程try{
httpRequestHandlerrequest=
newhttpRequestHandler(socket);
Threadthread=newThread(request);
//啟動(dòng)線程
thread.start();}
catch(Exceptione){
System.out.println;}}}
catch(IOExceptione){
System.out.println;}}}
classhttpRequestHandlerimplementsRunnable{
finalstaticStringCRLF="\\r\\n";
Socketsocket;
InputStreaminput;
OutputStreamoutput;
BufferedReaderbr;
//構(gòu)造方法
publichttpRequestHandler(Socketsocket)throwsException{
this.socket=socket;
this.input=socket.getInputStream();
this.output=socket.getOutputStream();
this.br=
newBufferedReader(newInputStreamReader(socket.getInputStream()));}
//實(shí)現(xiàn)Runnable接口的run()方法
publicvoidrun(){try{
processRequest();}
catch(Exceptione){
System.out.println;}}
privatevoidprocessRequest()throwsException{
while(true){
//讀取并顯示W(wǎng)eb瀏覽器提交的請(qǐng)求信息
StringheaderLine=br.readLine();
System.out.println("Theclientrequestis"+headerLine);
if(headerLine.equals(CRLF)||headerLine.equals(""))break;
StringTokenizers=newStringTokenizer(headerLine);
Stringtemp=s.nextToken();
if(temp.equals("GET")){
StringfileName=s.nextToken();
fileName="."+fileName;
//打開所請(qǐng)求的文件
FileInputStreamfis=null;
booleanfileExists=true;try{
fis=newFileInputStream(fileName);}
catch(FileNotFoundExceptione){
fileExists=false;}
//完成回應(yīng)消息
StringserverLine="Server:asimplejavahttpServer";
StringstatusLine=null;
StringcontentTypeLine=null;
StringentityBody=null;
StringcontentLengthLine="error";
if(fileExists){
statusLine="HTTP/1.0200OK"+CRLF;
contentTypeLine="Content-type:"+
contentType(fileName)+CRLF;
contentLengthLine="Content-Length:"
+(newInteger(fis.available())).toString()
+CRLF;}else{
statusLine="HTTP/1.0404NotFound"+CRLF;
contentTypeLine="text/html";
entityBody=""+""+
"404NotFound"+"
usage::port/"
+"fileName.html";}
//發(fā)送到服務(wù)器信息
output.write(statusLine.getBytes());
output.write(serverLine.getBytes());
output.write(contentTypeLine.getBytes());
output.write(contentLengthLine.getBytes());
output.write(CRLF.getBytes());//發(fā)送信息內(nèi)容
if(fileExists){
sendBytes(fis,output);
fis.close();}else{
output.write(entityBody.getBytes());}}}
//關(guān)閉套接字和流try{
output.close();
br.close();
socket.close();}
catch(Exceptione){}}
privatestaticvoidsendBytes(FileInputStreamfis,OutputStreamos)
throwsException{
//創(chuàng)建一個(gè)1Kbuffer
byte[]buffer=newbyte[1024];
intbytes=0;
//將文件輸出到套接字輸出流中
while((bytes=fis.read(buffer))!=-1){
os.write(buffer,0,bytes);}}
privatestaticStringcontentType(StringfileName){
if(fileName.endsWith(".htm")||fileName.endsWith(".html")){
return"text/html";}
return"fileName";}}
編程技巧說明◆主線程設(shè)計(jì)
主線程的設(shè)計(jì)就是在主線程httpServer類中實(shí)現(xiàn)了服務(wù)器端口的偵聽,服務(wù)器接受一個(gè)客戶端請(qǐng)求之后創(chuàng)建一個(gè)線程實(shí)例處理請(qǐng)求,代碼如下:
importjava.net.*;
importjava.io.*;
importjava.util.*;
importjava.lang.*;
publicclasshttpServer{
publicstaticvoidmain(Stringargs[]){port;
ServerSocketserver_socket;
//讀取服務(wù)器端口號(hào)try{
port=Integer.parseInt(args[0]);}
catch(Exceptione){
port=8080;}try{
//監(jiān)聽服務(wù)器端口,等待連接請(qǐng)求
server_socket=newServerSocket(port);
System.out.println("httpServerrunningonport"
+server_socket.getLocalPort());....................
◆連接處理分線程設(shè)計(jì)
在分線程httpRequestHandler類中實(shí)現(xiàn)了HTTP協(xié)議的處理,這個(gè)類實(shí)現(xiàn)了Runnable接口,代碼如下:
classhttpRequestHandlerimplementsRunnable{
finalstaticStringCRLF="\\r\\n";
Socketsocket;
InputStreaminput;
OutputStreamoutput;
BufferedReaderbr;
//構(gòu)造方法
publichttpRequestHandler(Socketsocket)throwsException{
this.socket=socket;
//得到輸入輸出流
this.input=socket.getInputStream();
this.output=socket.getOutputStream();
this.br=
newBufferedReader(newInputStreamReader(socket.getInputStream()));}
//實(shí)現(xiàn)Runnable接口的run()方法
publicvoidrun(){try{
processRequest();}
catch(Exceptione){
System.out.println;}}
◆構(gòu)建processRequest()方法來處理信息的接收和發(fā)送
作為實(shí)現(xiàn)Runnable接口的主要內(nèi)容,在run()方法中調(diào)用processRequest()方法來處理客戶請(qǐng)求內(nèi)容的接收和服務(wù)器返回信息的發(fā)送,代碼如下:
privatevoidprocessRequest()throwsException{
while(true){
//讀取并顯示W(wǎng)eb瀏覽器提交的請(qǐng)求信息
StringheaderLine=br.readLine();
System.out.println("Theclientrequestis"+headerLine);
if(headerLine.equals(CRLF)||headerLine.equals(""))break;
//根據(jù)請(qǐng)求字符串中的空格拆分客戶請(qǐng)求
StringTokenizers=newStringTokenizer(headerLine);
Stringtemp=s.nextToken();
if(temp.equals("GET")){
StringfileName=s.nextToken();
fileName="."+fileName;
.............
.............
在processRequest()方法中得到客戶端請(qǐng)求后,利用一個(gè)StringTokenizer類完成了字符串的拆分,這個(gè)類可以實(shí)現(xiàn)根據(jù)字符串中指定的分隔符(缺省為空格)將字符串拆分成為字串的功能。利用nextToken()方法依次得到這些字串;sendBytes()方法完成信息內(nèi)容的發(fā)送,contentType()方法用于判斷文件的類型。
顯示W(wǎng)eb頁面
顯示W(wǎng)eb頁面的index.html文件代碼如下:
*********歡迎你的到來!*********
這是一個(gè)用Java語言實(shí)現(xiàn)的Web服務(wù)器
--------------------------------------------------------------------------------
運(yùn)行實(shí)例
為了測(cè)試上述程序的正確性,將編譯后的httpServer.class、httpRequestHandler.class和上面的index.html文件置于網(wǎng)絡(luò)的某臺(tái)主機(jī)的同一目錄中。
首先運(yùn)行服務(wù)器程序javahttpServer8080,服務(wù)器程序運(yùn)行后顯示端口信息“httpServerruningonport8080”,然后在瀏覽器的地址欄中輸入:8080/index.html,就可以正確顯示網(wǎng)頁,同時(shí)在顯示“httpServerruningonport8080”窗口中服務(wù)器會(huì)出現(xiàn)一些信息。
友情提示:本文中關(guān)于《JavaWeb工作原理》給出的范例僅供您參考拓展思維使用,JavaWeb工作原理:該篇文章建議您自主創(chuàng)作。
來源:網(wǎng)絡(luò)整理 免責(zé)聲明:本文僅限學(xué)習(xí)分享,如產(chǎn)生版權(quán)問題,請(qǐng)聯(lián)系我們及時(shí)刪除。