




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
Java網(wǎng)路通信程式的設(shè)計(jì)
11.1處理URL內(nèi)容URL(UniformResourceLocator)是Internet的關(guān)鍵部分,它提供了人和機(jī)器的導(dǎo)航,其功能是指向電腦裏的資源,即定位。URL可以分成三個(gè)部分:通信協(xié)議、電腦地址和文件。URL常見的通信協(xié)議有三種:http,ftp和file。所謂通信協(xié)議,就是客戶端電腦與伺服器端電腦在網(wǎng)路上通信的方法。
有時(shí)候在地址後面還要指定使用哪一個(gè)端口(Port),例如:80/index.html。如果URL沒有指定使用哪一個(gè)端口,則會(huì)根據(jù)通信協(xié)議使用默認(rèn)的端口。一般地,http協(xié)議默認(rèn)端口為80,ftp協(xié)議默認(rèn)端口為21。
包中包含兩個(gè)專門用於URL的關(guān)鍵類,即URL和URLConnection。URL和URLConnection類封裝了檢索遠(yuǎn)程站點(diǎn)資訊的操作,因而大大地降低了這些操作的複雜性。下麵幾節(jié)將介紹這兩個(gè)類。11.1.1URL類的基本方法
URL類提供的最基本的網(wǎng)路功能是以流的形式讀取URL所指的的數(shù)據(jù)。URL類的實(shí)例可以用表示URL的文本串來建立,以表示URL所指的數(shù)據(jù)。構(gòu)造一個(gè)URL類實(shí)例的最簡(jiǎn)單方法是為URL構(gòu)造方法賦予一個(gè)字串:
URLurl=newURL(/index.html);
這被稱為“絕對(duì)”URL,因?yàn)橘x予的字串指定了從協(xié)議到資源名的全部?jī)?nèi)容。另一種URL類的構(gòu)造方法是構(gòu)造一個(gè)“相對(duì)”URL: URLdata=newURL(url,"data/data.html");
這種構(gòu)造方法指定了位於url的data子目錄中的data.html檔,它的絕對(duì)地址應(yīng)該是/data/data.html。這兩種構(gòu)造方法都可以指定一個(gè)URL,如果指定的URL是錯(cuò)誤的,構(gòu)造方法會(huì)拋出一個(gè)運(yùn)行時(shí)錯(cuò)誤:MalformedURLException,這個(gè)Excetion通知用戶構(gòu)造了一個(gè)形式錯(cuò)誤的URL。
注:URL類既支持http協(xié)議,也支持ftp和file協(xié)議。如果URL文本有錯(cuò)或者Java平臺(tái)不支持其協(xié)議部分,則這個(gè)構(gòu)造函數(shù)拋出一個(gè)MalformedURLException,該Exception是java.io.IOException的子類,指出給定的是不合法的URL。通常應(yīng)通過try-catch塊處理或聲明讓調(diào)用方法傳遞這個(gè)異常。URL類常用的構(gòu)造函數(shù)有下列三種:●URL(Stringspec)throwsMalformedURLException
創(chuàng)建一個(gè)由spec指定的URL類的實(shí)例?!馯RL(Stringspec,Stringhost,intport,Stringfile)throwsMalformedURLException
創(chuàng)建一個(gè)URL類的實(shí)例,分別指定其通信協(xié)議(protocal)、電腦地址(host)、連接端口(port)和文件(file)。如果port值是-1,則表示使用默認(rèn)端口。●URL(Stringprotocal,Stringhost,Stringfile)throwsMalformedURLException功能同上,但沒有指定端口,即使用默認(rèn)的端口。URL類的一些主要方法如下:●publicStringgetFile():返回URL中的檔部分。●publicStringgetHost():返回URL中的電腦地址部分。●publicintgetPort():返回URL中所使用的端口。●publicStringgetProtocal():返回URL中通信協(xié)議的部分。下麵是一個(gè)使用URL類及其方法的例子。例11.1URLDemo.javaimportgenesis.*;import.*;
publicclassURLDemo{ publicstaticvoidmain(Stringargs[]) { try {//創(chuàng)建一個(gè)指向首頁的URL類的實(shí)例
URLurl=newURL("/index.html"); Transcript.println("Protocol:"+url.getProtocol()); Transcript.println("Host:"+url.getHost()); Transcript.println("Port:"+url.getPort()); Transcript.println("File:"+url.getFile()); } catch(MalformedURLExceptione) { Transcript.println("錯(cuò)誤的URL!");} }}
這個(gè)例子很簡(jiǎn)單,因?yàn)闆]有指定URL的連接端口,所以顯示值為-1。實(shí)際連接時(shí),會(huì)根據(jù)通信協(xié)議而決定使用哪一個(gè)端口。它的運(yùn)行結(jié)果如圖11.1所示。
圖11.111.1.2用URL類實(shí)現(xiàn)頁面的訪問一旦構(gòu)造了URL,就可以用URL類中的openStream()方法讀取URL描述的數(shù)據(jù)。openStream()打開一個(gè)到URL類指定資源的連接,並返回一個(gè)InputStream對(duì)象。利用這個(gè)對(duì)象,可以方便的讀取資源的內(nèi)容,也可以鏈接到其他類型的輸入流和讀取器上。我們來看一個(gè)讀取頁面內(nèi)容的例子。例11.2GetPage.javaimportgenesis.*;importjava.io.*;import.*;publicclassGetPage{ publicstaticvoidmain(Stringargs[]) { try { URLurl=newURL("/index.html"); InputStreamin=url.openStream(); BufferedReaderreader= newBufferedReader(newInputStreamReader(in));//打開index.hmtl檔為寫做準(zhǔn)備
FileWriterfw=newFileWriter("index.html"); PrintWriterpw=newPrintWriter(fw); Stringline; //逐行讀入頁面內(nèi)容
while((line=reader.readLine())!=null) { //將讀入的行保存到index.html檔
pw.println(line); //將讀入的行顯示在窗口中
Transcript.println(line);}
reader.close(); pw.close(); fw.close(); } catch(IOExceptione) { Transcript.println(e.getMessage()); } }}程式的運(yùn)行結(jié)果如圖11.2所示。
圖11.2
上面的例子通過讀取的首頁index.html,展示了怎樣通過URL訪問Web頁面的內(nèi)容。圖11.2窗口中顯示的就是index.html頁面所包含的內(nèi)容(即它的源碼)。該例中將讀入的內(nèi)容保存在當(dāng)前目錄下,名為index.html的檔中,用流覽器打開這個(gè)檔,可以看到如同訪問的頁面。但是,無法看到頁面中的圖片,因?yàn)榇蜷_的輸入流只讀入了頁面的內(nèi)容,並沒有將圖片鏈接也讀入進(jìn)來。打開index.html檔將看到如圖11.3所示的頁面。圖11.311.1.3用URLConnection類實(shí)現(xiàn)頁面的訪問我們已經(jīng)知道了如何通過URL類訪問URL資源,但如果想瞭解關(guān)於這個(gè)資源的更多資訊,就需要使用URLConnection類。URLConnection類提供了訪問網(wǎng)路資源時(shí)更多、更好的控制方法。使用URLConnection類來訪問Web頁面的步驟如下:
(1)調(diào)用URL類的openConnection()方法得到一個(gè)URLConnection類的實(shí)例:
URLConnectionconn=url.openConnection();(2)調(diào)用以下方法,設(shè)置所有相關(guān)屬性:①setAllowUserInteraction()②setDoInput()③setDoOutput()④setIfModifiedSince()⑤setUseCaches()⑥setRequestProperty()(3)調(diào)用connect()方法連接遠(yuǎn)程資源:conn.connect();connect()方法除了創(chuàng)建一個(gè)連接指定伺服器的套接字連接外,還可以查詢伺服器以獲取相應(yīng)頭資訊(headerinformation)。(4)連接伺服器以後,使用getHeaderFieldKey()和getHeaderField()方法來枚舉出頭資訊的所有域。此外,也可以使用如下的方法來查詢標(biāo)準(zhǔn)域的內(nèi)容:
①getContentEncoding()②getContentLength()③getContentType()④getData()⑤getExpiration()⑥getLastModified()(5)使用getInputStream()方法訪問資源數(shù)據(jù)。用getInputStream()方法將返回一個(gè)輸入流,此輸入流和URL類的openStream()方法返回的輸入流是相同的。下麵我們將詳細(xì)的介紹其中的一些方法。在用於連接伺服器前設(shè)置連接屬性(第(2)步)的幾個(gè)方法中,setDoInput()和setDoOutput()這兩個(gè)方法最為重要。缺省時(shí),連接伺服器後將產(chǎn)生一個(gè)用於讀取伺服器數(shù)據(jù)的輸入流,但不會(huì)產(chǎn)生用於向伺服器寫出數(shù)據(jù)的輸出流。如果需要用到一個(gè)輸出流(例如,用於向CGIForm發(fā)送數(shù)據(jù)),就必須調(diào)用
conn.setDoOutput(true);
setIfModifiedSince()方法用於告訴連接:只需要在給出的日期之後被修改過的數(shù)據(jù)。
setUseCaches()和setAllowUserInteraction()方法只用於Applet。setUseCaches()方法指示流覽器先檢查它的高速緩存區(qū),這樣可以優(yōu)化訪問,如果設(shè)置其值為false,將不使用流覽器的緩存,其默認(rèn)值為true。setAllowUserInteraction()允許Applet彈出一個(gè)查詢用戶名和密碼的對(duì)話框。這些設(shè)置在Applet外部不起作用。
setRequestProperty()方法設(shè)置一個(gè)名字/值對(duì),用於說明某一特定的協(xié)議。例如,想訪問一個(gè)口令保護(hù)的網(wǎng)頁,必須進(jìn)行如下設(shè)置:Stringinput=username+":"+password;
conn.setRequestProperty("Authorization","Basic"+input);
注:用ftp協(xié)議訪問一個(gè)有口令保護(hù)的檔時(shí),將使用和上面完全不同的方法,只需要?jiǎng)?chuàng)建一個(gè)如下形式的URL:
ftp:://username:password@ftp.ftpserver.com/pub/file.txt
當(dāng)調(diào)用了connect()方法後,就可以查尋回應(yīng)頭資訊。我們通過下麵的例子來介紹如何獲得頭資訊的所有域和值以及頁面內(nèi)容。例11.3GetHeaderField.javaimportgenesis.*;import.*;importjava.io.*;
publicclassGetHeaderField{ publicstaticvoidmain(Stringargs[]) { try{
URLurl=newURL("/index.html"); //得到一個(gè)URLConnection對(duì)象
URLConnectionconn=url.openConnection(); //連接伺服器
conn.connect(); inti=1; Stringkey=""; Stringvalue="";//逐一讀出指定URL中的所有域
while((key=conn.getHeaderFieldKey(i))
!=null) { //讀出對(duì)應(yīng)域的值
value=conn.getHeaderField(i); Transcript.println(key+":"+value); i++; } //獲取一個(gè)輸入流
InputStreamin=conn.getInputStream();BufferedReaderreader= newBufferedReader(newInputStreamReader(in));
//打開index.hmtl檔為寫做準(zhǔn)備
FileWriterfw=newFileWriter("index.html"); PrintWriterpw=newPrintWriter(fw);
Stringline=""; //逐行讀入頁面內(nèi)容
while((line=reader.readLine())!=null){ //保存到index.html檔中
pw.println(line); }
reader.close(); pw.close(); fw.close(); } catch(IOExceptione){
Transcript.println(e.getMessage()); } }}程式的運(yùn)行結(jié)果如圖11.4所示。圖11.4
以上輸出結(jié)果展示了一個(gè)典型的HTTP請(qǐng)求的回應(yīng)頭所包含的一組域。getHeaderFieldKey()和getHeaderField()方法分別返回回應(yīng)頭中指定位置的功能變數(shù)名稱和值。指定值基數(shù)為1,當(dāng)指定值為零或大於頭中域總數(shù)時(shí),將返回null串。由於URLConnection類中沒有提供獲得相應(yīng)頭中域總數(shù)的方法,所以需重複調(diào)用getHeaderFieldKey()方法,直到返回null串來確定頭中域的總數(shù)。
程式中同時(shí)演示了如何通過URLConnection類獲取頁面內(nèi)容。調(diào)用URLConnection類的方法getInputStream()將返回一個(gè)InputStream類的實(shí)例,這個(gè)對(duì)象和調(diào)用URL類中openStream()返回的InputStream對(duì)象是相同的。利用這個(gè)對(duì)象,我們可以建立一個(gè)獲取頁面內(nèi)容的輸入流。運(yùn)行例子後,當(dāng)前目錄將會(huì)生成一個(gè)index.html檔,通過流覽器打開這個(gè)檔,可以看到,結(jié)果和前面通過URL訪問頁面內(nèi)容所得到的結(jié)果相同。此外,為了便於查詢最常見的回應(yīng)頭域的值,Java庫提供了六個(gè)更為方便的方法直接訪問這些域,如表11.1所示。表11.1直接獲取指定域值的方法域名方法返回類型DatagetDatalongExpiresgetExpirationlongLast-ModifiedgetLastModifiedlongContent-LengthgetContentLengthintContent-TypegetContenttypeStringContent-EncodinggetContentEncodingString11.1.4與CGI的溝通在Java技術(shù)出現(xiàn)之前,用CGI(CommonGatewayInterface)來提供相互交換網(wǎng)絡(luò)數(shù)據(jù)的方法就已廣泛使用。把資訊從網(wǎng)路流覽器發(fā)送給相應(yīng)的網(wǎng)路伺服器,例如使用電子郵件服務(wù),通常需要填寫用戶名和提交密碼。傳統(tǒng)的HTML語言提供了Form標(biāo)記發(fā)送格式數(shù)據(jù)。一個(gè)典型的HTMLForm描述如例11.4。例11.4form.html<html><head></head><body><formmethod="GET"action="/cgi-bin/login.exe"><p>Name:<inputtype="text"name="name"value=""size="40"></p><p>EmailAddress:<inputtype="text"name="email"value=""size="40"></p><p><inputtype="submit"name="submit"></p></form></body></html>
通過在流覽器上打開輸入以上代碼的html檔,我們可以看到一個(gè)如圖11.5所示的頁面。
圖11.5
在Internet上訪問郵件服務(wù)時(shí),用戶需要填寫一個(gè)類似上圖的表單。當(dāng)用戶點(diǎn)擊提交按鍵後,流覽器將會(huì)發(fā)送這些填寫的數(shù)據(jù)到action屬性指定的伺服器中的CGI程式。CGI程式負(fù)責(zé)處理這些數(shù)據(jù),然後生成一個(gè)HTML頁面,並送回流覽器。對(duì)於郵件服務(wù),這個(gè)新的回應(yīng)頁通常就是我們登陸後的頁面。(有關(guān)CGI程式和HTMLForm的詳細(xì)內(nèi)容,本章將不再繼續(xù)討論,如需瞭解可參考有關(guān)書籍)
除了處理傳統(tǒng)的非互動(dòng)式類型外,URLConnection類也提供了用於CGI或Servlet連接的功能。和HTML表單頁一樣,Java程式可以向伺服器發(fā)出一個(gè)CGI請(qǐng)求,這個(gè)請(qǐng)求既可以設(shè)為GET,也可以設(shè)為POST。此外,也可在程式中攔截CGI程式的輸出,所以通過Java程式可以實(shí)現(xiàn)和CGI程式交互資訊的全部過程。
通過GET方法向CGI程式發(fā)送資訊時(shí),只需將參數(shù)置於URL的末尾,並用“?”分隔。URL的基本格式為
http://host/script?parameters
當(dāng)包含兩個(gè)以上的參數(shù)時(shí),用一個(gè)“&”分開每個(gè)參數(shù),並對(duì)參數(shù)做以下的編碼處理:用“+”代替所有的空格,用%加兩位十六進(jìn)制數(shù)代替所有非字母的字元(包括漢字)。例如,假設(shè)一個(gè)參數(shù)為“C++Language”,它將被編碼為“C%2b%2b+Language”,其中2b(十進(jìn)位數(shù)43)就是ASCII碼的“+”字元。這種編碼方法避免了空格干擾,並可以解釋其他字元,這種編碼方式稱為URL編碼。GET方法非常方便,但是有一個(gè)大的限制,就是流覽器一般會(huì)限制GET請(qǐng)求的URL串的字元個(gè)數(shù)。而POST的方法則不同,使用POST請(qǐng)求的時(shí)候,不把參數(shù)放在URL的末端,而是採用建立一個(gè)到伺服器的輸出流的方式。通過得到一個(gè)來自URLConnection的輸出流,並把參數(shù)名/值對(duì)逐個(gè)寫入到輸出流,就可以實(shí)現(xiàn)POST方法的請(qǐng)求。使用POST方法時(shí),仍需用URL編碼方式處理這些輸入的參數(shù),並用“&”將參數(shù)分開。
下麵我們?nèi)匀煌ㄟ^一個(gè)例子來詳細(xì)的瞭解GET和POST方法與CGI通信的過程以及它們的不同之處。例11.5CGITest.javaimportgenesis.*;importjava.io.*;import.*;importjava.util.*;
publicclassCGITest{URLurl=null; URLConnectionconn=null; FileWriterfw=null; PrintWriterpw=null; publicstaticvoidmain(String[]args) { CGITesttest=newCGITest(); ArrayListparam=newArrayList(); if(args[0].equals("GET")||args[0].equals("get")){ //GET方法所需要的參數(shù)
param.add("p"); param.add(args[1]); param.add("u"); param.add("B"); //GET方法通過Yahoo提供的搜索引擎搜索給定的關(guān)鍵字
test.doGet("/search",param); }if(args[0].equals("POST")||args[0].equals("post")) { //POST方法所需要的參數(shù)
param.clear(); param.add("user"); param.add(args[1]); param.add("pass"); param.add(args[2]); param.add("type");
param.add("0"); param.add("verifycookie"); param.add("y"); //POST方法登陸163郵箱
test.doPost("http://bjweb.163.net/cgi/163/login_pro.cgi",param); } }publicStringdoGet(Stringlocation,java.util.Listparam) { if(param!=null) { StringBufferbufParam=newStringBuffer();
//通過一個(gè)枚舉器列出所有參數(shù)
Iteratoriterator=param.iterator(); Stringkey=null; Stringvalue=null;//枚舉出所有的參數(shù)
while(iterator.hasNext()) { key=(String)iterator.next(); value=(String)iterator.next(); //對(duì)參數(shù)進(jìn)行編碼,然後轉(zhuǎn)化成key=value的格式
//並將它們串在一起,通過&分隔開
bufParam.append(URLEncoder.encode(key)); bufParam.append("="); bufParam.append(URLEncoder.encode(value));if(iterator.hasNext()) bufParam.append("&"); }
//把參數(shù)連接在URL後面,用?號(hào)分隔開
location+="?"+bufParam.toString(); }
StringBufferpage=newStringBuffer();try { url=newURL(location); conn=url.openConnection(); //連接伺服器
conn.connect(); //得到一個(gè)輸入流
InputStreamin=conn.getInputStream(); BufferedReaderreader= newBufferedReader(new
InputStreamReader(in));//打開Get.html檔準(zhǔn)備寫
fw=newFileWriter("Get.html"); pw=newPrintWriter(fw);
Stringline=""; //逐行讀入
while((line=reader.readLine())!=null) { page.append(line+"\n"); pw.println(line);}
pw.close(); fw.close(); reader.close(); in.close(); } catch(IOExceptione) { Transcript.println(e.getMessage()); }returnpage.toString(); }
publicStringdoPost(Stringlocation,java.util.Listparam) { StringBufferpage=newStringBuffer();
try { url=newURL(location); conn=url.openConnection();//同時(shí)打開到伺服器的輸入和輸出流
conn.setDoOutput(true); conn.setDoInput(true);
if(param!=null) { //獲取一個(gè)到伺服器的輸入通道
PrintWriterout=newPrintWriter(conn.getOutputStream());
Iteratoriterator=param.iterator();Stringkey=null; Stringvalue=null; //枚舉列出所有參數(shù)
while(iterator.hasNext()) { //對(duì)參數(shù)進(jìn)行編碼,並逐個(gè)將參數(shù)轉(zhuǎn)化成
//key=value的格式,直接輸出到伺服器
key=URLEncoder.encode((String)iterator.next()); value=URLEncoder.encode((String)iterator.next());out.print(key+"="+value); if(iterator.hasNext()) out.print("&"); }
out.close(); }
//連接伺服器
conn.connect();//獲取一個(gè)輸入流
InputStreamin=conn.getInputStream(); BufferedReaderreader=
newBufferedReader(newInputStreamReader(in)); //打開Post.html準(zhǔn)備寫
fw=newFileWriter("Post.html"); pw=newPrintWriter(fw); Stringline=""; //逐行讀入while((line=reader.readLine())!=null) { page.append(line+"\n"); pw.println(line); }
pw.close(); fw.close(); reader.close(); in.close();}
catch(IOExceptione) { Transcript.println(e.getMessage()); }
returnpage.toString(); }}
這個(gè)例子比較長,但它並不複雜。程式中,通過兩個(gè)方法:doGet()和doPost()分別實(shí)現(xiàn)了GET和POST兩種方式與CGI的通信。在doGet()方法中,測(cè)試的是使用Yahoo提供的搜索引擎;在doPost()方法中,測(cè)試的是登陸163.net的免費(fèi)郵件系統(tǒng)。編譯這個(gè)程式後,測(cè)試doGet()方法,可以用下麵的命令行運(yùn)行它:
javaCGITestgetJava
產(chǎn)生的結(jié)果將保存在一個(gè)叫Get.html的檔中。用流覽器打開它,可以看到同在上的搜索引擎中輸入“Java”搜索產(chǎn)生的頁面相同的效果。doGet()方法需要提供兩個(gè)參數(shù),第一個(gè)參數(shù)是創(chuàng)建交互的CGI程式的URL,第二個(gè)參數(shù)是一個(gè)List範(fàn)本類,裏面存放了提供的參數(shù)。要注意的是,把這些參數(shù)串在一起並接在URL的末尾時(shí),必須用URLEncoder類的encode()方法對(duì)參數(shù)進(jìn)行URL編碼。
如果要測(cè)試doPost()方法,須通過下麵的命令行運(yùn)行這個(gè)程式:
javaCGITestpostUsernamePassword
要測(cè)試登陸163.net的免費(fèi)郵箱,就必須在163.net上註冊(cè)一個(gè)郵箱,並在命令行的第2、3個(gè)參數(shù)提供登陸郵箱的用戶名和密碼。產(chǎn)生的結(jié)果保存在一個(gè)叫Post.html的檔中。同樣,流覽這個(gè)檔看到的同在網(wǎng)站上登錄免費(fèi)郵件系統(tǒng)所看到的頁面一樣。
doPost()方法同樣需要提供交互用的URL和保存參數(shù)的List,但是這裏我們沒有把參數(shù)串在一起,而是通過URLConnection類的getOutputStream()方法打開輸入流,逐個(gè)將參數(shù)寫入到輸出流中。這樣無論有多少參數(shù)也沒關(guān)係了,但要注意的是,仍然要對(duì)參數(shù)進(jìn)行URL編碼並用“&”將它們分開。
注:由於各網(wǎng)站的CGI程式URL和參數(shù)可能會(huì)隨著網(wǎng)站的程式修改而改變,所以這個(gè)程式並不能保證一直得到正確的結(jié)果,但是只需查看網(wǎng)站的源代碼,並找到HTMLForm的action以及input的所有參數(shù),對(duì)上面例子中的程式略作修改,就可以順利的得到正確結(jié)果了。11.2使用Socket通信URL和URLConnection類提供給我們一種簡(jiǎn)便的方法編寫網(wǎng)路程式,實(shí)現(xiàn)一些較高級(jí)的協(xié)議訪問Internet。但是通常,這些協(xié)議是不夠的,我們常需要用一種更通用、更底層的方法編寫網(wǎng)路應(yīng)用程式,這就是我們最常用的套接字(Socket)通信。11.2.1InetAddress類
Internet上的每一臺(tái)電腦都擁有一個(gè)惟一的地址,這樣才能和其他網(wǎng)路上的電腦互通資訊。我們常說的TCP/IP中的IP(InternetProtocal)。就是定義Internet上電腦的地址的。我們知道,一個(gè)IP地址必須由四個(gè)0~255之間的數(shù)字組成,數(shù)字之間用點(diǎn)隔開。由於很難記憶這個(gè)由四個(gè)數(shù)字組成的地址,所以有了主機(jī)名(hostname)的出現(xiàn),例如,就是一個(gè)主機(jī)名。InetAddress就是用來實(shí)現(xiàn)IP的類。
InetAddress
類的聲名如下:
publicfinalclassInetAddressextendsObjectimplementsSerializableInetAddress類沒有提供任何構(gòu)造函數(shù),我們只能通過它本身提供的一些靜態(tài)方法來建立一個(gè)它的實(shí)例。通常,用於建立一個(gè)InetAddress類的方法有如下幾種:●publicstaticInetAddressgetByName(Stringhost)throwsUnknowHostException指定主機(jī)名建立一個(gè)InetAddress的實(shí)例?!駊ublicstaticInetAddressgetByAddress(byte[]addr)throwsUnknowHostException指定IP地址建立一個(gè)InetAddress的實(shí)例?!駊ublicstaticInetAddressgetLocalHost()throwsUnknowHostException建立本機(jī)的InetAddress的實(shí)例。下麵我們通過一個(gè)實(shí)例來看看如何使用InetAddress類。例11.6InetAddressDemo.javaimportgenesis.*;import.*;
publicclassInetAddressDemo{ publicstaticvoidmain(String[]args) { try {
InetAddressaddr1=InetAddress.getByName("www.szptt.net.cn"); Transcript.println(addr1.getHostAddress());
InetAddressaddr2=InetAddress.getByAddress(addr1.getAddress()); Transcript.println(addr2.getHostName());
Transcript.println();
InetAddressaddr3=InetAddress.getLocalHost(); Transcript.println(addr3.getHostName()); Transcript.println(addr3.getHostAddress()); } catch(UnknownHostExceptione) { Transcript.println(e.getMessage()); } }}程式的運(yùn)行結(jié)果如圖11.6所示。圖11.6
從運(yùn)行結(jié)果我們可以看到,調(diào)用getByName()方法通過主機(jī)名建立一個(gè)InetAddress類的實(shí)例後,我們就可以得到對(duì)應(yīng)於這個(gè)主機(jī)名的地址。需要注意的是,在調(diào)用建立InetAddress實(shí)例的幾個(gè)靜態(tài)方法時(shí),這些方法會(huì)連接指定的地址或功能變數(shù)名稱,如果無法連通,會(huì)拋出一個(gè)UnknowHostException,所以必須用try-catch塊包含這個(gè)方法,並處理無法連通的情況。11.2.2客戶端Socket類
TCP/IP最主要的功能是提供點(diǎn)對(duì)點(diǎn)通信機(jī)制。一個(gè)機(jī)器要在網(wǎng)路上與另一臺(tái)機(jī)器通信,就需要建立一個(gè)互聯(lián)的通道。這個(gè)通道傳輸兩個(gè)數(shù)據(jù)流,一個(gè)為發(fā)出的數(shù)據(jù)流,一個(gè)為接收的數(shù)據(jù)流。由於有了IP地址,我們可以方便的在Internet上找到另一臺(tái)機(jī)器。假如自己的機(jī)器要和另一臺(tái)機(jī)器通信,就必須向那臺(tái)主機(jī)發(fā)起一個(gè)連接請(qǐng)求。如果這個(gè)請(qǐng)求被接受,這次通信過程就開始了。通常情況下,我們把發(fā)起請(qǐng)求的機(jī)器叫客戶端,被請(qǐng)求的機(jī)器則稱為伺服器。Socket類是用來編寫客戶端程式的類。Socket類的聲明如下:
publicclassSocketextendsObjectsSocket類常用的方法有下列幾種:●Socket(InetAddressaddress,intport)throwsIOException建立一個(gè)Socket的實(shí)例,用來連接指定的地址和端口?!馭ocket(Stringhost,intport)throwsUnknowHostException,IOException和上面類似,但是通過主機(jī)名代替InetAddress。●publicvoidclose()throwsIOException中斷該Socket連接。●publicInetAddressgetInetAddress()返回該Socket實(shí)例所連接的地址?!駊ublicInputStreamgetInputStream()throwsIOException返回一個(gè)輸入流,用來接收伺服器端發(fā)送的資訊?!駊ublicOutputStreamgetOutputStream()throwsIOException返回一個(gè)輸出流,用來向伺服器端發(fā)送資訊?!駊ublicintgetPort()返回該Socket實(shí)例連接遠(yuǎn)程的端口?!駊ublicintgetSoTimeout()throwsSocketException返回接收資訊的時(shí)間限制。如果返回值為0,則表示沒有限制?!駊ublicvoidsetSoTimeout(inttimeout)throwsSocketException
設(shè)置接收資訊的時(shí)間限制,以千分之一秒為單位。利用InputStream接收資訊時(shí),程式會(huì)一直掛起,直到接收到資訊,或者到達(dá)時(shí)間限制為止。如果出現(xiàn)超時(shí),則會(huì)拋出一個(gè)java.io.InterruptedIOException。如果timeout設(shè)為0,則沒有時(shí)間限制,程式會(huì)一直掛起等待。下麵來看一個(gè)簡(jiǎn)單的Socket編程的示例。例11.7SocketDemo.javaimportgenesis.*;importjava.io.*;import.*;publicclassSocketDemo{ publicstaticvoidmain(String[]args) { try { //構(gòu)造一個(gè)連接到時(shí)間伺服器time-A.timefreq.bldrdoc.gov //端口13的Socket類實(shí)例。
Socketsock=newSocket("time-A.timefreq.bldrdoc.gov",13);//從Socket獲得一個(gè)接收伺服器資訊的輸入流
BufferedReaderin =newBufferedReader( newInputStreamReader(sock.getInputStream())); while(true) { Stringline=in.readLine(); if(null==line) break; elseTranscript.println(line); } } catch(IOExceptione) { Transcript.println(e.getMessage()); } }}程式的運(yùn)行結(jié)果如圖11.7所示。圖11.7
這個(gè)例子非常簡(jiǎn)單,它打開了一個(gè)Socket類,這個(gè)Socket類連接到時(shí)間伺服器time-A.timefreq.bldrdoc.gov的13端口上。當(dāng)連接成功後,Socket類的getInputStream()方法返回一個(gè)InputStream對(duì)象,然後程式把流對(duì)象鏈接到一個(gè)BufferedReader上。接著就可以用readLine()方法讀取伺服器發(fā)送的所有字元,並把它們輸出到螢?zāi)簧?。直到readLine()方法返回null,即讀完所有伺服器發(fā)送的資訊,程式才會(huì)結(jié)束。在Windows命令行下輸入telnettime-A.timefreq.bldrdoc.gov13,可以看到和上面類似的輸出結(jié)果,如圖11.8所示。圖11.811.2.3伺服器端ServerSocket類前面實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的網(wǎng)路客戶程式,它可以從伺服器上接收資訊,下麵我們學(xué)習(xí)實(shí)現(xiàn)伺服器端的編程方法。一個(gè)伺服器程式啟動(dòng)後,通常會(huì)監(jiān)聽某一個(gè)端口,等待某一臺(tái)客戶端電腦發(fā)出連接的請(qǐng)求,然後作出反應(yīng)。ServerSocket類的聲明如下:
publicclassServerSocketextendsObjects
ServerSocket類的大部分方法都和Socket類的類似,它最常用的構(gòu)造方法如下:
ServerSocket(intport)throwsIOException
創(chuàng)建一個(gè)ServerSocket示例是不需要指定IP地址的,SeverSocket總是處於監(jiān)聽本機(jī)端口的狀態(tài)。下麵我們看一個(gè)伺服器程式的示例。例11.8ReversalServer.javaimportjava.io.*;import.*;
publicclassReversalServer{
publicstaticvoidmain(String[]agrs){
try { //創(chuàng)建一個(gè)監(jiān)聽8189端口的ServerSocket ServerSockets=newServerSocket(8189); //啟動(dòng)ServerSocket的監(jiān)聽
Socketinsock=s.accept(); //建立輸入流通道
BufferedReaderin=newBufferedReader( newInputStreamReader(insock.getInputStream()));//建立輸出流通道
PrintWriterout= newPrintWriter(insock.getOutputStream(),true/*autoFlush*/); out.println("Enterquittoexit.");
while(true) { Stringline=in.readLine(); if(null==line) continue;if(line.trim().equals("quit")) break;else { //翻轉(zhuǎn)輸入的字串
StringBufferrline=newStringBuffer(); for(inti=line.length();i>0;i--) rline.append(line.charAt(i-1)); out.println("Reversed:"+rline.toString()); } }out.close(); in.close(); insock.close(); } catch(IOExceptione) { e.printStackTrace(); } }}
上面的例子是一個(gè)簡(jiǎn)單的伺服器程式,它啟動(dòng)了一個(gè)ServerSocket監(jiān)聽本機(jī)的8189端口,這個(gè)端口一般不會(huì)被用到。當(dāng)有其他客戶端請(qǐng)求與它聯(lián)接時(shí),accept()方法將接受這個(gè)請(qǐng)求並創(chuàng)建一個(gè)獨(dú)立的Socket實(shí)例。通過這個(gè)實(shí)例,我們可以取得輸入和輸出的兩個(gè)流。程式首先通過輸出流向客戶端發(fā)送了一條問候資訊:
out.println("Hello!Pleaseinput.Enterquittoexit.");
然後,伺服器程式接收來自客戶端的輸入。每次從客戶端讀取一行資訊,就將這些輸入的字串翻轉(zhuǎn)以後再送回給客戶端,所以這個(gè)伺服器程式叫做ReversalSever。編譯並運(yùn)行這個(gè)程式,然後在Windows命令行下輸入“telnet8189”。IP地址是一個(gè)特殊的地址,稱為本機(jī)回送地址(LocalLoopbackAddress),它代表著本機(jī)。
由於運(yùn)行伺服器程式的機(jī)器和啟動(dòng)telnet測(cè)試的是同一主機(jī),所以這個(gè)地址也正是我們要連接的。當(dāng)然,也可以從其他機(jī)器上啟動(dòng)telnet來測(cè)試這個(gè)伺服器程式,這時(shí)候就必須telnet到運(yùn)行伺服器程式的IP地址上。啟動(dòng)telnet以後,輸入任何資訊,看看結(jié)果是不是你想像的,最後輸入quit結(jié)束連接。此處如輸入“123456”,則結(jié)果如圖11.9所示。
圖11.911.2.4多客戶通信機(jī)制我們注意到,前面例子中的伺服器程式只能服務(wù)於一個(gè)客戶端,也就是說,一個(gè)客戶連接到這個(gè)服務(wù)程式後,將一直獨(dú)佔(zhàn)它。而通常情況下,我們看到的伺服器總是要服務(wù)於許許多多的客戶端的,比如一個(gè)網(wǎng)頁伺服器,可以接受許多客戶的流覽。利用線程的特性,我們就可以很好地解決服務(wù)於多個(gè)客戶的問題。
我們只需要對(duì)上面的程式做少許修改,就可以使它服務(wù)於多個(gè)客戶端。首先,應(yīng)該把主程序部分放入一個(gè)迴圈,每接收到一個(gè)來自客戶端的請(qǐng)求,就啟動(dòng)一個(gè)線程來處理他。而主程序則可以繼續(xù)等待來自其他客戶端的請(qǐng)求。下麵看修改以後的程式,可試著啟動(dòng)多個(gè)telnet連接它進(jìn)行測(cè)試。例11.9ThreadedReversalServer.javaimportjava.io.*;import.*;
publicclassThreadedReversalServer{ publicstaticvoidmain(String[]agrs) { inti=0;try { //創(chuàng)建一個(gè)監(jiān)聽8189端口的ServerSocket ServerSockets=newServerSocket(8189); for(;;) { //啟動(dòng)ServerSocket的監(jiān)聽
Socketinsock=s.accept(); System.out.println("Thread"+i+"run."); //啟動(dòng)處理客戶端資訊的線程newThreadReversal(insock,i).start(); i++; } } catch(IOExceptione) { e.printStackTrace(); } }
}classThreadReversalextendsThread{ privateSocketsock; privateintcounter;
publicThreadReversal(Sockets,inti) { sock=s; counter=i; }publicvoidrun() { try { //建立輸入流通道
BufferedReaderin= newBufferedReader( newInputStreamReader(sock.getInputStream())); //建立輸出流通道
PrintWriterout=newPrintWriter(sock.getOutputStream(),true/*autoFlush*/); out.println("Hello!Pleaseinput.Enterquittoexit."); while(true) { Stringline=in.readLine(); if(null==line) continue; if(line.trim().equals("quit")) break;else { //翻轉(zhuǎn)輸入的字串
StringBufferrline=newStringBuffer(); for(inti=line.length();i>0;i--) rline.append(
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024美容師考試的片面性和全面性及答案
- 寵物營養(yǎng)相關(guān)法律法規(guī)試題及答案
- 2024年計(jì)算機(jī)基礎(chǔ)考試模擬演練試題及答案
- 2024-2025學(xué)年內(nèi)蒙古巴彥淖爾一中高一下學(xué)期第一次學(xué)業(yè)診斷英語及答案
- 二手車評(píng)估師考試專業(yè)術(shù)語試題及答案
- 安裝造價(jià)考試試題及答案
- 開門安全教育課件
- 網(wǎng)絡(luò)貨幣基礎(chǔ)知識(shí)考核及答案
- 小自考公共事業(yè)管理反饋機(jī)制試題及答案
- 文綜全國卷試題及答案
- GB/T 16921-2005金屬覆蓋層覆蓋層厚度測(cè)量X射線光譜方法
- GB/T 11168-2009光學(xué)系統(tǒng)像質(zhì)測(cè)試方法
- 新教材高中歷史必修中外歷史綱要上全冊(cè)教學(xué)課件
- 公共部門人力資源管理概論課件
- 六年級(jí)下冊(cè)科學(xué)第一單元質(zhì)量檢測(cè)卷粵教版(含答案)
- 【計(jì)算機(jī)應(yīng)用基礎(chǔ)試題】韓山師范大學(xué)2022年練習(xí)題匯總(附答案解析)
- 愛愛醫(yī)資源-生理學(xué)-122排卵、黃體形成與月經(jīng)周期
- 科技小巨人工程驗(yàn)收培訓(xùn)
- 大班繪本教案《月亮冰激凌》
- 火力發(fā)電廠運(yùn)煤設(shè)計(jì)規(guī)程
- 01-第一章--粉末的制取霧化法
評(píng)論
0/150
提交評(píng)論