Java網(wǎng)絡(luò)編程技術(shù)_第1頁
Java網(wǎng)絡(luò)編程技術(shù)_第2頁
Java網(wǎng)絡(luò)編程技術(shù)_第3頁
Java網(wǎng)絡(luò)編程技術(shù)_第4頁
Java網(wǎng)絡(luò)編程技術(shù)_第5頁
已閱讀5頁,還剩24頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

PAGE340PAGE341第14章Java網(wǎng)絡(luò)編程技術(shù)網(wǎng)絡(luò)編程的目的就是直接或間接地通過網(wǎng)絡(luò)協(xié)議與其他計(jì)算機(jī)進(jìn)行通信。網(wǎng)絡(luò)編程中有兩個主要的問題:一個是如何準(zhǔn)確地定位網(wǎng)絡(luò)上一臺或多臺主機(jī),另一個就是找到主機(jī)后如何可靠、高效地進(jìn)行數(shù)據(jù)傳輸。在TCP/IP協(xié)議集中,IP協(xié)議主要負(fù)責(zé)網(wǎng)絡(luò)主機(jī)的定位,由IP地址可以唯一地確定Internet上的一臺主機(jī);而TCP協(xié)議實(shí)現(xiàn)可靠的數(shù)據(jù)傳輸。目前較為流行的網(wǎng)絡(luò)編程模型是客戶機(jī)/服務(wù)器(C/S)模式,即通信的一方作為服務(wù)器等待客戶機(jī)提出請求并予以響應(yīng);客戶機(jī)則在需要服務(wù)時向服務(wù)器提出申請。服務(wù)器一般作為守護(hù)進(jìn)程始終運(yùn)行,監(jiān)聽服務(wù)器的網(wǎng)絡(luò)端口,一旦某個客戶機(jī)提出請求,服務(wù)器就會為每個客戶機(jī)創(chuàng)建并啟動一個服務(wù)線程來響應(yīng)該客戶機(jī),同時自己繼續(xù)監(jiān)聽服務(wù)器的網(wǎng)絡(luò)端口,使后來的客戶機(jī)也能及時得到服務(wù)。14.1使用URLURL(UniformResourceLocator,統(tǒng)一資源定位器)主要用來對Internet上的信息進(jìn)行定位。瀏覽器借助它來查找Web上的信息。實(shí)際上,Web通過URL和HTML對所有的網(wǎng)絡(luò)資源定位。在Java的網(wǎng)絡(luò)編程中,程序員使用URL類庫,獲取Internet上的信息。14.1.1URL組成URL的兩種語句格式:1./2.:80/index.htm從上面的URL格式可以看出,URL由以下4個部分組成:協(xié)議(http)。指網(wǎng)絡(luò)連接用到的協(xié)議,如,本例的http是超文本協(xié)議,http不是唯一的協(xié)議,常見的協(xié)議還有FTP、Gopher等。其中的冒號:將協(xié)議與URL的其他部分隔開。主機(jī)名或其IP地址()。該項(xiàng)位于雙斜線(//)和單斜線(/)之間,其中的冒號(:)是可選部。如,本例的/或:80表示了主機(jī)名。端口號(80)。它是可選的參數(shù),它位于主機(jī)名和右邊的單斜線(/)之間(HTTP協(xié)議的默認(rèn)端口為80,所以“:80”可以不寫),可以是別的數(shù)字組成端口號。網(wǎng)頁文件及所在的目錄(index.htm)。網(wǎng)頁文件及目錄是瀏覽器要查找的網(wǎng)頁資源。如index.html或index.htm文件。14.1.2URL類在Java中URL類的繼承關(guān)系如下:publicfinalclassURLextendsObjectimplementsSerializable1.URL的構(gòu)造方法URL類有多個構(gòu)造方法,它們都可能拋出MalformedURLException異常。(1)URL(StringurlSpecifier)的使用格式URLurl=newURL("/");(2)URL(Stringprotocol,StringhostName,intport,Stringpath)的使用格式URLurl=newURL("http","",80,"nba");(3)URL(Stringprotocol,StringhostName,Stringpath)的使用格式URLurl=newURL("http","","nba");(4)URL(URLcontext,Stringspec)使用一個已經(jīng)存在的URL創(chuàng)建一個新的URLURLurl1=newURL("/");//構(gòu)造url1對象URLurl2=newURL(url,"nba");//利用上面的url1對象和一個字符串為參數(shù)創(chuàng)建新的url2對象2.URL的實(shí)用方法booleanequals(Objectobj):比較兩個URL是否相同。StringgetAuthority():獲得URL的授權(quán)部分。ObjectgetContent():獲得URL的內(nèi)容。ObjectgetContent(Class[]classes):獲得URL的內(nèi)容。intgetDefaultPort():獲得URL中的默認(rèn)端口號。StringgetFile():獲得URL中的網(wǎng)頁文件名。StringgetHost():獲得URL中的主機(jī)名。StringgetPath():獲得URL中網(wǎng)頁所在的路徑。intgetPort():獲得URL中的端口號,若沒有設(shè)置端口號則返回-1。StringgetProtocol():獲得URL所用的協(xié)議名稱。StringgetQuery():獲得URL的查詢部分。StringgetRef():獲得URL的錨點(diǎn)(也稱為“引用”)。StringgetUserInfo():獲得URL的UserInfo部分。inthashCode():創(chuàng)建一個適合散列表索引的整數(shù)。InputStreamopenStream():打開URL的鏈接。并返回一個用于從該鏈接讀入的InputStream流。booleansameFile(URLother):比較兩個URL,不包括片段部分。StringtoExternalForm():將URL轉(zhuǎn)換為字符串格式。StringtoString():構(gòu)造URL的字符串表示形式。URItoURI():返回與URL等效的URI。3.URL應(yīng)用【例14.1】創(chuàng)建一個URL實(shí)例,然后檢查它的相關(guān)屬性。程序名:Example14_1.java?!境绦蛟创a】import.*;classExample14_1{ publicstaticvoidmain(Stringargs[])throwsMalformedURLException{ try{ URLurl=newURL(""); //創(chuàng)建一個URL對象 System.out.println("授權(quán):"+url.getAuthority());//檢查它的授權(quán)部分 System.out.println("協(xié)議名稱:"+url.getProtocol());//獲得此URL的協(xié)議名 System.out.println("默認(rèn)端口號:"+url.getDefaultPort());//獲得默認(rèn)端口號 System.out.println("主機(jī)名:"+url.getHost()); //獲得此URL的主機(jī)名 System.out.println("文件名:"+url.getFile()); //獲得此URL的文件名 System.out.println("Ext:"+url.toExternalForm()); System.out.println("字符串表示形式:"+url.toString()); } catch(MalformedURLExceptionex){ System.out.println("fail!"); } }}【例14.2】在Applet的文本框中輸入一個網(wǎng)址,單擊search(搜索)按鈕后鏈接到該網(wǎng)址相對應(yīng)的頁面。程序名:Example14_2.java【程序說明】要在Applet中鏈接到其它Web頁面,可使用下面的代碼來實(shí)現(xiàn)。getAppletContext().showDocument(url);//執(zhí)行該方法,轉(zhuǎn)向url頁面因?yàn)間etAppletContext()方法是在Applet類中定義的,由showDocument()定位到另一個Web頁面。【程序源代碼】importjava.awt.*;importjava.awt.event.*;importjavax.swing.*;import.*;importjava.io.*;importjava.applet.*; publicclassShowWebPageextendsJAppletimplementsActionListener{ privatestaticfinallongserialVersionUID=1L; privateJLabellfn=newJLabel("URLaddress"); privateJTextFieldtfu=newJTextField(20);//網(wǎng)址輸入框 privateJButtonsearb=newJButton("search");//搜索按鈕 /**初始化界面*/ publicvoidinit(){ Containerc=getContentPane();//獲取底層容器 searb.addActionListener(this);//為按鈕注冊監(jiān)聽器 c.setLayout(newFlowLayout());//設(shè)置布局 c.add(lfn);//添加標(biāo)簽 c.add(tfu);//添加網(wǎng)址輸入文本框 c.add(searb);//添加Search(搜索)按鈕 } /**響應(yīng)按鈕事件*/ publicvoidactionPerformed(ActionEvente){ Strings=tfu.getText();//獲取輸入框內(nèi)容 try{ URLu=newURL(s);//利用網(wǎng)址輸入框中的內(nèi)容創(chuàng)建一個URL對象 getAppletContext().showDocument(u);//顯示指定URL的網(wǎng)頁 }catch(MalformedURLExceptionex){ showStatus(s+"fileformaterror!");} }}14.2Socket套接字14.2.1Socket的含義Socket描述了網(wǎng)絡(luò)上運(yùn)行的兩個程序間實(shí)現(xiàn)通信的任一端。它既可以接收請求,也可以發(fā)送請求。利用它可以方便地編寫網(wǎng)絡(luò)上傳遞數(shù)據(jù)的程序。在Java中,有專門的Socket類來處理用戶的請求和響應(yīng)。利用Socket類的方法,就可以實(shí)現(xiàn)兩臺計(jì)算機(jī)之間的通信。在Java中,Socket可以理解為客戶端或者服務(wù)器端的一個特殊的對象。這個對象有兩個關(guān)鍵的方法:一個是getInputStream()方法;另一個是getOutputStream()方法。getInputStream()方法可以得到一個輸入流??蛻舳说腟ocket對象上的getInputStream()方法得到的輸入流就是從服務(wù)器端發(fā)送回來的數(shù)據(jù)流。getOutputStream()方法得到一個輸出流。客戶端的Socket對象上的getOutputStream()方法返回的輸出流就是將要發(fā)送到服務(wù)器端的數(shù)據(jù)流(其實(shí)是一個緩沖區(qū),暫時存儲將要發(fā)送到服務(wù)器端的數(shù)據(jù))。下面介紹Socket實(shí)現(xiàn)網(wǎng)絡(luò)通信的機(jī)制。圖14-1服務(wù)器與客戶機(jī)進(jìn)行通信的方式1.建立服務(wù)器端套接字Java中有一個ServerSocket類,專門用來建立服務(wù)器端的套接字??梢杂梅?wù)器需要使用的端口號作為參數(shù)來創(chuàng)建服務(wù)器套接字。ServerSocketserver=newServerSocket(8000);這條語句創(chuàng)建了一個服務(wù)器套接字server。該服務(wù)器使用8000號端口監(jiān)聽客戶機(jī)請求。2.在服務(wù)器端為每個客戶機(jī)建立代理套接字當(dāng)一個客戶端程序要求與服務(wù)器建立一個Socket連接時,在服務(wù)器端,服務(wù)器套接字便會為客戶機(jī)建立一個客戶機(jī)代理套接字connectToClient,通過該套接字與客戶機(jī)端的Socket套接字建立連接。(1)在服務(wù)器端為客戶機(jī)建立代理套接字的格式如下:SocketconnectToClient=server.accept();//代理套接字connectToClient(2)服務(wù)器端從客戶端收到的輸入流s_in(也就是客戶端的輸出流)的其格式如下:BufferedReaders_in=newBufferedReader(newInputStreamReader(connectToClient.getInputStream()));(3)服務(wù)器端傳給客戶端的輸出流s_out(也就是客戶端的輸入流)的格式如下:PrintWriterout=newPrintWriter(connectToClient.getOutputStream(),true);隨后,就可以使用s_in.readLine()方法得到客戶端的輸入;也可以使用s_out.println()方法向客戶端發(fā)送數(shù)據(jù)。在所有通信結(jié)束以后應(yīng)該關(guān)閉這兩個數(shù)據(jù)流。關(guān)閉的順序是先關(guān)閉輸出流,再關(guān)閉輸入流,即依次調(diào)用out.close();和in.close();方法。3.建立客戶端套接字(Socket)客戶端只需用服務(wù)器所在機(jī)器的IP以及服務(wù)器的端口號作為參數(shù)即可創(chuàng)建一個客戶端套接字(connectToServer)。假設(shè)服務(wù)器的端口號是8000。(1)建立客戶機(jī)的套接字格式如下:SocketconnectToServer=newSocket("服務(wù)器IP地址",8000);或者SocketconnectToServer=newSocket("服務(wù)器主機(jī)名",8000);(2)客戶端從服務(wù)器端收到的輸入流in(也就是服務(wù)器端的輸出流)的格式如下:InputStreamin=connectToServer.getInputStream();(3)客戶端向服務(wù)器端發(fā)送的輸出流out(也就是服務(wù)器端的輸入流)的格式如下:OutputStreamout=connectToServer.getOutputStream();14.2.2Socket的應(yīng)用【例14.3】實(shí)現(xiàn)客戶機(jī)和服務(wù)器相互交替接收對方所寫入的信息,實(shí)現(xiàn)兩者間的通信。程序名:MyClient.java&&MyServer.java【程序源代碼】/**客戶端程序:MyClient.Java*/packagesocket;import.*;importjava.io.*;publicclassMyClient{ staticSocketserver; publicstaticvoidmain(String[]args)throwsIOException{ try{ /**向本機(jī)的5678端口發(fā)出客戶機(jī)請求*由Socket對象得到輸入流,并構(gòu)造相應(yīng)的BufferedReader對象*由Socket對象得到輸出流,并構(gòu)造PrintWriter對象*/ server=newSocket(InetAddress.getLocalHost(),5678); BufferedReaderin=newBufferedReader(newInputStreamReader(server.getInputStream())); PrintWriterout=newPrintWriter(server.getOutputStream()); /**由系統(tǒng)標(biāo)準(zhǔn)輸入設(shè)備構(gòu)造BufferedReader對象作為用戶輸入信息流*/ System.out.println("輸入發(fā)送信息:"); BufferedReaderwt=newBufferedReader(newInputStreamReader(System.in)); /**利用循環(huán)不斷發(fā)送數(shù)據(jù)*/ while(true){ Stringstr=wt.readLine();//從系統(tǒng)標(biāo)準(zhǔn)輸入設(shè)備讀入一字符串 out.println(str);//將從系統(tǒng)標(biāo)準(zhǔn)輸入設(shè)備讀入的字符串輸出到Server out.flush();//刷新輸出流,使Server馬上收到該字符串 if(str.equals("end")){ break;} System.out.println("Server說:"+in.readLine());//輸出接收到信息 } out.close();//關(guān)閉Socket輸出流 in.close();//關(guān)閉Socket輸入流 server.close();//關(guān)閉Socket }catch(Exceptione){ System.out.println("error:"+e); } }} /**服務(wù)器端程序:MyServer.java*/packagesocket;//類包importjava.io.*;import.*;publicclassMyServer{ publicstaticvoidmain(String[]args)throwsIOException{ try{ //創(chuàng)建一個ServerSocket在端口5678監(jiān)聽客戶機(jī)請求 ServerSocketserver=newServerSocket(5678); /**使用accept()阻塞等待客戶機(jī)請求,*有客戶機(jī)請求到來則產(chǎn)生一個Socket對象,并繼續(xù)執(zhí)行*/ Socketclient=server.accept(); //顯示連接到的客戶機(jī)IP System.out.println("connectto:"+client.getInetAddress()); //由系統(tǒng)標(biāo)準(zhǔn)輸入設(shè)備構(gòu)造BufferedReader對象作為用戶輸入信息流 BufferedReaderwt=newBufferedReader(newInputStreamReader(System.in)); //由Socket對象得到輸入流,并構(gòu)造相應(yīng)的BufferedReader對象 BufferedReaderin=newBufferedReader(newInputStreamReader(client.getInputStream())); //由Socket對象得到輸出流,并構(gòu)造PrintWriter對象 PrintWriterout=newPrintWriter(client.getOutputStream()); /**利用循環(huán)不斷接收數(shù)據(jù)*/ while(true){ Stringstr1=in.readLine(); //在標(biāo)準(zhǔn)輸出設(shè)備上打印從客戶端讀入的字符串 System.out.println("Client說:"+str1); if(str1.equals("end"))break;//若客戶機(jī)輸入"end",則斷開與客戶機(jī)的連接 Stringstr2=wt.readLine(); out.println(str2);//將從系統(tǒng)標(biāo)準(zhǔn)輸入設(shè)備讀入的字符串輸出到客戶機(jī) out.flush();//刷新輸出流,使服務(wù)器馬上收到該字符串 } out.close();//關(guān)閉Socket輸出流 in.close();//關(guān)閉Socket輸入流 client.close();//關(guān)閉Socket server.close();//關(guān)閉ServerSocket }catch(Exceptione){ System.out.println("error:"+e); } }}【程序運(yùn)行結(jié)果】將上面兩個程序放在Socket目錄下編譯,先運(yùn)行服務(wù)器端,然后運(yùn)行客戶端,結(jié)果如圖14-2、圖14-3所示。圖14-2例14.3客戶端程序運(yùn)行結(jié)果圖14-3例14.3服務(wù)器端程序運(yùn)行結(jié)果【例14.4】Socket的多線程通信。程序名:Cal_Client.java&&Cal_Server.java【程序說明】在本例中,在客戶機(jī)輸入三角形3條邊的長度并發(fā)送給服務(wù)器;服務(wù)器把計(jì)算出的三角形面積返回給客戶機(jī)??梢詫⒂?jì)算量大的工作放在服務(wù)器端,客戶端則負(fù)責(zé)計(jì)算量小的工作,實(shí)現(xiàn)客戶機(jī)、服務(wù)器交互計(jì)算,從而完成任務(wù)。套接字連接中涉及到輸入流和輸出流操作,為了不影響做其他的事情,應(yīng)把套接字連接放在一個單獨(dú)的線程中。另外,服務(wù)器端為每個客戶機(jī)創(chuàng)建并啟動一個服務(wù)線程?!境绦蛟创a】/**客戶端代碼:Cal_Client.Java**/import.*;importjava.io.*;importjava.awt.*;importjava.awt.event.*;importjava.applet.*;importjavax.swing.*;publicclassCal_ClientextendsJAppletimplementsRunnable,ActionListener{ JButtonb_calc; //計(jì)算按鈕 TextFieldb_edge,b_result;//b_edg是3條邊輸入的文本框;b_result是結(jié)果文本框 Socketsocket=null; //連接套接字 DataInputStreamin=null; //輸入數(shù)據(jù)流 DataOutputStreamout=null; //輸出數(shù)據(jù)流 Threadthread; //負(fù)責(zé)接收計(jì)算結(jié)果的線程 /**下面初始化界面*/ publicvoidinit()

{ setLayout(newGridLayout(2,2)); JPanelp1=newJPanel(),p2=newJPanel(); b_calc=newJButton("b_calc"); b_edge=newTextField(12); b_result=newTextField(12); p1.add(newJLabel("輸入三角形三邊的長度,用逗號或空格分隔:")); p1.add(b_edge);p2.add(newJLabel("計(jì)算結(jié)果:")); p2.add(b_result);p2.add(b_calc); b_calc.addActionListener(this); add(p1);add(p2); /**創(chuàng)建連接套接字,并構(gòu)造相應(yīng)的輸入輸出流對象*/ Try { /**向本機(jī)的4331端口發(fā)出客戶請求*由Socket對象得到輸入流,并構(gòu)造相應(yīng)的DataInputStream對象*由Socket對象得到輸出流,并構(gòu)造DataOutputStream對象*/ socket=newSocket("localhost",4331); in=newDataInputStream(socket.getInputStream()); out=newDataOutputStream(socket.getOutputStream()); }catch(IOExceptione){}/**創(chuàng)建線程,負(fù)責(zé)接收服務(wù)器信息*/ if(thread==null){thread=newThread(this);thread.start();} } /**線程調(diào)用方法,負(fù)責(zé)接收運(yùn)算結(jié)果*/ publicvoidrun(){ Strings=null; while(true) { try{//利用Socket輸入流對象接收服務(wù)器運(yùn)算結(jié)果,在讀取到信息之前處于堵塞狀態(tài)。 s=in.readUTF(); b_result.setText(s); }catch(IOExceptione){b_result.setText("與服務(wù)器已斷開"); break;} } } /**響應(yīng)動作事件,負(fù)責(zé)將數(shù)據(jù)發(fā)送到服務(wù)器端*/ publicvoidactionPerformed(ActionEvente) { if(e.getSource()==b_calc) { Strings=b_edge.getText(); if(s!=null) {try {//將三角形的邊長數(shù)據(jù)通過Socket輸出流對象發(fā)送到服務(wù)器 out.writeUTF(s); } catch(IOExceptione1){} } } }}/**對應(yīng)的HTML文檔:*<APPLETCODE=Cal_Client.classWIDTH=500HEIGHT=500>*</APPLET>*/ /**服務(wù)器端代碼:Cal_Server.java**/importjava.io.*;import.*;importjava.util.*;publicclassCal_Server{ publicstaticvoidmain(Stringargs[]){ ServerSocketserver=null; Server_threadthread; Socketyou=null; while(true) { try { server=newServerSocket(4331); } catch(IOExceptione1){System.out.println("正在監(jiān)聽");//ServerSocket對象不能重復(fù)創(chuàng)建} try { you=server.accept(); System.out.println("客戶的地址:"+you.getInetAddress()); } catch(IOExceptione){System.out.println("正在等待客戶"); } if(you!=null){newServer_thread(you).start();//為每個客戶啟動一個專門的線程} else{continue; } } }}/**線程類,負(fù)責(zé)計(jì)算面積工作**/classServer_threadextendsThread{ Socketsocket=null; //連接套接字 DataInputStreamin=null; //數(shù)據(jù)輸入流 DataOutputStreamout=null; //數(shù)據(jù)輸出流 Strings=null; //待發(fā)送的運(yùn)算結(jié)果 /**利用Cal_Server類中的Socket對象構(gòu)造線程對象**/ publicServer_thread(Sockett){ socket=t; try{ //由Socket對象得到輸入流,并構(gòu)造相應(yīng)的DataInputStream對象 in=newDataInputStream(socket.getInputStream()); //由Socket對象得到輸出流,并構(gòu)造DataOutputStream對象 out=newDataOutputStream(socket.getOutputStream()); } catch(IOExceptione){ } } /**線程調(diào)用方法,負(fù)責(zé)計(jì)算面積,并將計(jì)算結(jié)果發(fā)給客戶端**/ publicvoidrun(){ while(true) { doublea[]=newdouble[3]; inti=0; try{ /**利用Socket輸入流對象接收客戶端傳來的邊長參數(shù),*在讀取到信息之前處于堵塞狀態(tài)*/ s=in.readUTF(); /**判斷、分析傳來的參數(shù),若格式錯誤則向客戶端提示錯誤, *正確則進(jìn)行面積計(jì)算并返回給客戶端 */ StringTokenizerfenxi=newStringTokenizer(s,","); while(fenxi.hasMoreTokens()) { Stringtemp=fenxi.nextToken(); try { a[i]=Double.valueOf(temp).doubleValue(); i++; } catch(NumberFormatExceptione) {out.writeUTF("請輸入數(shù)字字符");} } doublep=(a[0]+a[1]+a[2])/2.0; /**將面積通過Socket輸出流對象發(fā)送給客戶端*/ out.writeUTF("" +Math.sqrt(p*(p-a[0])*(p-a[1])*(p-a[2]))); sleep(2); } catch(InterruptedExceptione){ } catch(IOExceptione){System.out.println("客戶離開");break;} }//while語句結(jié)束 }//方法結(jié)束}【程序運(yùn)行結(jié)果】程序運(yùn)行結(jié)果如圖14-4、圖14-5所示。圖14-4例14.4客戶端程序運(yùn)行結(jié)果圖14-5例14.4服務(wù)器端程序運(yùn)行結(jié)果14.3InetAddress類.InetAddress類封裝了IP地址。該類的聲明格式如下:publicfinalclassInetAddressextendsobjectimplementsSerializable該類里有兩個成員變量:hostName(數(shù)據(jù)類型是String)和address(數(shù)據(jù)類型是int),即主機(jī)名和IP地址。這兩個成員變量是的訪問權(quán)限是私有的(private)。14.3.1InetAddress類1.獲取InetAddress對象InetAddress類沒有構(gòu)造方法。可以通過該類的類方法獲取其實(shí)例。(1)publicstaticInetAddressgetLocalHost()該方法獲得本地機(jī)器的InetAddress對象,當(dāng)查找不到本地機(jī)器的地址時將拋出一個UnknownHostException異常。示范代碼如下:try{InetAddressaddress=InetAddress.getLocalHost();…//其他處理代碼}catch(UnknownExceptione){…//異常處理代碼}(2)publicstaticInetAddressgetByName(Stringhost)該方法獲得host(計(jì)算機(jī)的域名)指定的InetAddress對象。如果找不到主機(jī)將觸發(fā)UnknownHostException異常。示范代碼如下:try{InetAddressaddress=InetAddress.getByName(host);…//其他處理代碼}catch(UnknownExceptione){…//異常處理代碼}(3)publicstaticInetAddress[]getAllByName(Stringhost)該方法把網(wǎng)絡(luò)上的一組計(jì)算機(jī)的InetAddress對象保存在數(shù)組中。出錯了同樣會拋出UnknownException異常。示范代碼如下:try{InetAddressaddress=InetAddress.getAllByName(host);…//其他處理代碼}catch(UnknownExceptione){…//異常處理代碼}提示:InteAddress類有一個getAddress()方法,該方法將IP地址以網(wǎng)絡(luò)字節(jié)順序作為字節(jié)數(shù)組返回。當(dāng)前IP只有4個字節(jié),但是當(dāng)實(shí)行IPV6時就有6個字節(jié)了。如果需要知道數(shù)組的長度,可以使用數(shù)組的length字段。getAddress()方法的一般性用法如下:InetAddressinetaddress=InetAddress.getLocalHost();byte[]address=inetaddress.getAddress();2.InetAddress類的實(shí)用方法(1)publicStringgetHostName()該方法返回主機(jī)名(一個字符串)。如果要查詢的機(jī)器沒有主機(jī)名,則該方法就返回主機(jī)的IP地址。一般的使用格式如下:InetAddressinetadd=InetAddress.getLocalHost();Stringlocalname=inetadd.getHostName();(2)publicStringgetHostAddress()該方法返回主機(jī)的IP地址(字符串格式)。(3)publicStringtoString()該方法返回主機(jī)名和IP地址(字符串格式),其具體形式為“主機(jī)名/點(diǎn)分地址”。如果一個InetAddress對象沒有主機(jī)名,則返回IP地址(字符串格式)。14.3.2InetAddress類的應(yīng)用【例14.5】查詢IP地址是IPV4還是IPV6,以及地址的類型(A,B,C,D)。程序名:Example14_5.java【程序源代碼】import.*;importjava.io.*;publicclassExample14_5{ publicstaticvoidmain(Stringargs[]){ try{ //獲得本地IP地址 InetAddressinetadd=InetAddress.getLocalHost(); //將IP地址以網(wǎng)絡(luò)字節(jié)順序作為字節(jié)數(shù)組返回 byte[]address=inetadd.getAddress(); if(address.length==4){ System.out.println("Theipversionisipv4"); intfirstbyte=address[0]; /** *由于返回的byte[]字節(jié)是無符號的,但是Java沒有無符號字節(jié)的基本數(shù) *據(jù)類型,因此如果要對返回的字節(jié)進(jìn)行操作,必須要將int進(jìn)行適當(dāng)?shù)恼{(diào)整 */ if(firstbyte<0) firstbyte+=256; if((firstbyte&0x80)==0)//firstbyte<=126,對應(yīng)IP為A類地址 System.out.println("theipclassisA"); //128<=firstbyte<=191,對應(yīng)IP為B類地址 elseif((firstbyte&0xC0)==0x80) System.out.println("TheipclassisB"); //192<=firstbyte<=223,對應(yīng)IP為C類地址 elseif((firstbyte&0xE0)==0xC0) System.out.println("TheipclassisC"); //224<=firstbyte<=239,對應(yīng)IP為D類地址 elseif((firstbyte&0xF0)==0xE0) System.out.println("TheipclassisD"); //240<=firstbyte<=255,對應(yīng)IP為E類地址 elseif((firstbyte&0xF8)==0xF0) System.out.println("TheipclassisE"); }elseif(address.length==16) System.out.println("Theipversionisipv6"); }catch(Exceptione){} }}【程序運(yùn)行結(jié)果】Theipversionisipv4TheipclassisC【例14.6】獲取域名是和的主機(jī)域名及ip地址程序名:Example14_6【程序源代碼】import.*;publicclassExample14_6{ publicstaticvoidmain(Stringargs[]) { try { InetAddressaddress_1=InetAddress.getByName(""); System.out.println(address_1.toString()); InetAddressaddress_2=InetAddress.getByName(""); System.out.println(address_2.toString()); } catch(Exceptione){ System.out.println("無法找到"); } }}14.4UDP數(shù)據(jù)報在TCP/IP協(xié)議包含TCP協(xié)議和UDP協(xié)議。相對而言,UDP的應(yīng)用不如TCP廣泛,幾個標(biāo)準(zhǔn)的應(yīng)用層協(xié)議HTTP、FTP、SMTP等使用的都是TCP協(xié)議。但是,隨著計(jì)算機(jī)網(wǎng)絡(luò)的發(fā)展,UDP協(xié)議正越來越顯示出其威力,尤其是在需要很強(qiáng)的實(shí)時交互性的場合,如網(wǎng)絡(luò)游戲、視頻會議等,UDP更是顯示出極強(qiáng)的威力。下面就來介紹Java環(huán)境下如何實(shí)現(xiàn)UDP網(wǎng)絡(luò)傳輸。14.4.1什么是Datagram所謂數(shù)據(jù)報(Datagram)就像日常生活中的郵件系統(tǒng)一樣,不能確??煽康丶牡?;而面向鏈接的TCP就好比是電話,雙方能肯定對方接收到了信息。TCP和UDP的區(qū)別:TCP可靠,傳輸大小無限制,但是需要時間建立連接,差錯控制開銷大;UDP不可靠,差錯控制開銷較小,傳輸大小限制在64KB以下,不需要建立連接。14.4.2Datagram通信包中提供了兩個類(DatagramSocket和DatagramPacket)支持?jǐn)?shù)據(jù)報通信。其中,DatagramSocket用于在程序之間建立通信連接;DatagramPacket則用來表示一個數(shù)據(jù)報。1.DatagramSocket的構(gòu)造方法DatagramSocket();//構(gòu)造方法調(diào)用時都要拋出SocketException異常。DatagramSocket(intprot);構(gòu)造方法調(diào)用時都要拋出SocketException異常。DatagramSocket(intport,InetAddressladdr);。其中,port指明Socket所使用的端口號,如果未指明端口號,則把Socket連接到本地主機(jī)上一個可用的端口;laddr指明一個可用的本地地址。給出端口號時要保證不發(fā)生端口沖突,否則會拋出SocketException異常。使用時的格式如下:try{DatagramSocketds1=DatagramSocekt();DatagramSocektds2=DatagramSocket(5678);DatagramSocektds3=DatagramSocket(5678,InetAddress.getByName(localhost).);…//其他處理代碼}catch(SocketExceptione){…//異常處理代碼}用數(shù)據(jù)報方式編寫Client/Server程序時,無論在客戶端還是服務(wù)器端,首先都要建立一個DatagramSocket對象,用來接收或發(fā)送數(shù)據(jù)報,然后使用DatagramPacket對象作為傳輸數(shù)據(jù)的載體。2.DatagramPacket的構(gòu)造方法DatagramPacket(bytebuf[],intlength);。DatagramPacket(bytebuf[],intlength,InetAddressaddr,intport);。DatagramPacket(byte[]buf,intoffset,intlength);。DatagramPacket(byte[]buf,intoffset,intlength,InetAddressaddress,intport);。其中,buf中存放數(shù)據(jù)報數(shù)據(jù),length為數(shù)據(jù)報中數(shù)據(jù)的長度,addr和port指明目的地址,offset指明了數(shù)據(jù)報的位移量。3.基于UDP的通信模式(1)將數(shù)據(jù)打包(稱為數(shù)據(jù)包),就像將信件裝入信封一樣,然后將數(shù)據(jù)包發(fā)往目的地。在發(fā)送數(shù)據(jù)包前,先要創(chuàng)建一個DatagramPacket對象,這時就要用到上文介紹的構(gòu)造方法Datagrampacker(bytebuf[],intlength,InetAddressaddr,intport);在給出存放發(fā)送數(shù)據(jù)的緩沖區(qū)的同時,還要給出完整的目的地址,包括IP地址和端口號。發(fā)送數(shù)據(jù)是通過DatagramSocket的方法send()實(shí)現(xiàn)的。send()根據(jù)數(shù)據(jù)報的目的地址來尋徑,以傳遞數(shù)據(jù)報。發(fā)送數(shù)據(jù)報的格式如下:try{InetAddressaddress=InetAddress.getByName("localhost");DatagramPacketdata_pack=newDatagramPacket(buffer,buffer.length,address,888);DatagramSocketmail_data=newDatagramSocket();mail_data.send(data_pack);//利用send()方法將指定的數(shù)據(jù)從端口888處發(fā)送出去…//其他處理代碼}catch(Exceptione){//異常處理代碼}(2)接收別人發(fā)來的數(shù)據(jù)包,好比接收信件一樣,然后查看數(shù)據(jù)包中的內(nèi)容。在接收數(shù)據(jù)前,應(yīng)該采用上面介紹的Datagrampacker(bytebuf[],intlength)方法創(chuàng)建一個DatagramPacket對象,給出接收數(shù)據(jù)的緩沖區(qū)及其長度。然后調(diào)用DatagramSocket的方法receive()等待數(shù)據(jù)報的到來。receive()將一直等待,直到收到一個數(shù)據(jù)報為止。接收數(shù)據(jù)包的格式如下:try{DatagramSocketmail_data=newDatagramSocket(666);//從端口666處接收數(shù)據(jù)DatagramPacketdata_pack=newDatagramPacket(data,data.length);//data為指定接收數(shù)據(jù)的字節(jié)數(shù)組mail_data.receive(data_pack);//利用receive()方法等待接收數(shù)據(jù)…//其他處理代碼}catch(Exceptione){//異常處理代碼}14.4.3UDP數(shù)據(jù)報的應(yīng)用【例14.7】利用UDP實(shí)現(xiàn)網(wǎng)絡(luò)通信。程序名:UDP_ME.Java&&UDP_YOU.java【程序說明】本例通過UDP數(shù)據(jù)報實(shí)現(xiàn)網(wǎng)絡(luò)通信。為了方便測試,本例的通信雙方都設(shè)置在本地主機(jī)上(同一臺機(jī)器)。讀者可以根據(jù)實(shí)際情況調(diào)整DatagramPacket對象,實(shí)現(xiàn)在不同主機(jī)之間進(jìn)行通信?!境绦蛟创a】/**UDP_ME端代碼:UDP_ME.Java*/packageudp;//類包import.*;importjava.io.*;importjava.awt.*;importjava.awt.event.*;importjavax.swing.*;importjavax.swing.event.*;publicclassUDP_MEextendsJFrame{ publicUDP_ME(){ Chat_mecs=newChat_me(); getContentPane().add(cs); } publicstaticvoidmain(String[]args){ UDP_MEframe=newUDP_ME(); frame.setTitle("梁山伯界面"); frame.setSize(510,500); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }}classChat_meextendsJPanelimplementsRunnable,ActionListener{ JButtonsend;//發(fā)送按鈕 TextAreamsg_show;//信息顯示框 TextFieldmsg_send;//信息發(fā)送框 Threadthread=null;//負(fù)責(zé)接收數(shù)據(jù)的線程 /**初始化界面**/ publicChat_me(){ setLayout(null); send=newJButton("發(fā)送"); msg_show=newTextArea(); msg_send=newTextField(); JLabeljl=newJLabel("信息顯示區(qū)"); jl.setBounds(0,0,500,20); add(jl); /**添加信息顯示框到面板上*/ msg_show.setBounds(0,20,500,300); msg_show.setEditable(false); add(msg_show); JLabeljl1=newJLabel("信息發(fā)送區(qū)"); jl1.setBounds(0,320,500,20); add(jl1); /**添加信息發(fā)送框到面板上*/ msg_send.setBounds(0,340,400,80); add(msg_send); msg_send.addActionListener(this);//為信息發(fā)送框設(shè)置監(jiān)聽器 /**添加發(fā)送按鈕到面板上*/ send.setBounds(400,360,100,40); add(send); send.addActionListener(this);//為發(fā)送按鈕設(shè)置監(jiān)聽器 /**建立并啟動線程,負(fù)責(zé)接收數(shù)據(jù)*/ thread=newThread(this); thread.start(); } /**實(shí)現(xiàn)監(jiān)聽事件*/ publicvoidactionPerformed(ActionEvente){ if(e.getSource()==msg_send||e.getSource()==send){//外層if語句開始 if(msg_send.getText()!=""){//內(nèi)層if開始 //將要發(fā)送的數(shù)據(jù)字符串轉(zhuǎn)換為字節(jié)數(shù)組 bytebuffer[]=msg_send.getText().trim().getBytes(); try{ //獲取本機(jī)IP地址對象 InetAddressaddress=InetAddress.getByName("localhost"); //發(fā)送數(shù)據(jù)的數(shù)據(jù)包,其目標(biāo)端口是3441,接收方需在這個端口接收 //創(chuàng)建發(fā)送數(shù)據(jù)報的套接字 DatagramSocketmail_data=newDatagramSocket(); /**ParagramPacket對象方法調(diào)用*/ msg_show.append("數(shù)據(jù)報目標(biāo)主機(jī)地址:"+data_pack.getAddress()+"\n"); msg_show.append("數(shù)據(jù)報目標(biāo)端口是:"+data_pack.getPort()+"\n"); msg_show.append("數(shù)據(jù)報長度:"+data_pack.getLength()+"\n"); msg_show.append("梁山伯說:"+msg_send.getText().trim()+"\n"); msg_send.getText(); mail_data.send(data_pack);//發(fā)送數(shù)據(jù)報 }catch(Exceptionex){ } }//內(nèi)層if結(jié)束 }//外層if結(jié)束 }//方法定義結(jié)束 /**線程調(diào)用方法,負(fù)責(zé)數(shù)據(jù)接收*/ publicvoidrun(){ DatagramSocketmail_data=null;//接收數(shù)據(jù)包的套接字 bytedata[]=newbyte[8192];//存放接收數(shù)據(jù)的字節(jié)數(shù)組 DatagramPacketpack=null;//接收數(shù)據(jù)的數(shù)據(jù)報對象 try{ pack=newDatagramPacket(data,data.length); //使用端口3445來接收數(shù)據(jù)包(因?yàn)閷Ψ桨l(fā)來的數(shù)據(jù)報的目標(biāo)端口是3445) mail_data=newDatagramSocket(3445); }catch(Exceptione){ } /**利用循環(huán)不斷接收數(shù)據(jù)*/ while(true){ if(mail_data==null) break; else try{ mail_data.receive(pack);//接收數(shù)據(jù)包 /**處理接收到的數(shù)據(jù), *獲取收到的數(shù)據(jù)的實(shí)際長度, *獲取收到的數(shù)據(jù)包的始發(fā)地址, *獲取收到的數(shù)據(jù)包的始發(fā)端口。 */ intlength=pack.getLength(); InetAddressadress=pack.getAddress(); intport=pack.getPort(); Stringmessage=newString(pack.getData(),0,length); msg_show.append("收到數(shù)據(jù)長度"+length+"\n"); msg_show.append("收到數(shù)據(jù)來自"+adress+"端口"+port+"\n"); //將接收到的數(shù)據(jù)顯示在信息顯示框中 msg_show.append("祝英臺說:"+message+"\n"); }catch(Exceptione){ } } }}/**UDP_YOU端代碼:UDP_YOU.java**/packageudp;//類包import.*;importjava.io.*;importjava.awt.*;importjava.awt.event.*;importjavax.swing.*;importjavax.swing.event.*; publicclassUDP_YOUextendsJFrame{ publicUDP_YOU(){ Chat_youcs=newChat_you(); getContentPane().add(cs); } publicstaticvoidmain(String[]args){ UDP_YOUframe=newUDP_YOU(); frame.setTitle("祝英臺界面"); frame.setSize(510,500); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }}classChat_youextendsJPanelimplementsRunnable,ActionListener{ JButtonsend;//發(fā)送按鈕 TextAreamsg_show;//信息顯示框 TextFieldmsg_send;//信息發(fā)送框 Threadthread=null;//負(fù)責(zé)接收數(shù)據(jù)的線程 /**下面初始化界面*/ publicChat_you(){ setLayout(null); send=newJButton("發(fā)送"); msg_show=newTextArea(); msg_send=newTextField(); JLabeljl=newJLabel("信息顯示區(qū)"); jl.setBounds(0,0,500,20); add(jl); /**添加信息顯示框到面板上*/ msg_show.setBounds(0,20,500,300); msg_show.setEditable(false); add(msg_show); JLabeljl1=newJLabel("信息發(fā)送區(qū)"); jl1.setBounds(0,320,500,20); add(jl1); /**添加信息發(fā)送框到面板上*/ msg_send.setBounds(0,340,400,80); add(msg_send); msg_send.addActionListener(this);//為信息發(fā)送框設(shè)置監(jiān)聽器 /**添加發(fā)送按鈕到面板上*/ send.setBounds(400,360,100,40); add(send); send.addActionListener(this);//為發(fā)送按鈕設(shè)置監(jiān)聽器 /**建立并啟動線程,負(fù)責(zé)接收數(shù)據(jù)*/ thread=newThread(this); thread.start(); } /**實(shí)現(xiàn)監(jiān)聽事件*/ publicvoidactionPerformed(ActionEvente){ if(e.getSource()==msg_send||e.getSource()==send){ if(msg_send.getText()!=""){ //將要發(fā)送的數(shù)據(jù)字符串轉(zhuǎn)換為字節(jié)數(shù)組 bytebuffer[]=msg_send.getText().trim().getBytes(); try{ //獲取本機(jī)IP地址對象 InetAddressaddress=InetAddress.getByName("localhost"); //發(fā)送數(shù)據(jù)的數(shù)據(jù)包,其目標(biāo)端口是3445,接收方需在這個端口接收 DatagramPacketdata_pack=newDatagramPacket(buffer, buffer.length,address,3445); //創(chuàng)建發(fā)送數(shù)據(jù)報的套接字 DatagramSocketmail_data=newDatagramSocket(); /**ParagramPacket對象方法調(diào)用*/ msg_show.append("數(shù)據(jù)報目標(biāo)主機(jī)地址:"+data_pack.getAddress()+"\n"); msg_show.append("數(shù)據(jù)報目標(biāo)端口是:"+data_pack.getPort()+"\n"); msg_show.append("數(shù)據(jù)報長度:"+data_pack.getLength()+"\n"); msg_show.append("祝英臺說:"+msg_send.getText().trim()+"\n"); msg_send.setText(null); mail_data.send(data_pack);//發(fā)送數(shù)據(jù)報 }catch(Exceptionex){ } } } } /**線程調(diào)用方法,負(fù)責(zé)數(shù)據(jù)接收*/ publicvoidrun(){ DatagramSocketmail_data=null;//接收數(shù)據(jù)包的套接字 bytedata[]=newbyte[8192];//存放接收數(shù)據(jù)的字節(jié)數(shù)組 DatagramPacketpack=null;//接收數(shù)據(jù)的數(shù)據(jù)報對象 try{ pack=newDatagramPacket(data,data.length); //使用端口3441來接收數(shù)據(jù)包(因?yàn)閷Ψ桨l(fā)來的數(shù)據(jù)報的目標(biāo)端口是3441) mail_data=newDatagramSocket(3441); }catch(Exceptione){ } /**利用循環(huán)不斷接收數(shù)據(jù)*/ while(true){ if(mail_data==null) break; else try{ mail_data.receive(pack);//接收數(shù)據(jù)包 /**處理接收到的信息, *獲取收到的數(shù)據(jù)的實(shí)際長度, *獲取收到的數(shù)據(jù)包的始發(fā)地址, *獲取收到的數(shù)據(jù)包的始發(fā)端口, *將數(shù)據(jù)轉(zhuǎn)換為字符串。 */ intlength=pack.getLength(); InetAddressadress=pack.getAddress(); intport=pack.getPort(); Stringmessage=newString(pack.getData(),0,length); msg_show.append("收到數(shù)據(jù)長度"+length+"\n"); msg_show.append("收到數(shù)據(jù)來自"+adress+"端口"+port+"\n"); //將接收到的數(shù)據(jù)顯示在信息顯示框中 msg_show.append("梁山伯說:"+message+"\n"); }catch(Exceptione){ msg_show.append("對方已斷開連接"); } } }}將上面兩個程序放在UDP目錄下編譯,然后,執(zhí)行服務(wù)器和客戶程序。14.5廣播數(shù)據(jù)報14.5.1廣播數(shù)據(jù)報概要廣播數(shù)據(jù)報類似于電臺廣播。進(jìn)行廣播的電臺需在指定的波段和頻率上廣播信息,接收者只有將收音機(jī)調(diào)到指定的波段、頻率上才能收聽到廣播的內(nèi)容。廣播數(shù)據(jù)報涉及到地址和端口。Internet的地址是以a.b.c.d的格式給出,該地址的一部分代表用戶自己的主機(jī),而另一部分代表用戶所在的網(wǎng)絡(luò)。當(dāng)a小于128,那么b.c.d就用來表示主機(jī),這類地址稱為A類地址;如果a大于等于128并且小于192,則a.b表示網(wǎng)絡(luò)地址,而c.d表示主機(jī)地址,這類地址稱為B類地址;如果a大于或等于192,小于224則網(wǎng)絡(luò)地址是a.b.c。而d表示主機(jī)地址,這類地址稱為C類地址;與55是保留地址,稱為D類地址。廣播或接收廣播的主機(jī)都必須加入到同一個D類地址。一個D類地址也稱為一個廣播組。加入到同一個廣播組的主機(jī)可以在某個端口上廣播信息,也可以在某個端口號上接收信息。14.5.2MultiCastSocket類多播數(shù)據(jù)報套接字用于發(fā)送和接收IP多播數(shù)據(jù)包。MulticastSocket是一種(UDP)DatagramSocket。它具有加入Internet上其他多播主機(jī)所屬“組”的功能(多播組通過D類IP地址和標(biāo)準(zhǔn)UDP端口號指定)。D類IP地址的范圍是~55(包括兩者)。地址被保留(不許使用)。首先使用所需端口創(chuàng)建MulticastSocket對象,然后調(diào)用joinGroup(InetAddressgroupAddr)方法來加入多播組。1.MulticastSocket的構(gòu)造方法(1)MulticastSocket():創(chuàng)建多播套接字。(2)MulticastSocket(intport):創(chuàng)建多播套接字,并將其綁定到特定端口。(3)MulticastSocket(SocketAddressbindaddr):創(chuàng)建綁定到指定套接字地址的MulticastSocket對象。這3個構(gòu)造方法都會拋出IOException異?;騍ecurityException異常,所以必須在try-catch結(jié)構(gòu)中創(chuàng)建MulticastSocket對象。2.MulticastSocket的實(shí)用方法publici

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論