23----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方upcaseNext=ch=='-';}returnnewString(chars);}publicStringget(Stringkey){return(String)super.get(fix(key));}publicvoidput(Stringkey,Stringval){super.put(fix(key),val);}}HttpResponse.javaHttpResponse類是所有與HTTP服務(wù)器應(yīng)答有關(guān)的事物的包裝程序。它被httpd類的代理部分使用。當(dāng)你向一個(gè)HTTP服務(wù)器發(fā)送一個(gè)請(qǐng)求時(shí),它以一個(gè)存儲(chǔ)在statusCode中的整數(shù)形式的代碼以及一個(gè)存儲(chǔ)在reasonPhrase中的文本應(yīng)答(這些變量名在正式的HTTP規(guī)范中規(guī)定)。這個(gè)單行的響應(yīng)后面跟隨著一個(gè)包含進(jìn)一步應(yīng)答信息的MIME頭。我們用以前解釋過(guò)的MimeHeader對(duì)象來(lái)解析這個(gè)字符串。MimeHeader對(duì)象存儲(chǔ)在HttpResponse類的mh變量中。這些變量不是私有的,所以httpd可以直接使用它們。構(gòu)造函數(shù)如果用一個(gè)字符串參數(shù)創(chuàng)建一個(gè)HttpResponse類對(duì)象,它被用來(lái)作為一個(gè)HTTP服務(wù)器的原始響應(yīng),并傳向下面描述的parse(?)來(lái)初始化對(duì)象。你還可以傳入一個(gè)預(yù)計(jì)算的狀態(tài)碼,原因語(yǔ)句以及MIME標(biāo)頭。parse(?)parse(?)方法獲得從HTTP服務(wù)器上讀取的原始數(shù)據(jù),從第一行解析出statusCode和reasonPhrase,然后在剩下的行外部創(chuàng)建一個(gè)MimeHeader。toString(?)toString(?)方法是parse(?)的逆方法。它獲取HttpResponse對(duì)象的當(dāng)前值并返回一個(gè)字符串,HTTP客戶希望從服務(wù)器讀回該字符串。代碼下面是HttpResponse的源代碼:importjava.io.*;/**HttpResponse*ParseareturnmessageandMIMEheaderfromaserver.*HTTP/1.0302Found=redirection,checkLocationforwhere.*HTTP/1.0200OK=filedatacomesaftermimeheader.*/classHttpResponse{intstatusCode;//Status-CodeinspecStringreasonPhrase;//Reason-PhraseinspecMimeHeadermh;staticStringCRLF="\r
24";voidparse(Stringrequest){intfsp=request.indexOf('');
25----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方intnsp=request.indexOf('',fsp+1);inteol=request.indexOf('
26');Stringprotocol=request.substring(0,fsp);statusCode=Integer.parseInt(request.substring(fsp+1,nsp));reasonPhrase=request.substring(nsp+1,eol);Stringraw_mime_header=request.substring(eol+1);mh=newMimeHeader(raw_mime_header);}HttpResponse(Stringrequest){parse(request);}HttpResponse(intcode,Stringreason,MimeHeaderm){statusCode=code;reasonPhrase=reason;mh=m;}publicStringtoString(){return"HTTP/1.0"+statusCode+""+reasonPhrase+CRLF+mh+CRLF;}}UrlCacheEntry.java為在服務(wù)器上保存文檔的內(nèi)容,必須在用于找回文檔的URL和文檔自身描述之間建立聯(lián)系。一個(gè)文檔由它的MimeHeader和原始數(shù)據(jù)描述。例如一副圖像可以被一個(gè)Content-Type:image/gif樣式的MimeHeader描述,而原始圖像數(shù)據(jù)就是一個(gè)字節(jié)數(shù)組。同樣,一個(gè)網(wǎng)頁(yè)在它的MimeHeader中有Content-Type:text/html關(guān)鍵字/值對(duì),而原始數(shù)據(jù)就是HTML頁(yè)的內(nèi)容。再次申明,實(shí)例變量不是私有的,所以httpd可以自由的訪問(wèn)它們。構(gòu)造函數(shù)UrlCacheEntry對(duì)象的構(gòu)造函數(shù)需要用URL作為關(guān)鍵字以及一個(gè)與之相關(guān)的MimeHeader。如果MimeHeader內(nèi)部有一個(gè)名為Content-Length成員(大多數(shù)情況下如此),數(shù)據(jù)區(qū)域被預(yù)先分配足夠大的空間來(lái)保存它的內(nèi)容。append(?)append(?)方法用來(lái)給UrlCacheEntry對(duì)象增添數(shù)據(jù)的。它不是一個(gè)簡(jiǎn)單的setData(?)方法,原因是數(shù)據(jù)可能流經(jīng)網(wǎng)絡(luò)且需要在一定時(shí)間被存儲(chǔ)成塊。append(?)方法處理三種情形。第一種,數(shù)據(jù)緩沖區(qū)根本沒(méi)有分配。第二種情形,數(shù)據(jù)緩沖區(qū)對(duì)于引入的數(shù)據(jù)來(lái)說(shuō)太小,所以它被重新分配。最后一種情況,引入的數(shù)據(jù)正好可以插入緩沖區(qū)。在任何時(shí)候,length成員變量保存數(shù)據(jù)緩沖區(qū)當(dāng)前的有效大小值。代碼下面是UrlCacheEntry的源代碼:classUrlCacheEntry{Stringurl;MimeHeadermh;bytedata[];intlength=
27----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方0;publicUrlCacheEntry(Stringu,MimeHeaderm){url=u;mh=m;Stringcl=mh.get("Content-Length");if(cl!=null){data=newbyte[Integer.parseInt(cl)];}}voidappend(byted[],intn){if(data==null){data=newbyte[n];System.arraycopy(d,0,data,0,n);length=n;}elseif(length+n>data.length){byteold[]=data;data=newbyte[old.length+n];System.arraycopy(old,0,data,0,old.length);System.arraycopy(d,0,data,old.length,n);}else{System.arraycopy(d,0,data,length,n);length+=n;}}}LogMessage.javaLogMessage是一個(gè)簡(jiǎn)單的接口,它只定義了一個(gè)方法log(?),該方法只有一個(gè)String型參數(shù)。它用來(lái)抽象從httpd獲得消息的輸出。在應(yīng)用程序條件下,該方法用來(lái)打印標(biāo)準(zhǔn)應(yīng)用程序起始處控制臺(tái)的輸出。在小應(yīng)用程序情況下,數(shù)據(jù)被送到一個(gè)視窗文本緩沖區(qū)。代碼下面是LogMessage的源程序:interfaceLogMessage{publicvoidlog(Stringmsg);}httpd.java這真是一個(gè)具有很多功能的大類。我們將一個(gè)方法一個(gè)方法的講解它。構(gòu)造函數(shù)存在5個(gè)主要的實(shí)例變量:port,docRoot,log,cache和stopFlag,它們都是私有的。其中的三個(gè)可以由httpd的獨(dú)立構(gòu)造函數(shù)設(shè)置,顯示如下:httpd(intp,Stringdr,LogMessagelm)它初始化監(jiān)聽(tīng)端口,初始化檢索文件的目錄以及初始化發(fā)送消息的接口。第四個(gè)實(shí)例變量cache,是在RAM中保存所有文件的Hashtable,它是在對(duì)象創(chuàng)建時(shí)被初始化的。stopFlag控制程序的執(zhí)行。
28----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方靜態(tài)部分該類中有幾個(gè)重要的靜態(tài)變量。MIME標(biāo)頭中的“Server”域報(bào)告的版本在變量version中被發(fā)現(xiàn)。接著定義了一些常量:HTML文件的MIME類型,mime_text_html;MIME的結(jié)束順序,CRLF;代替原始目錄請(qǐng)求返回的HTML文件名,indexfile以及在輸入/輸出中用到的數(shù)據(jù)緩沖區(qū)的大小,buffer_size。然后mt定義了一系列文件擴(kuò)展名和這些文件相應(yīng)的MIME類型。typesHashtable在下一個(gè)塊中被靜態(tài)初始化,以用來(lái)包含作為可選關(guān)鍵字和值的數(shù)組mt。接著可以用fnameToMimeType(?)方法來(lái)返回傳入的每個(gè)filename的合適的MIME類型。如果filename不含mt表中的任何一個(gè)擴(kuò)展名,該方法返回defaultExt或“text/plain.”。統(tǒng)計(jì)計(jì)算器下面,我們聲明另外5個(gè)實(shí)例變量。它們是沒(méi)有private修飾符,所以一個(gè)外部監(jiān)控器可以檢查這些值并以圖形形式顯示它們(我們將在后面演示)。這些變量表示了我們的Web服務(wù)器所用的統(tǒng)計(jì)資料。點(diǎn)擊數(shù)和提供的字節(jié)的原始數(shù)目被存儲(chǔ)在hits_served和bytes_served中。通常存儲(chǔ)在高速緩存中的文件和字節(jié)數(shù)被存放在files_in_cache和bytes_in_cache中。最后,我們把成功在高速緩存外部提供服務(wù)的點(diǎn)擊數(shù)目存放在hits_to_cache中。toBytes(?)接著,我們有一個(gè)方便的程序,toBytes(?)。該程序把它的字符串轉(zhuǎn)變成一個(gè)字節(jié)數(shù)組。這是十分必要的,因?yàn)镴ava的String對(duì)象是以統(tǒng)一編碼的字符形式存儲(chǔ)的,而Internet協(xié)議中的混合語(yǔ)例如HTTP是老式的8位ASCII碼。makeMimeHeader(?)MakeMimeHeader()方法是另一個(gè)方便的方法,它用來(lái)創(chuàng)建由一些關(guān)鍵字值填充的MimeHeader對(duì)象。該方法返回的MimeHeader在Date成員中含有當(dāng)前時(shí)間和時(shí)期,在Server成員中有服務(wù)器的名稱和版本,Content-Type成員中有type參數(shù),Content-Length成員中有l(wèi)ength參數(shù)。error(?)error(?)方法用來(lái)格式化HTML頁(yè)并返回提出不能完成請(qǐng)求的Web客戶。第一個(gè)參數(shù)code,是返回的出錯(cuò)代碼。一般它在400到499之間。我們的服務(wù)器返回404和405錯(cuò)誤。它用HttpResponse類和適當(dāng)?shù)腗imeHeader來(lái)封裝返回的代碼。該方法返回字符串表示是與HTML頁(yè)有關(guān)的響應(yīng)。該頁(yè)包括易于人讀的錯(cuò)誤代碼信息msg和導(dǎo)致錯(cuò)誤的url請(qǐng)求。getRawRequest(?)GetRawRequest()方法是很簡(jiǎn)單的。它從流讀取數(shù)據(jù)直到它獲得兩個(gè)連續(xù)的換行符。它忽略回車符號(hào)并且只尋找換行符。一旦它已經(jīng)發(fā)現(xiàn)了連續(xù)的兩個(gè)換行符,它使字節(jié)數(shù)組轉(zhuǎn)向一個(gè)String對(duì)象并返回該對(duì)象。如果輸入流在結(jié)束之前沒(méi)有生成兩個(gè)連續(xù)的換行符,它將返回null。這說(shuō)明了HTTP服務(wù)器和客戶的消息是怎樣被格式化的。它們以狀態(tài)的一行開(kāi)始然后立即跟著一個(gè)MIME頭。MIME頭的結(jié)尾被兩個(gè)換行符從剩余的內(nèi)容中分離。logEntry(?)logEntry(?)方法用來(lái)報(bào)告標(biāo)準(zhǔn)格式下的HTTP服務(wù)器的每個(gè)點(diǎn)擊數(shù)。該方法生成的格式也許看起來(lái)有一點(diǎn)奇怪,但是它和HTTP日志文件的當(dāng)前標(biāo)準(zhǔn)相匹配。該方法有若干個(gè)用來(lái)格式化每個(gè)日志項(xiàng)的日期戳的輔助變量和方法。months數(shù)組用來(lái)把月份轉(zhuǎn)換成字符串。當(dāng)host變量接受一個(gè)給定主機(jī)的連接時(shí)它由主HTTP循環(huán)設(shè)置。fmt02d(?)方法把0到9的整數(shù)格式化成兩位的,第一位為零的數(shù),然后結(jié)果字符串通過(guò)LogMessage接口變量log傳輸。
29----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方writeString(?)另一個(gè)方便的方法writeString(?),用來(lái)隱藏字符到字節(jié)數(shù)組的轉(zhuǎn)變,以使它可以被寫入流。writeUCE(?)writeUCE(?)方法占取一個(gè)OutputStream和一個(gè)UrlCacheEntry。它從高速緩存項(xiàng)提取信息,以便給網(wǎng)絡(luò)客戶傳送消息。消息中包含適當(dāng)?shù)捻憫?yīng)代碼,MIME標(biāo)頭和內(nèi)容。serveFromCache(?)這個(gè)布爾方法試圖在高速緩存中發(fā)現(xiàn)一個(gè)特殊的URL。如果成功,緩存項(xiàng)的內(nèi)容被寫給客戶,hits_to_cache變量遞增,調(diào)用者被返回true。否則,它只返回false。loadFile(?)該方法占用了一個(gè)InputStream、與之相應(yīng)的url以及該URL的MimeHeader。用存儲(chǔ)在MimeHeader的信息創(chuàng)建一個(gè)新的UrlCacheEntry。輸入流在buffer_size字節(jié)塊中被讀取并傳入U(xiǎn)rlCacheEntry。結(jié)果的UrlCacheEntry存儲(chǔ)在高速緩存中。files_in_cache和bytes_in_cache變量更新,UrlCacheEntry返回調(diào)用者。readFile(?)readFile(?)方法就loadFile(?)方法來(lái)說(shuō)看起來(lái)有些多余,實(shí)際不然。該方法嚴(yán)格的從本地文件系統(tǒng)讀取文件。在本地文件系統(tǒng)中,loadFile(?)方法用來(lái)與各種類型的流交流。如果File對(duì)象f存在,將會(huì)為它創(chuàng)建一個(gè)InputStream類。文件的大小是決定了的,MIME類型來(lái)自文件名。當(dāng)loadFile(?)被調(diào)用來(lái)做實(shí)際的讀取和緩存高速工作時(shí),有兩個(gè)變量用來(lái)創(chuàng)建合適的MimeHeader。writeDiskCache(?)writeDiskCache(?)方法占用一個(gè)UrlCacheEntry對(duì)象并且把它持久的寫入本地磁盤。它從URL中建立一個(gè)目錄名,確保用與系統(tǒng)有關(guān)的separatorChar代替斜線(/)字符。然后它調(diào)用mkdirs(?)方法來(lái)保證這個(gè)URL的本地磁盤路徑存在。最后,它打開(kāi)一個(gè)FileOutputStream,向它寫入所有的數(shù)據(jù)然后關(guān)閉它。handleProxy(?)HandleProxy()程序是該服務(wù)器的兩個(gè)主要模式之一?;A(chǔ)思想是:如果你把你的瀏覽器設(shè)置成把該服務(wù)器當(dāng)成代理服務(wù)器,則要傳給它的請(qǐng)求將包括完整的URL,URL中常規(guī)GET方法可刪除“http://”和主機(jī)名部分。我們僅把完整的URL拆碎,尋找“://”序列,其次是斜線(/),然后是使用非標(biāo)準(zhǔn)端口號(hào)的服務(wù)器的另一個(gè)冒號(hào)(:)。一旦我們發(fā)現(xiàn)了這些字符,我們就可以知道已經(jīng)所需要的主機(jī)和端口號(hào)以及我們需要獲取的URL。然后我們可以試圖從RAM高速緩存中轉(zhuǎn)載一個(gè)先前保存過(guò)的文檔版本。如果失敗,我們可以試圖從文件系統(tǒng)裝載它到RAM高速緩存并且再嘗試從RAM高速緩存裝載它。如果此舉失敗,那么事情變得很有趣,因?yàn)槲覀儽仨殢倪h(yuǎn)程站點(diǎn)讀取該文件。為此,我們打開(kāi)一個(gè)遠(yuǎn)程站點(diǎn)和端口的套接字。我們發(fā)送一個(gè)GET請(qǐng)求,要求傳給我們的URL。無(wú)論我們從遠(yuǎn)程站點(diǎn)獲得什么響應(yīng)標(biāo)頭信息,我們把它傳給客戶。如果代碼是200,對(duì)于成功的文件傳輸,我們還把確認(rèn)數(shù)據(jù)流讀到一個(gè)新的UrlCacheEntry類并且把它寫入客戶套接字。然后,我們調(diào)用writeDiskCache(?)來(lái)保存?zhèn)鬏斀Y(jié)果到本地磁盤。我們記錄傳輸日志,關(guān)閉套接字,然后返回。handleGet(?)當(dāng)http后臺(tái)進(jìn)程像一個(gè)普通的Web服務(wù)器一樣工作時(shí),handleGet(?)被調(diào)用。它有一個(gè)服務(wù)于文件的本地磁盤文件根目錄。handleGet(?)的參數(shù)告訴它向何處寫結(jié)果,何處訪問(wèn)URL以及何處請(qǐng)求網(wǎng)絡(luò)瀏覽器的MimeHeader
30----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方。這個(gè)MIME標(biāo)頭將包括用戶代理字符串和其他有用的屬性。開(kāi)始,我們?cè)噲D在RAM高速緩存外為URL提供服務(wù)。如果此舉失敗,為尋找URL,我們順序訪問(wèn)文件系統(tǒng)。如果文件不存在或不可讀,我們向Web客戶報(bào)告一個(gè)錯(cuò)誤。否則,我們就用readFile(?)方法獲得文件的內(nèi)容并把它們輸入到高速緩存。然后調(diào)用writeUCE(?)方法以用來(lái)傳輸文件內(nèi)容到客戶套接字。doRequest(?)每次連接服務(wù)器時(shí)都會(huì)調(diào)用一次doRequest(?)方法。它解析請(qǐng)求字符串和引入的MIME標(biāo)頭。它在請(qǐng)求字符串中是否存在“://”的基礎(chǔ)上判定是調(diào)用handleProxy(?)還是handleGet(?)方法。如果不用GET,而使用任何其他的方法例如HEAD或POST,該程序向客戶返回一個(gè)405錯(cuò)誤。注意如果stopFlag是true時(shí)HTTP請(qǐng)求被忽略。run(?)run(?)方法在服務(wù)器線程啟動(dòng)時(shí)被調(diào)用。它在指定端口生成一個(gè)新的ServerSocket,在服務(wù)器套接字處進(jìn)入一個(gè)調(diào)用accept(?)的無(wú)限循環(huán),把結(jié)果Socket傳給doRequest(?)作檢查。start(?)ANDstop(?)存在兩個(gè)用來(lái)啟動(dòng)和終止服務(wù)器過(guò)程的方法。這些方法設(shè)置stopFlag的值。main(?)你可以在命令行用main(?)方法來(lái)運(yùn)行應(yīng)用程序。它為服務(wù)器自身設(shè)置LogMessage參數(shù),然后為log(?)提供簡(jiǎn)單的控制臺(tái)輸出執(zhí)行。代碼下面是httpd的源代碼:importjava.net.*;importjava.io.*;importjava.text.*;importjava.util.*;classhttpdimplementsRunnable,LogMessage{privateintport;privateStringdocRoot;privateLogMessagelog;privateHashtablecache=newHashtable();privatebooleanstopFlag;privatestaticStringversion="1.0";privatestaticStringmime_text_html="text/html";privatestaticStringCRLF="\r
31";privatestaticStringindexfile="index.html";privatestaticintbuffer_size=8192;staticStringmt[]={//mappingfromfileexttoMime-Type"txt","text/plain","html",mime_text_html,"htm","text/html","gif","image/gif","jpg","image/jpg","jpeg","image/jpg","class","application/octet-stream"};staticStringdefaultExt="txt";staticHashtabletypes=newHashtable();static{for(inti=0;i32----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方}staticStringfnameToMimeType(Stringfilename){if(filename.endsWith("/"))//specialforindexfiles.returnmime_text_html;intdot=filename.lastIndexOf('.');Stringext=(dot>0)?filename.substring(dot+1):defaultExt;Stringret=(String)types.get(ext);returnret!=null?ret:(String)types.get(defaultExt);}inthits_served=0;intbytes_served=0;intfiles_in_cache=0;intbytes_in_cache=0;inthits_to_cache=0;privatefinalbytetoBytes(Strings)[]{byteb[]=s.getBytes();returnb;}privateMimeHeadermakeMimeHeader(Stringtype,intlength){MimeHeadermh=newMimeHeader();DatecurDate=newDate();TimeZonegmtTz=TimeZone.getTimeZone("GMT");SimpleDateFormatsdf=newSimpleDateFormat("ddMMMyyyyhh:mm:sszzz");sdf.setTimeZone(gmtTz);mh.put("Date",sdf.format(curDate));mh.put("Server","JavaCompleteReference/"+version);mh.put("Content-Type",type);if(length>=0)mh.put("Content-Length",String.valueOf(length));returnmh;}privateStringerror(intcode,Stringmsg,Stringurl){Stringhtml_page="
"+CRLF+""+code+""+msg+"
"+CRLF;if(url!=null)html_page+="ErrorwhenfetchingURL:"+url+CRLF;html_page+=""+CRLF;MimeHeadermh=makeMimeHeader(mime_text_html,html_page.length());HttpResponsehr=newHttpResponse(code,msg,mh);logEntry("GET",url,code,0);returnhr+html_page;}//Read'in'untilyougettwo
33'sinarow.//ReturnuptothatpointasaString.//Discardall\r's.
34----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方privateStringgetRawRequest(InputStreamin)throwsIOException{bytebuf[]=newbyte[buffer_size];intpos=0;intc;while((c=in.read())!=-1){switch(c){case'\r':break;case'
35':if(buf[pos-1]==c){returnnewString(buf,0,pos);}default:buf[pos++]=(byte)c;}}returnnull;}staticStringmonths[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};privateStringhost;//fmt02disthesameasC'sprintf("%02d",i)privatefinalStringfmt02d(inti){if(i<0){i=-i;return((i<9)?"-0":"-")+i;}else{return((i<9)?"0":"")+i;}}privatevoidlogEntry(Stringcmd,Stringurl,intcode,intsize){Calendarcalendar=Calendar.getInstance();inttzmin=calendar.get(Calendar.ZONE_OFFSET)/(60*1000);inttzhour=tzmin/60;tzmin-=tzhour*60;log.log(host+"--["+fmt02d(calendar.get(Calendar.DATE))+"/"+months[calendar.get(Calendar.MONTH)]+"/"+calendar.get(Calendar.YEAR)+":"+fmt02d(calendar.get(Calendar.HOUR))+":"+fmt02d(calendar.get(Calendar.MINUTE))+":"+fmt02d(calendar.get(Calendar.SECOND))+""+fmt02d(tzhour)+fmt02d(tzmin)+"]\""+cmd+""+url+"HTTP/1.0\""+code+""+size+"
36");
37----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方hits_served++;bytes_served+=size;}privatevoidwriteString(OutputStreamout,Strings)throwsIOException{out.write(toBytes(s));}privatevoidwriteUCE(OutputStreamout,UrlCacheEntryuce)throwsIOException{HttpResponsehr=newHttpResponse(200,"OK",uce.mh);writeString(out,hr.toString());out.write(uce.data,0,uce.length);logEntry("GET",uce.url,200,uce.length);}privatebooleanserveFromCache(OutputStreamout,Stringurl)throwsIOException{UrlCacheEntryuce;if((uce=(UrlCacheEntry)cache.get(url))!=null){writeUCE(out,uce);hits_to_cache++;returntrue;}returnfalse;}privateUrlCacheEntryloadFile(InputStreamin,Stringurl,MimeHeadermh)throwsIOException{UrlCacheEntryuce;bytefile_buf[]=newbyte[buffer_size];uce=newUrlCacheEntry(url,mh);intsize=0;intn;while((n=in.read(file_buf))>=0){uce.append(file_buf,n);size+=n;}in.close();cache.put(url,uce);files_in_cache++;bytes_in_cache+=uce.length;returnuce;}privateUrlCacheEntryreadFile(Filef,Stringurl)throwsIOException{if(!f.exists())returnnull;
38----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方InputStreamin=newFileInputStream(f);intfile_length=in.available();Stringmime_type=fnameToMimeType(url);MimeHeadermh=makeMimeHeader(mime_type,file_length);UrlCacheEntryuce=loadFile(in,url,mh);returnuce;}privatevoidwriteDiskCache(UrlCacheEntryuce)throwsIOException{Stringpath=docRoot+uce.url;Stringdir=path.substring(0,path.lastIndexOf("/"));dir.replace('/',File.separatorChar);newFile(dir).mkdirs();FileOutputStreamout=newFileOutputStream(path);out.write(uce.data,0,uce.length);out.close();}//Aclientasksusforaurlthatlookslikethis://http://the.internet.site/the/url//wegogetitfromthesiteandreturnit...privatevoidhandleProxy(OutputStreamout,Stringurl,MimeHeaderinmh){try{intstart=url.indexOf("://")+3;intpath=url.indexOf('/',start);Stringsite=url.substring(start,path).toLowerCase();intport=80;Stringserver_url=url.substring(path);intcolon=site.indexOf(':');if(colon>0){port=Integer.parseInt(site.substring(colon+1));site=site.substring(0,colon);}url="/cache/"+site+((port!=80)?(":"+port):"")+server_url;if(url.endsWith("/"))url+=indexfile;if(!serveFromCache(out,url)){if(readFile(newFile(docRoot+url),url)!=null){serveFromCache(out,url);return;}//Ifwehaven'talreadycachedthispage,openasocket//tothesite'sportandsendaGETcommandtoit.//Wemodifytheuser-agenttoaddourselves..."via".Socketserver=newSocket(site,port);InputStreamserver_in=server.getInputStream();
39----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方OutputStreamserver_out=server.getOutputStream();inmh.put("User-Agent",inmh.get("User-Agent")+"viaJavaCompleteReferenceProxy/"+version);Stringreq="GET"+server_url+"HTTP/1.0"+CRLF+inmh+CRLF;writeString(server_out,req);Stringraw_request=getRawRequest(server_in);HttpResponseserver_response=newHttpResponse(raw_request);writeString(out,server_response.toString());if(server_response.statusCode==200){UrlCacheEntryuce=loadFile(server_in,url,server_response.mh);out.write(uce.data,0,uce.length);writeDiskCache(uce);logEntry("GET",site+server_url,200,uce.length);}server_out.close();server.close();}}catch(IOExceptione){log.log("Exception:"+e);}}privatevoidhandleGet(OutputStreamout,Stringurl,MimeHeaderinmh){bytefile_buf[]=newbyte[buffer_size];Stringfilename=docRoot+url+(url.endsWith("/")?indexfile:"");try{if(!serveFromCache(out,url)){Filef=newFile(filename);if(!f.exists()){writeString(out,error(404,"NotFound",filename));return;}if(!f.canRead()){writeString(out,error(404,"PermissionDenied",filename));return;}UrlCacheEntryuce=readFile(f,url);writeUCE(out,uce);}}catch(IOExceptione){log.log("Exception:"+e);}}privatevoiddoRequest(Sockets)throwsIOException{if(stopFlag)return;InputStreamin=s.getInputStream();
40----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方OutputStreamout=s.getOutputStream();Stringrequest=getRawRequest(in);intfsp=request.indexOf('');intnsp=request.indexOf('',fsp+1);inteol=request.indexOf('
41');Stringmethod=request.substring(0,fsp);Stringurl=request.substring(fsp+1,nsp);Stringraw_mime_header=request.substring(eol+1);MimeHeaderinmh=newMimeHeader(raw_mime_header);request=request.substring(0,eol);if(method.equalsIgnoreCase("get")){if(url.indexOf("://")>=0){handleProxy(out,url,inmh);}else{handleGet(out,url,inmh);}}else{writeString(out,error(405,"MethodNotAllowed",method));}in.close();out.close();}publicvoidrun(){try{ServerSocketacceptSocket;acceptSocket=newServerSocket(port);while(true){Sockets=acceptSocket.accept();host=s.getInetAddress().getHostName();doRequest(s);s.close();}}catch(IOExceptione){log.log("acceptloopIOException:"+e+"
42");}catch(Exceptione){log.log("Exception:"+e);}}privateThreadt;publicsynchronizedvoidstart(){stopFlag=false;if(t==null){t=newThread(this);t.start();}}publicsynchronizedvoidstop(){
43----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方stopFlag=true;log.log("Stoppedat"+newDate()+"
44");}publichttpd(intp,Stringdr,LogMessagelm){port=p;docRoot=dr;log=lm;}//Thismainandlogmethodallowhttpdtoberunfromtheconsole.publicstaticvoidmain(Stringargs[]){httpdh=newhttpd(80,"c:\\www",null);h.log=h;h.start();try{Thread.currentThread().join();}catch(InterruptedExceptione){};}publicvoidlog(Stringm){System.out.print(m);}}HTTP.java這里是一個(gè)給HTTP服務(wù)器一個(gè)“前面板”功能的applet類。該applet有兩個(gè)可以用來(lái)配置服務(wù)器的參數(shù):port和docroot。這是一個(gè)很簡(jiǎn)單的小應(yīng)用程序。它創(chuàng)建了一個(gè)httpd實(shí)例,把它作為L(zhǎng)ogMessage接口傳入自己。然后創(chuàng)建了兩個(gè)面板。一個(gè)面板在頂部有一個(gè)簡(jiǎn)單的標(biāo)簽,在中部是一個(gè)TextArea文本區(qū)以顯示LogMessage;另一個(gè)面板在底部有兩個(gè)按鈕及一個(gè)標(biāo)簽。小應(yīng)用程序的start(?)和stop(?)方法調(diào)用httpd相應(yīng)的方法。標(biāo)注有“Start”和“Stop”的按鈕調(diào)用httpd中它們相應(yīng)的方法。在任何時(shí)候只要一條消息被記錄,右下角的Label對(duì)象就更新,這樣它包含httpd最新的統(tǒng)計(jì)數(shù)據(jù)。importjava.util.*;importjava.applet.*;importjava.awt.*;importjava.awt.event.*;publicclassHTTPextendsAppletimplementsLogMessage,ActionListener{privateintm_port=80;privateStringm_docroot="c:\\www";privatehttpdm_httpd;privateTextAream_log;privateLabelstatus;privatefinalStringPARAM_port="port";privatefinalStringPARAM_docroot="docroot";
45----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方publicHTTP(){}publicvoidinit(){setBackground(Color.white);Stringparam;//port:Portnumbertolistenonparam=getParameter(PARAM_port);if(param!=null)m_port=Integer.parseInt(param);//docroot:webdocumentrootparam=getParameter(PARAM_docroot);if(param!=null)m_docroot=param;setLayout(newBorderLayout());Labellab=newLabel("JavaHTTPD");lab.setFont(newFont("SansSerif",Font.BOLD,18));add("North",lab);m_log=newTextArea("",24,80);add("Center",m_log);Panelp=newPanel();p.setLayout(newFlowLayout(FlowLayout.CENTER,1,1));add("South",p);Buttonbstart=newButton("Start");bstart.addActionListener(this);p.add(bstart);Buttonbstop=newButton("Stop");bstop.addActionListener(this);p.add(bstop);status=newLabel("raw");status.setForeground(Color.green);status.setFont(newFont("SansSerif",Font.BOLD,10));p.add(status);m_httpd=newhttpd(m_port,m_docroot,this);}publicvoiddestroy(){stop();}publicvoidpaint(Graphicsg){}publicvoidstart(){m_httpd.start();status.setText("Running");clear_log("Logstartedon"+newDate()+"
46");
47----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方}publicvoidstop(){m_httpd.stop();status.setText("Stopped");}publicvoidactionPerformed(ActionEventae){Stringlabel=ae.getActionCommand();if(label.equals("Start")){start();}else{stop();}}publicvoidclear_log(Stringmsg){m_log.setText(msg+"
48");}publicvoidlog(Stringmsg){m_log.append(msg);status.setText(m_httpd.hits_served+"hits("+(m_httpd.bytes_served/1024)+"K),"+m_httpd.files_in_cache+"cachedfiles("+(m_httpd.bytes_in_cache/1024)+"K),"+m_httpd.hits_to_cache+"cachedhits");status.setSize(status.getPreferredSize());}}注意:httpd.java和HTTP.java文件中,代碼是在假定文件根部是“c:\www”的基礎(chǔ)上建立的。你可能需要改變配置的值。因?yàn)檫@個(gè)小應(yīng)用程序?qū)懭胍粋€(gè)日志文件,它只能在被信任的條件下工作。例如,一個(gè)小應(yīng)用程序在它可以獲得用戶CLASSPATH時(shí)被信任。18.9數(shù)據(jù)報(bào)對(duì)于你的大多數(shù)網(wǎng)絡(luò)需求,你會(huì)對(duì)TCP/IP型網(wǎng)絡(luò)很滿意。它提供了有序的、可預(yù)測(cè)和可靠的信息包數(shù)據(jù)流。但是,這樣做的代價(jià)也很大。TCP包含很多在擁擠的網(wǎng)絡(luò)中處理?yè)砣刂频膹?fù)雜算法以及信息丟失的悲觀的預(yù)測(cè)。這導(dǎo)致了一個(gè)效率很差的傳輸數(shù)據(jù)方式。數(shù)據(jù)報(bào)是一種可選的替換方法。數(shù)據(jù)報(bào)(Datagrams)是在機(jī)器間傳遞的信息包,它有些像從一個(gè)訓(xùn)練有素但是很盲目的捕手投出一記有力的傳球給三壘。一旦數(shù)據(jù)報(bào)被釋放給它們預(yù)定的目標(biāo),不保證它們一定到達(dá)目的地,甚至不保證一定存在數(shù)據(jù)的接收者。同樣,數(shù)據(jù)報(bào)被接受時(shí),不保證它在傳輸過(guò)程不受損壞,不保證發(fā)送它的機(jī)器仍在那兒等待響應(yīng)。Java通過(guò)兩個(gè)類實(shí)現(xiàn)UDP協(xié)議頂層的數(shù)據(jù)報(bào):DatagramPacket對(duì)象是數(shù)據(jù)容器,
49----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方DatagramSocket是用來(lái)發(fā)送和接受DatagramPackets的機(jī)制。18.9.1DatagramPacketDatagramPackets可以用四個(gè)構(gòu)造函數(shù)中的一個(gè)創(chuàng)建。第一個(gè)構(gòu)造函數(shù)指定了一個(gè)接收數(shù)據(jù)的緩沖區(qū)和信息包的容量大小。它通過(guò)DatagramSocket接收數(shù)據(jù)。第二種形式允許你在存儲(chǔ)數(shù)據(jù)的緩沖區(qū)中指定一個(gè)偏移量。第三種形式指定了一個(gè)用于DatagramSocket決定信息包將被送往何處的目標(biāo)地址和端口。第四種形式從數(shù)據(jù)中指定的偏移量位置開(kāi)始傳輸數(shù)據(jù)包。想象前兩種形式是建立在“盒內(nèi)”的,后兩種形式形成了填塞物,并為一個(gè)信封注明了地址。下面是四個(gè)構(gòu)造函數(shù):DatagramPacket(bytedata[],intsize)DatagramPacket(bytedata[],intoffset,intsize)DatagramPacket(bytedata[],intsize,InetAddressipAddress,intport)DatagramPacket(bytedata[],intoffset,intsize,InetAddressipAddress,intport)存在幾種方法可獲取DatagramPacket內(nèi)部狀態(tài)。它們對(duì)信息包的目標(biāo)地址和端口號(hào)以及原始數(shù)據(jù)和數(shù)據(jù)長(zhǎng)度有完全的使用權(quán),下面是它們的概述:InetAddressgetAddress(?)返回目標(biāo)文件InetAddress,一般用于發(fā)送。IntgetPort(?)返回端口號(hào)。byte[]getData(?)返回包含在數(shù)據(jù)包中的字節(jié)數(shù)組數(shù)據(jù)。多用于在接收數(shù)據(jù)之后從數(shù)據(jù)包來(lái)檢索數(shù)據(jù)。IntgetLength(?)返回包含在將從getData(?)方法返回的字節(jié)數(shù)組中有效數(shù)據(jù)長(zhǎng)度。通常它與整個(gè)字節(jié)數(shù)組長(zhǎng)度不等。18.9.2數(shù)據(jù)報(bào)服務(wù)器和客戶下面的例子實(shí)現(xiàn)了一個(gè)非常簡(jiǎn)單的聯(lián)網(wǎng)通信的客戶和服務(wù)器。消息被寫入服務(wù)器的窗口并通過(guò)網(wǎng)絡(luò)寫入客戶端,在此它們被顯示。//DemonstrateDatagrams.importjava.net.*;classWriteServer{publicstaticintserverPort=666;publicstaticintclientPort=999;publicstaticintbuffer_size=1024;publicstaticDatagramSocketds;publicstaticbytebuffer[]=newbyte[buffer_size];publicstaticvoidTheServer()throwsException{intpos=0;while(true){intc=System.in.read();switch(c){
50----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方case-1:System.out.println("ServerQuits.");return;case'\r':break;case'
51':ds.send(newDatagramPacket(buffer,pos,InetAddress.getLocalHost(),clientPort));pos=0;break;default:buffer[pos++]=(byte)c;}}}publicstaticvoidTheClient()throwsException{while(true){DatagramPacketp=newDatagramPacket(buffer,buffer.length);ds.receive(p);System.out.println(newString(p.getData(),0,p.getLength()));}}publicstaticvoidmain(Stringargs[])throwsException{if(args.length==1){ds=newDatagramSocket(serverPort);TheServer();}else{ds=newDatagramSocket(clientPort);TheClient();}}}該程序被DatagramSocket構(gòu)造函數(shù)限制在本地機(jī)的兩個(gè)端口間運(yùn)行。試著運(yùn)行該程序,在一個(gè)窗口中鍵入javaWriteServer這是在客戶端的。然后運(yùn)行javaWriteServer1這是在服務(wù)器端的。在服務(wù)器窗口中打印的任何東西在接受回車之后將被送到客戶窗口。注意:該例題需要你的計(jì)算機(jī)與Internet相連。
52----------專業(yè)最好文檔,專業(yè)為你服務(wù),急你所急,供你所需-------------文檔下載最佳的地方18.10網(wǎng)絡(luò)價(jià)值給你5個(gè)類InetAddress,Socket,ServerSocket,DatagramSocket和DatagramPacket,你可以編寫現(xiàn)有的任何Internet協(xié)議。它們同樣為Internet連接提供功能強(qiáng)大的低級(jí)控制。Java的網(wǎng)絡(luò)包也完美地反映了Internet的狀態(tài)。