




已閱讀5頁(yè),還剩18頁(yè)未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
網(wǎng)絡(luò)編程的基本概念, TCP/IP協(xié)議簡(jiǎn)介 8.1.1 網(wǎng)絡(luò)基礎(chǔ)知識(shí) 計(jì)算機(jī)網(wǎng)絡(luò)形式多樣,內(nèi)容繁雜。網(wǎng)絡(luò)上的計(jì)算機(jī)要互相通信,必須遵循一定的協(xié)議。目前使用最廣泛的網(wǎng)絡(luò)協(xié)議是 Internet 上所使用的 TCP/IP 協(xié)議。 網(wǎng)絡(luò)編程的目的就是指直接或間接地通過(guò)網(wǎng)絡(luò)協(xié)議與其他計(jì)算機(jī)進(jìn)行通訊。網(wǎng)絡(luò)編程中有兩個(gè)主要的問(wèn)題,一個(gè)是如何準(zhǔn)確的定位網(wǎng)絡(luò)上一臺(tái)或多臺(tái)主機(jī),另一個(gè)就是找到主機(jī)后如何可靠高效的進(jìn)行數(shù)據(jù)傳輸。在 TCP/IP 協(xié)議中 IP 層主要負(fù)責(zé)網(wǎng)絡(luò)主機(jī)的定位,數(shù)據(jù)傳輸?shù)穆酚?,?IP 地址可以唯一地確定 Internet 上 的一臺(tái)主機(jī)。而 TCP 層則提供面向應(yīng)用的可靠的或非可靠的數(shù)據(jù)傳輸機(jī)制,這是網(wǎng)絡(luò)編程的主要對(duì)象,一般不需要關(guān)心 IP 層是如何處理數(shù)據(jù)的。 目前較為流行的網(wǎng)絡(luò)編程模型是客戶機(jī) /服務(wù)器( C/S)結(jié)構(gòu)。即通信雙方一方作為服務(wù)器等待客戶提出請(qǐng)求并予以響應(yīng)??蛻魟t在需要服務(wù)時(shí)向服務(wù)器提出申請(qǐng)。服務(wù)器一般作為守護(hù)進(jìn)程始終運(yùn)行,監(jiān)聽網(wǎng)絡(luò)端口,一旦有客戶請(qǐng)求,就會(huì)啟動(dòng)一個(gè)服務(wù)進(jìn)程來(lái)響應(yīng)該客戶,同時(shí)自己繼續(xù)監(jiān)聽服務(wù)端口,使后來(lái)的客戶也能及時(shí)得到服務(wù)。 8.1.2網(wǎng)絡(luò)基本概念 IP 地址:標(biāo)識(shí)計(jì)算機(jī)等網(wǎng)絡(luò)設(shè)備的網(wǎng)絡(luò)地址,由 四個(gè) 8 位的二進(jìn)制數(shù)組成,中間以小數(shù)點(diǎn)分隔。 如: , 0 主機(jī)名 (hostname):網(wǎng)絡(luò)地址的助記名,按照域名進(jìn)行分級(jí)管理。 如: 端口號(hào) (port number):網(wǎng)絡(luò)通信時(shí)同一機(jī)器上的不同進(jìn)程的標(biāo)識(shí)。 如 :80, 21, 23, 25,其中 11024 為系統(tǒng)保留的端口號(hào) 服務(wù)類型 (service):網(wǎng)絡(luò)的各種服務(wù)。 http, telnet, ftp, smtp 我們可以用以下的一幅圖來(lái)描述這里我們所提到的幾個(gè)概念: 在 Internet 上 IP 地址和主機(jī)名是一一對(duì)應(yīng)的,通過(guò)域名解析可以由主機(jī)名得到機(jī)器的IP,由于機(jī)器名更接近自然語(yǔ)言,容易記憶,所以使用比 IP 地址廣泛,但是對(duì)機(jī)器而言只有 IP 地址才是有效的標(biāo)識(shí)符。 通常一臺(tái)主機(jī)上總是有很多個(gè)進(jìn)程需要網(wǎng)絡(luò)資源進(jìn)行網(wǎng)絡(luò)通訊 。網(wǎng)絡(luò)通訊的對(duì)象準(zhǔn)確的講不是主機(jī),而應(yīng)該是主機(jī)中運(yùn)行的進(jìn)程。這時(shí)候光有主機(jī)名或 IP 地址來(lái)標(biāo)識(shí)這么多個(gè)進(jìn)程顯然是不夠的。端口號(hào)就是為了在一臺(tái)主機(jī)上提供更多的網(wǎng)絡(luò)資源而采取得一種手段,也是 TCP 層提供的一種機(jī)制。只有通過(guò)主機(jī)名或 IP 地址和端口號(hào)的組合才能唯一的確定網(wǎng)絡(luò)通訊中的對(duì)象:進(jìn)程。 服務(wù)類型是在 TCP 層上面的應(yīng)用層的概念?;?TCP/IP 協(xié)議可以構(gòu)建出各種復(fù)雜的應(yīng)用,服務(wù)類型是那些已經(jīng)被標(biāo)準(zhǔn)化了的應(yīng)用,一般都是網(wǎng)絡(luò)服務(wù)器(軟件)。讀者可以編寫自己的基于網(wǎng)絡(luò)的服務(wù)器,但都不能被稱作標(biāo)準(zhǔn)的服務(wù)類型。 8.1.3兩類傳輸協(xié)議: TCP; UDP 盡管 TCP/IP 協(xié)議的名稱中只有 TCP 這個(gè)協(xié)議名,但是在 TCP/IP 的傳輸層同時(shí)存在 TCP 和 UDP 兩個(gè)協(xié)議。 TCP是 Tranfer Control Protocol 的簡(jiǎn)稱,是一種面向連接的保證可靠傳輸?shù)膮f(xié)議。通過(guò) TCP協(xié)議傳輸,得到的是一個(gè)順序的無(wú)差錯(cuò)的數(shù)據(jù)流。發(fā)送方和接收方的成對(duì)的兩個(gè) socket 之間必須建立連接,以便在 TCP 協(xié)議的基礎(chǔ)上進(jìn)行通信,當(dāng)一個(gè) socket(通常都是 server socket)等待建立連接時(shí),另一個(gè) socket 可以要求進(jìn)行連接,一旦這兩個(gè) socket 連接起來(lái),它們就可以進(jìn)行雙向數(shù)據(jù)傳輸,雙方都可以進(jìn)行發(fā)送或接收操作。 UDP是 User Datagram Protocol 的簡(jiǎn)稱,是一種無(wú)連接的協(xié)議,每個(gè)數(shù)據(jù)報(bào)都是一個(gè)獨(dú)立的信息,包括完整的源地址或目的地址,它在網(wǎng)絡(luò)上以任何可能的路徑傳往目的地,因此能否到達(dá)目的地,到達(dá)目的地的時(shí)間以及內(nèi)容的正確性都是不能被保證的。 下面我們對(duì)這兩種協(xié)議做簡(jiǎn)單比較: 使用 UDP 時(shí),每個(gè)數(shù)據(jù)報(bào)中都給出了完整的地址信息,因此無(wú)需要建立發(fā)送方和接收方的連接。對(duì)于 TCP 協(xié)議,由于它是一個(gè)面向連接的協(xié)議 ,在 socket 之間進(jìn)行數(shù)據(jù)傳輸之前必然要建立連接,所以在 TCP 中多了一個(gè)連接建立的時(shí)間。 使用 UDP 傳輸數(shù)據(jù)時(shí)是有大小限制的,每個(gè)被傳輸?shù)臄?shù)據(jù)報(bào)必須限定在 64KB 之內(nèi)。而 TCP 沒(méi)有這方面的限制,一旦連接建立起來(lái),雙方的 socket 就可以按統(tǒng)一的格式傳輸大量的數(shù)據(jù)。 UDP 是一個(gè)不可靠的協(xié)議,發(fā)送方所發(fā)送的數(shù)據(jù)報(bào)并不一定以相同的次序到達(dá)接收方。而 TCP 是一個(gè)可靠的協(xié)議,它確保接收方完全正確地獲取發(fā)送方所發(fā)送的全部數(shù)據(jù)。 總之, TCP 在網(wǎng)絡(luò)通信上有極強(qiáng)的生命力,例如遠(yuǎn)程連接( Telnet)和文件傳輸 ( FTP)都需要不定長(zhǎng)度的數(shù)據(jù)被可靠地傳輸。相比之下 UDP 操作簡(jiǎn)單,而且僅需要較少的監(jiān)護(hù),因此通常用于局域網(wǎng)高可靠性的分散系統(tǒng)中 client/server 應(yīng)用程序。 既然有了保證可靠傳輸?shù)?TCP 協(xié)議,為什么還要非可靠傳輸?shù)?UDP 協(xié)議呢?主要的原因有兩個(gè)。一是可靠的傳輸是要付出代價(jià)的,對(duì)數(shù)據(jù)內(nèi)容正確性的檢驗(yàn)必然占用計(jì)算機(jī)的處理時(shí)間和網(wǎng)絡(luò)的帶寬,因此 TCP 傳輸?shù)男什蝗?UDP 高。二是在許多應(yīng)用中并不需要保證嚴(yán)格的傳輸可靠性,比如視頻會(huì)議系統(tǒng),并不要求音頻視頻數(shù)據(jù)絕對(duì)的正確,只要保證連貫性就可以了,這種情況下顯 然使用 UDP 會(huì)更合理一些。 8.2 基于 URL 的高層次 Java 網(wǎng)絡(luò)編程 8.2.1一致資源定位器 URL URL(Uniform Resource Locator)是一致資源定位器的簡(jiǎn)稱,它表示 Internet 上某一資源的地址。通過(guò) URL 我們可以訪問(wèn) Internet 上的各種網(wǎng)絡(luò)資源,比如最常見的 WWW, FTP 站點(diǎn)。瀏覽器通過(guò)解析給定的 URL 可以在網(wǎng)絡(luò)上查找相應(yīng)的文件或其他資源。 URL 是最為直觀的一種網(wǎng)絡(luò)定位方法。使用 URL 符合人們的語(yǔ)言習(xí)慣,容易記憶,所以應(yīng)用十分廣泛。而且在目前使用最為廣泛的 TCP/IP 中對(duì)于 URL 中主機(jī)名的解析也是協(xié)議的一個(gè)標(biāo)準(zhǔn),即所謂的域名解析服務(wù)。使用 URL 進(jìn)行網(wǎng)絡(luò)編程,不需要對(duì)協(xié)議本身有太多的了解,功能也比較弱,相對(duì)而言是比較簡(jiǎn)單的,所以在這里我們先介紹在 Java 中如何使用 URL 進(jìn)行網(wǎng)絡(luò)編程來(lái)引導(dǎo)讀者入門。 8.2.2 URL 的組成 protocol:/resourceName 協(xié)議名( protocol)指明獲取資源所使用的傳輸協(xié)議,如 http、 ftp、 gopher、 file 等,資源名( resourceName)則應(yīng)該是資源的完整地址,包括主機(jī)名、端口號(hào)、文件名或 文件內(nèi)部的一個(gè)引用。例如: / 協(xié)議名 :/主機(jī)名 /home/welcome.html 協(xié)議名 :/機(jī)器名文件名 :80/Gamelan/network.html#BOTTOM 協(xié)議名 :/機(jī)器名端口號(hào)文件名內(nèi)部引用 端口號(hào)是和 Socket 編程相關(guān)的一個(gè)概念,初學(xué)者不必在此深究,在后面會(huì)有詳細(xì)講解。內(nèi)部引用是HTML 中的標(biāo)記,有興趣的讀者可以參考有關(guān) HTML 的書籍。 8.2.3 創(chuàng)建一個(gè) URL 為了表示 URL, 中實(shí)現(xiàn)了類 URL。我們可以通過(guò)下面的構(gòu)造方法來(lái)初始化一個(gè) URL 對(duì)象: ( 1) public URL (String spec); 通過(guò)一個(gè)表示 URL 地址的字符串可以構(gòu)造一個(gè) URL 對(duì)象。 URL urlBase=new URL(http:/www. 263.net/) ( 2) public URL(URL context, String spec); 通過(guò)基 URL 和相對(duì) URL 構(gòu)造一個(gè) URL 對(duì)象。 URL net263=new URL (/); URL index263=new URL(net263, index.html) ( 3) public URL(String protocol, String host, String file); new URL(http, , /pages/G. html); ( 4) public URL(String protocol, String host, int port, String file); URL gamelan=new URL(http, , 80, Pages/Gwork.html); 注意: 類 URL 的構(gòu)造方法都聲明拋棄非運(yùn)行時(shí)例外( MalformedURLException),因此生成 URL 對(duì)象時(shí),我們必須要對(duì)這一例外進(jìn)行處理,通常是用 try-catch 語(yǔ)句進(jìn)行捕獲。格式如下: try URL myURL= new URL() catch (MalformedURLException e) /exception handler code here 8.2.4 解析一個(gè) URL 一個(gè) URL 對(duì)象生成后,其屬性是不能被改變的,但是我們可以通過(guò)類 URL 所提供的方法來(lái)獲取這些屬性: public String getProtocol() 獲取該 URL 的協(xié)議名。 public String getHost() 獲取該 URL 的主機(jī)名。 public int getPort() 獲取該 URL 的端口號(hào),如果沒(méi)有設(shè)置端口,返回 -1。 public String getFile() 獲取該 URL 的文件名。 public String getRef() 獲取該 URL 在文件中的相對(duì)位置。 public String getQuery() 獲取該 URL 的查詢信息。 public String getPath() 獲取該 URL 的路徑 public String getAuthority() 獲取該 URL 的權(quán)限信息 public String getUserInfo() 獲得使用者的信息 public String getRef() 獲得該 URL 的錨 下面的例子中,我們生成一個(gè) URL 對(duì)象,并獲取它的各個(gè)屬性。 import .*; import java.io.*; public class ParseURL public static void main (String args) throws Exception URL Aurl=new URL(:80/docs/books/); URL tuto=new URL(Aurl,ro.html#DOWNLOADING); System.out.println(protocol=+ tuto.getProtocol(); System.out.println(host =+ tuto.getHost(); System.out.println(filename=+ tuto.getFile(); System.out.println(port=+ tuto.getPort(); System.out.println(ref=+tuto.getRef(); System.out.println(query=+tuto.getQuery(); System.out.println(path=+tuto.getPath(); System.out.println(UserInfo=+tuto.getUserInfo(); System.out.println(Authority=+tuto.getAuthority(); 執(zhí)行結(jié)果為: protocol=http host = filename=/docs/books/ro.html port=80 ref=DOWNLOADING query=null path=/docs/books/ro.html UserInfo=null Authority=:80 8.2.5 從 URL 讀取 WWW網(wǎng)絡(luò)資源 當(dāng)我們得到一個(gè) URL 對(duì)象后,就可以通過(guò)它讀取指定的 WWW 資源。這時(shí)我們將使用 URL的方法 openStream(),其定義為: InputStream openStream(); 方法 openSteam()與指定的 URL 建立連接并返回 InputStream 類的對(duì)象以從這一連接中讀取數(shù)據(jù)。 public class URLReader public static void main(String args) throws Exception /聲明拋出所有例外 URL tirc = new URL(/); /構(gòu)建一 URL 對(duì)象 BufferedReader in = new BufferedReader(new InputStreamReader(tirc.openStream(); /使用 openStream 得到一輸入流并由此構(gòu)造一個(gè) BufferedReader 對(duì)象 String inputLine; while (inputLine = in.readLine() != null) /從輸入流不斷的讀數(shù)據(jù),直到讀完為止 System.out.println(inputLine); /把讀入的數(shù)據(jù)打印到屏幕上 in.close(); /關(guān)閉輸入流 8.2.6 通過(guò) URLConnetction連接 WWW 通過(guò) URL 的方法 openStream(),我們只能從網(wǎng) 絡(luò)上讀取數(shù)據(jù),如果我們同時(shí)還想輸出數(shù)據(jù),例如向服務(wù)器端的 CGI 程序發(fā)送一些數(shù)據(jù),我們必須先與 URL 建立連接,然后才能對(duì)其進(jìn)行讀寫,這時(shí)就要用到類 URLConnection 了。 CGI 是公共網(wǎng)關(guān)接口( Common Gateway Interface)的簡(jiǎn)稱,它是用戶瀏覽器和服務(wù)器端的應(yīng)用程序進(jìn)行連接的接口,有關(guān) CGI 程序設(shè)計(jì),請(qǐng)讀者參考有關(guān)書籍。 類 URLConnection 也在包 中定義,它表示 Java 程序和 URL 在網(wǎng)絡(luò)上的通信連接。當(dāng)與一個(gè) URL 建立連接時(shí),首先要在一個(gè) URL 對(duì)象上通過(guò)方 法 openConnection()生成對(duì)應(yīng)的 URLConnection 對(duì) 象 。 例 如 下 面 的 程 序 段 首 先 生 成 一 個(gè) 指 向 地 址/index.shtml 的對(duì)象,然后用 openConnection()打開該 URL 對(duì)象上的一個(gè)連接,返回一個(gè) URLConnection 對(duì)象。如果連接過(guò)程失敗,將產(chǎn)生 IOException. Try URL netchinaren = new URL (/index.shtml); URLConnectonn tc = netchinaren.openConnection(); catch(MalformedURLException e) /創(chuàng)建 URL()對(duì)象失敗 catch (IOException e) /openConnection()失敗 類 URLConnection 提供了很多方法來(lái)設(shè)置或獲取連接參數(shù),程序設(shè)計(jì)時(shí)最常使用的是getInputStream()和 getOurputStream(),其定義為: InputSteram getInputSteram(); OutputSteram getOutputStream(); 通過(guò)返回的輸入 /輸出流我們可以與遠(yuǎn)程對(duì)象進(jìn)行通信??聪旅娴睦樱?URL url =new URL (/cgi-bin/backwards); /創(chuàng)建一 URL 對(duì)象 URLConnectin con=url.openConnection(); /由 URL 對(duì)象獲取 URLConnection 對(duì)象 DataInputStream dis=new DataInputStream (con.getInputSteam(); /由 URLConnection 獲取輸入流,并構(gòu)造 DataInputStream 對(duì)象 PrintStream ps=new PrintSteam(con.getOutupSteam(); /由 URLConnection 獲取輸出流,并構(gòu)造 PrintStream 對(duì)象 String line=dis.readLine(); /從服務(wù)器讀入一行 ps.println(client ); /向服務(wù)器寫出字符串 client 其中 backwards 為服務(wù)器端的 CGI 程序。實(shí)際上,類 URL 的方法 openSteam()是通過(guò) URLConnection 來(lái)實(shí)現(xiàn)的。它等價(jià)于 openConnection().getInputStream(); 基于 URL 的網(wǎng)絡(luò)編程在底層其實(shí)還是基于下面要講的 Socket 接口的。 WWW, FTP 等標(biāo)準(zhǔn)化的網(wǎng)絡(luò)服務(wù)都是基于 TCP 協(xié)議的,所以本質(zhì)上講 URL 編程也是基于 TCP 的一種應(yīng)用。 8.3 基于 Socket(套接字)的低層次 Java 網(wǎng)絡(luò)編程 8.3.1 Socket 通訊 網(wǎng)絡(luò)上的兩個(gè)程序通過(guò)一個(gè)雙向的通訊連接實(shí)現(xiàn)數(shù)據(jù)的交換,這個(gè)雙向鏈路的一端稱為一個(gè) Socket。 Socket 通常用來(lái)實(shí)現(xiàn)客戶方和服務(wù)方的連接。 Socket 是 TCP/IP 協(xié)議的一個(gè)十分流行的編程界面,一個(gè) Socket 由一個(gè) IP 地址和一個(gè)端口號(hào)唯一確定。 在傳統(tǒng)的 UNIX 環(huán)境下可以操作 TCP/IP 協(xié)議的接口不止 Socket 一個(gè), Socket 所支持的協(xié)議種類也不光 TCP/IP 一種,因此兩者之間是沒(méi)有必然聯(lián)系的。在 Java 環(huán)境下, Socket 編程主要是指基于 TCP/IP 協(xié)議的網(wǎng)絡(luò)編程。 說(shuō) Socket 編程是低層次網(wǎng)絡(luò)編程并不等于它功能不強(qiáng)大,恰恰相反,正因?yàn)閷哟蔚停琒ocket 編程比基于 URL 的網(wǎng)絡(luò)編程提供了更強(qiáng)大的功能和更靈活的控制,但是卻要更復(fù)雜一些。由于 Java 本身的特殊性, Socket 編程在 Java 中可能已經(jīng)是層次最低的網(wǎng)絡(luò)編程接口,在 Java 中要直接操作協(xié)議中更低的層次,需要使用 Java 的本地方法調(diào)用( JNI),在這里就不予討論了。 8.3.2 Socket 通訊的一般過(guò)程 前面已經(jīng)提到 Socket 通常用來(lái)實(shí)現(xiàn) C/S 結(jié)構(gòu)。 使用 Socket 進(jìn)行 Client/Server 程序設(shè)計(jì)的一般連接過(guò)程是這樣的: Server 端 Listen(監(jiān)聽 )某個(gè)端口是否有連接請(qǐng)求, Client 端向 Server 端發(fā)出 Connect(連接 )請(qǐng)求, Server 端向 Client端發(fā)回 Accept(接受)消息。一個(gè)連接就建立起來(lái)了。 Server 端和 Client 端都可以通過(guò) Send,Write 等方法與對(duì)方通信。 對(duì)于一個(gè)功能齊全的 Socket,都 要包含以下基本結(jié)構(gòu),其工作過(guò)程包含以下四個(gè)基本的步驟: ( 1) 創(chuàng)建 Socket; ( 2) 打開連接到 Socket 的輸入 /出流; ( 3) 按照一定的協(xié)議對(duì) Socket 進(jìn)行讀 /寫操作; ( 4) 關(guān)閉 Socket. 第三步是程序員用來(lái)調(diào)用 Socket 和實(shí)現(xiàn)程序功能的關(guān)鍵步驟,其他三步在各種程序中基本相同。 以上 4個(gè)步驟是針對(duì) TCP 傳輸而言的,使用 UDP 進(jìn)行傳輸時(shí)略有不同,在后面會(huì)有具體講解。 8.3.3 創(chuàng)建 Socket java 在包 中提供了兩個(gè)類 Socket 和 ServerSocket,分別用來(lái)表示雙向連接的客戶端和服務(wù)端。這是兩個(gè)封裝得非常好的類,使用很方便。其構(gòu)造方法如下: Socket(InetAddress address, int port); Socket(InetAddress address, int port, boolean stream); Socket(String host, int prot); Socket(String host, int prot, boolean stream); Socket(SocketImpl impl) Socket(String host, int port, InetAddress localAddr, int localPort) Socket(InetAddress address, int port, InetAddress localAddr, int localPort) ServerSocket(int port); ServerSocket(int port, int backlog); ServerSocket(int port, int backlog, InetAddress bindAddr) 其中 address、 host 和 port 分別是雙向連接中另一方的 IP 地址、主機(jī)名和端口號(hào), stream指明 socket 是流 socket 還是數(shù)據(jù)報(bào) socket, localPort 表示本地主機(jī)的端口號(hào), localAddr 和bindAddr 是本地機(jī)器的地址( ServerSocket 的主機(jī)地址), impl 是 socket 的父類,既可以用來(lái)創(chuàng)建 serverSocket 又可以用來(lái)創(chuàng)建 Socket。 count 則表示服務(wù)端所能支持的最大連接數(shù)。例如: Socket client = new Socket(127.0.01., 80); ServerSocket server = new ServerSocket(80); 注意,在選擇端口時(shí),必須小心。每一個(gè)端口提供一種特定的服務(wù),只有給出正確的端口,才能獲得相應(yīng)的服務(wù)。 01023的端口號(hào)為系統(tǒng)所保留,例如 http 服務(wù)的端口號(hào)為 80,telnet服務(wù)的端口號(hào)為 21,ftp 服務(wù)的端口號(hào)為 23, 所以我們?cè)谶x擇端口號(hào)時(shí),最好選擇一個(gè)大于1023的數(shù)以防止發(fā)生沖突。 在創(chuàng)建 socket 時(shí)如果發(fā)生錯(cuò)誤,將產(chǎn) 生 IOException,在程序中必須對(duì)之作出處理。所以在創(chuàng)建 Socket 或 ServerSocket 是必須捕獲或拋出例外。 8.3.4 客戶端的 Socket 下面是一個(gè)典型的創(chuàng)建客戶端 Socket 的過(guò)程。 try Socket socket=new Socket(,4700); /是 TCP/IP 協(xié)議中默認(rèn)的本機(jī)地址 catch(IOException e) System.out.println(Error:+e); 這是最簡(jiǎn)單的在客戶端創(chuàng)建一個(gè) Socket 的一個(gè)小程序段,也是使用 Socket 進(jìn)行網(wǎng)絡(luò)通訊的第一步,程序相當(dāng)簡(jiǎn)單,在這里不作過(guò)多解釋了。在后面的程序中會(huì)用到該小程序段。 8.3.5 服務(wù)器端的 ServerSocket 下面是一個(gè)典型的創(chuàng)建 Server 端 ServerSocket 的過(guò)程。 ServerSocket server=null; try server=new ServerSocket(4700); /創(chuàng)建一個(gè) ServerSocket 在端口 4700監(jiān) 聽客戶請(qǐng)求 catch(IOException e) System.out.println(can not listen to :+e); Socket socket=null; try socket=server.accept(); /accept()是一個(gè)阻塞的方法,一旦有客戶請(qǐng)求,它就會(huì)返回一個(gè) Socket 對(duì)象用于同客戶進(jìn)行交互 catch(IOException e) System.out.println(Error:+e); 以上的程序是 Server 的典型工作模式,只不過(guò)在這里 Server 只能接收一個(gè)請(qǐng)求,接受完后 Server 就退出了。實(shí)際的應(yīng)用中總是讓它不停的循環(huán)接收,一旦有客戶請(qǐng)求, Server 總是會(huì)創(chuàng)建一個(gè)服務(wù)線程來(lái)服務(wù)新來(lái)的客戶,而自己繼續(xù)監(jiān)聽。程序中 accept()是一個(gè)阻塞函數(shù),所謂阻塞性方法就是說(shuō)該方法被調(diào)用后,將等待客戶的請(qǐng)求,直到有一個(gè)客戶啟動(dòng)并請(qǐng)求連接到相同的端口,然后 accept()返回一個(gè)對(duì)應(yīng)于客戶的socket。這時(shí),客戶方和服務(wù)方都建立了用于通信的 socket,接下來(lái)就是由各個(gè) socket 分別打開各自的輸入/輸出流。 8.3.6 打開輸入 /出流 類 Socket 提供了方法 getInputStream ()和 getOutStream()來(lái)得到對(duì)應(yīng)的輸入 /輸出流以進(jìn)行讀 /寫操作,這兩個(gè)方法分別返回 InputStream 和 OutputSteam 類對(duì)象。為了便于讀 /寫數(shù)據(jù),我們可以在返回的輸入 /輸出流對(duì)象上建立過(guò)濾流,如 DataInputStream、 DataOutputStream 或PrintStream 類 對(duì) 象 , 對(duì) 于 文 本 方 式 流 對(duì) 象 , 可 以 采 用 InputStreamReader 和OutputStreamWriter、 PrintWirter 等處理。 例如: PrintStream os=new PrintStream(new BufferedOutputStreem(socket.getOutputStream(); DataInputStream is=new DataInputStream(socket.getInputStream(); PrintWriter out=new PrintWriter(socket.getOutStream(),true); BufferedReader in=new ButfferedReader(new InputSteramReader(Socket.getInputStream(); 輸入輸出流是網(wǎng)絡(luò)編程的實(shí)質(zhì)性部分,具體如何構(gòu)造所需要的過(guò)濾流,要根據(jù)需要而定,能否運(yùn)用自如主要看讀者對(duì) Java 中輸入輸出部分掌握如何。 8.3.7 關(guān)閉 Socket 每一個(gè) Socket 存在時(shí),都將占用一定的資源,在 Socket 對(duì)象使用完畢時(shí),要其關(guān)閉。關(guān)閉Socket 可以調(diào)用 Socket 的 Close()方法。在關(guān)閉 Socket 之前, 應(yīng)將與 Socket 相關(guān)的所有的輸入 /輸出流全部關(guān)閉,以釋放所有的資源。而且要注意關(guān)閉的順序,與 Socket 相關(guān)的所有的輸入 /輸出該首先關(guān)閉,然后再關(guān)閉 Socket。 os.close(); is.close(); socket.close(); 盡管 Java 有自動(dòng)回收機(jī)制,網(wǎng)絡(luò)資源最終是會(huì)被釋放的。但是為了有效的利用資源,建議讀者按照合理的順序主動(dòng)釋放資源。 8.3.8 簡(jiǎn)單的 Client/Server 程序設(shè)計(jì) 下面我們給出一個(gè)用 Socket 實(shí)現(xiàn)的客戶和服務(wù)器交互的典型的 C/S 結(jié)構(gòu)的演示 程序,讀者通過(guò)仔細(xì)閱讀該程序,會(huì)對(duì)前面所討論的各個(gè)概念有更深刻的認(rèn)識(shí)。程序的意義請(qǐng)參考注釋。 1. 客戶端程序 import java.io.*; import .*; public class TalkClient public static void main(String args) try Socket socket=new Socket(,4700); /向本機(jī)的 4700端口發(fā)出 客戶請(qǐng)求 BufferedReader sin=new BufferedReader(new InputStreamReader(System.in); /由系統(tǒng)標(biāo)準(zhǔn)輸入設(shè)備構(gòu)造 BufferedReader 對(duì)象 PrintWriter os=new PrintWriter(socket.getOutputStream(); /由 Socket 對(duì)象得到輸出流,并構(gòu)造 PrintWriter 對(duì)象 BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream(); /由 Socket 對(duì)象得到輸入流,并構(gòu)造相應(yīng)的 BufferedReader 對(duì)象 String readline; readline=sin.readLine(); /從系統(tǒng)標(biāo)準(zhǔn)輸入讀入一字符串 while(!readline.equals(bye) /若從標(biāo)準(zhǔn)輸入讀入的字符串為 bye則停止循環(huán) os.println(readline); /將從系統(tǒng)標(biāo)準(zhǔn)輸入讀入的字符串輸出到 Server os.flush(); /刷新輸出流,使 Server 馬上收到該字符串 System.out.println(Client:+readline); /在系統(tǒng)標(biāo)準(zhǔn)輸出上打印讀入的字符串 System.out.println(Server:+is.readLine(); /從 Server 讀入一字符串,并打印到標(biāo)準(zhǔn)輸出上 readline=sin.readLine(); /從系統(tǒng)標(biāo)準(zhǔn)輸入讀入一字符串 /繼續(xù)循環(huán) os.close(); /關(guān)閉 Socket 輸出流 is.close(); /關(guān)閉 Socket 輸入流 socket.close(); /關(guān)閉 Socket catch(Exception e) System.out.println(Error+e); /出錯(cuò),則打印出錯(cuò)信息 2. 服務(wù)器端程序 import java.io.*; import .*; import java.applet.Applet; public class TalkServer public static void main(String args) try ServerSocket server=null; try server=new ServerSocket(4700); /創(chuàng)建一個(gè) ServerSocket 在端口 4700監(jiān)聽客戶請(qǐng)求 catch(Exception e) System.out.println(can not listen to:+e); /出錯(cuò),打印出錯(cuò)信息 Socket socket=null; try socket=server.accept(); /使用 accept()阻塞等待客戶請(qǐng)求,有客戶 /請(qǐng)求到來(lái)則產(chǎn)生一個(gè) Socket 對(duì)象,并繼續(xù)執(zhí)行 catch(Exception e) System.out.println(Error.+e); /出錯(cuò),打印出錯(cuò)信息 String line; BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream(); /由 Socket 對(duì)象得到輸入流,并構(gòu)造相應(yīng)的 BufferedReader 對(duì)象 PrintWriter os=newPrintWriter(socket.getOutputStream(); /由 Socket 對(duì)象得到輸出流,并構(gòu)造 PrintWriter 對(duì)象 BufferedReader sin=new BufferedReader(new InputStreamReader(System.in); /由系統(tǒng)標(biāo)準(zhǔn)輸入設(shè)備構(gòu)造 BufferedReader 對(duì)象 System.out.println(Client:+is.readLine(); /在標(biāo)準(zhǔn)輸出上打印從客戶端讀入的字符串 line=sin.readLine(); /從標(biāo)準(zhǔn)輸入讀入一字符串 while(!line.equals(bye) /如果該字符串為 bye,則停止循環(huán) os.println(line); /向客戶端輸出該字符串 os.flush(); /刷新輸出流,使 Client 馬上收到該字符串 System.out.println(Server:+line); /在系統(tǒng)標(biāo)準(zhǔn)輸出上打印讀入的字符串 System.out.println(Client:+is.readLine(); /從 Client 讀入一字符串,并打印到標(biāo)準(zhǔn)輸出上 line=sin.readLine(); /從系統(tǒng)標(biāo)準(zhǔn)輸入讀入一字符串 /繼續(xù)循環(huán) os.close(); /關(guān)閉 Socket 輸出流 is.close(); /關(guān)閉 Socket 輸入流 socket.close(); /關(guān)閉 Socket server.close(); /關(guān) 閉 ServerSocket catch(Exception e) System.out.println(Error:+e); /出錯(cuò),打印出錯(cuò)信息 從上面的兩個(gè)程序中我們可以看到, socket 四個(gè)步驟的使用過(guò)程。讀者可以分別將 Socket 使用的四個(gè)步驟的對(duì)應(yīng)程序段選擇出來(lái),這樣便于讀者對(duì) socket 的使用有進(jìn)一步的了解。 讀者可以在單機(jī)上試驗(yàn)該程序,最好是能在真正的網(wǎng)絡(luò)環(huán)境下試驗(yàn)該程序,這樣更容易分辨輸出 的內(nèi)容和客戶機(jī),服務(wù)器的對(duì)應(yīng)關(guān)系。同時(shí)也可以修改該程序,提供更為強(qiáng)大的功能,或更加滿足讀者的意圖。 8.3.9 支持多客戶的 client/server 程序設(shè)計(jì) 前面提供的 Client/Server 程序只能實(shí)現(xiàn) Server 和一個(gè)客戶的對(duì)話。在實(shí)際應(yīng)用中,往往是在服務(wù)器上運(yùn)行一個(gè)永久的程序,它可以接收來(lái)自其他多個(gè)客戶端的請(qǐng)求,提供相應(yīng)的服務(wù)。為了實(shí)現(xiàn)在服務(wù)器方給多個(gè)客戶提供服務(wù)的功能,需要對(duì)上面的程序進(jìn)行改造,利用多線程實(shí)現(xiàn)多客戶機(jī)制。服務(wù)器總是在指定的端口上監(jiān)聽是否有客戶請(qǐng)求,一旦監(jiān)聽到客戶請(qǐng)求,服務(wù)器 就會(huì)啟動(dòng)一個(gè)專門的服務(wù)線程來(lái)響應(yīng)該客戶的請(qǐng)求,而服務(wù)器本身在啟動(dòng)完線程之后馬上又進(jìn)入監(jiān)聽狀態(tài),等待下一個(gè)客戶的到來(lái)。 客戶端的程序和上面程序是完全一樣的,讀者如果仔細(xì)閱讀過(guò)上面的程序,可以跳過(guò)不讀,把主要精力集中在 Server 端的程序上。 1. 客戶端程序: MultiTalkClient.java import java.io.*; import .*; public class MultiTalkClient public static void main(String args) try Socket socket=new Socket(,4700); /向本機(jī)的 4700端口發(fā)出客戶請(qǐng)求 BufferedReader sin=new BufferedReader(new InputStreamReader(System.in); /由系統(tǒng)標(biāo)準(zhǔn)輸入設(shè)備構(gòu)造 BufferedReader 對(duì)象 PrintWriter os=new PrintWriter(socket.getOutputStream(); /由 Socket 對(duì)象得到輸出流,并構(gòu)造 PrintWriter 對(duì)象 BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream(); /由 Socket 對(duì)象得到輸入流,并構(gòu)造相應(yīng)的 BufferedReader 對(duì)象 String readline; readline=sin.readLine(); /從系統(tǒng)標(biāo)準(zhǔn)輸入讀入一字符串 while(!readline.equals(bye) /若從標(biāo)準(zhǔn)輸入讀入的字符串為 bye則停止循環(huán) os.println(readline); /將從系統(tǒng)標(biāo)準(zhǔn)輸入讀入的字符串輸出到 Server os.flush(); /刷新輸出流,使 Server 馬上收到該字符串 System.out.println(Client:+readline); /在系統(tǒng)標(biāo)準(zhǔn)輸出上打印讀入的字符串 System.out.println(Server:+is.readLine(); /從 Server 讀入一字符串,并打印到標(biāo)準(zhǔn)輸出上 readline=sin.readLine(); /從系統(tǒng)標(biāo)準(zhǔn)輸入讀入一字符串 /繼續(xù)循環(huán) os.close(); /關(guān)閉 Socket 輸出流 is.close(); /關(guān)閉 Socket 輸入流 socket.close(); /關(guān)閉 Socket catch(Exception e) System.out.println(Error+e); /出錯(cuò),則打印出錯(cuò)信息 2. 服務(wù)器端程序 : MultiTalkServer.java import java.io.*; import .*; import ServerThread; public class MultiTalkServer static int clientnum=0; /靜態(tài)成員變量,記錄當(dāng)前客戶的個(gè)數(shù) public static void main(String args) throws IOException ServerSocket serverSocket=null; boolean listening=true; try serverSocket=new ServerSocket(4700); /創(chuàng)建一個(gè) ServerSocket 在端口 4700監(jiān)聽客戶請(qǐng)求 catch(IOException e) System.out.println(Could not listen on port:4700.); /出錯(cuò),打印出錯(cuò)信息 System.exit(-1); /退出 while(listening) /永遠(yuǎn)循環(huán)監(jiān)聽 new ServerThread(serverSocket.accept(),clientnum).start(); /監(jiān)聽到客戶請(qǐng)求,根據(jù)得到的 Socket 對(duì)象和 客戶計(jì)數(shù)創(chuàng)建服務(wù)線程,并啟動(dòng)之 clientnum+; /增加客戶計(jì)數(shù) serverSocket.close(); /關(guān)閉 ServerSocket 3. 程序 ServerThread.java import java.io.*; import .*; public class ServerThread extends Thread Socket socket=null; /保存與本線程相關(guān)的 Socket 對(duì)象 int clientnum; /保存本進(jìn)程的客戶計(jì)數(shù) public ServerThread(Socket socket,int num) /構(gòu)造函數(shù) this.socket=socket; /初始化 socket 變量 clientnum=num+1; /初始化 clientnum 變量 public void run() /線程主體 try String line; BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream(); /由 Socket 對(duì)象得到輸入流,并構(gòu)造相應(yīng)的 BufferedReader 對(duì)象 PrintWriter os=newPrintWriter(socket.getOutputStream(); /由 Socket 對(duì)象得到輸出流,并構(gòu)造 PrintWriter 對(duì)象 BufferedReader sin=new BufferedReader(new InputStreamReader(System.in); /由系統(tǒng)標(biāo)準(zhǔn)輸入設(shè)備構(gòu)造 BufferedReader 對(duì)象 System.out.println(Client:+ clientnum +is.readLine(); /在標(biāo)準(zhǔn)輸出上打印從客戶端讀入的字符串 line=sin.readLine(); /從標(biāo)準(zhǔn)輸入讀入一字符串 while(!line.equals(bye) /如果該 字符串為 bye,則停止循環(huán) os.println(line); /向客戶端輸出該字符串 os.flush(); /刷新輸出流,使 Client 馬上收到該字符串 System.out.println(Server:+line); /在系統(tǒng)標(biāo)準(zhǔn)輸出上打印該字符串 System.out.println(Client:+ clientnum +is.readLine(); /從 Client 讀入一字符串,并打印到標(biāo)準(zhǔn)輸出上 line=sin.readLine(); /從系統(tǒng)標(biāo)準(zhǔn)輸入讀入一字符串 /繼續(xù)循環(huán) os.close(); /關(guān)閉 Socket 輸出流 is.close(); /關(guān)閉 Socket 輸入流 socket.close(); /關(guān)閉 Socket server.close(); /關(guān)閉 ServerSocket catch(Exception e) System.out.println(Error:+e); /出錯(cuò),打印出錯(cuò)信息 這個(gè)程序向讀者展示了網(wǎng)絡(luò)應(yīng)用中最為典型的 C/S 結(jié)構(gòu),我們可以用下面的圖來(lái)描述這樣一種模型: 通過(guò)以上的學(xué)習(xí),讀者應(yīng)該對(duì) Java 的面向流的網(wǎng)絡(luò)編程有了一個(gè)比較全面的認(rèn)識(shí),這些都是基于 TCP的應(yīng)用,后面我們將介 紹基于 UDP 的 Socket 編程。 8.3.10 據(jù)報(bào) Datagram 通訊 前面在介紹 TCP/IP 協(xié)議的時(shí)候,我們已經(jīng)提到,在 TCP/IP 協(xié)議的傳輸層除了 TCP協(xié)議之外還有一個(gè) UDP 協(xié)議,相比而言 UDP 的應(yīng)用不如 TCP 廣泛,幾個(gè)標(biāo)準(zhǔn)的應(yīng)用層協(xié)議 HTTP, FTP, SMTP 使用的都是 TCP 協(xié)議。但是,隨著計(jì)算機(jī)網(wǎng)絡(luò)的發(fā)展, UDP 協(xié)議正越來(lái)越來(lái)顯示出其威力,尤其是在需要很強(qiáng)的實(shí)時(shí)交互性的場(chǎng)合,如網(wǎng)絡(luò)游戲,視頻會(huì)議等, UDP 更是顯示出極強(qiáng)的威力,下面我們就介紹一下 Java 環(huán)境下如何實(shí)現(xiàn) UDP 網(wǎng)絡(luò)傳輸。 8.3.11 什么是 Datagram 所謂數(shù)據(jù)報(bào)( Datagram)就跟日常生活中的郵件系統(tǒng)一樣,是不能保證可靠的寄到的,而面向鏈接的 TCP 就好比電話,雙方能肯定對(duì)方接受到了信息。在本章前面,我們已經(jīng)對(duì)UDP 和 TCP 進(jìn)行了比較,在這里再稍作小節(jié): TCP,可靠,傳輸大小無(wú)限制,但是需要連接建立時(shí)間,差錯(cuò)控制開銷大。 UDP,不可靠,差錯(cuò)控制開銷較小,傳輸大小限制在 64K 以下,不需要建立連接。 總之,這兩種協(xié)議各有特點(diǎn),應(yīng)用的場(chǎng)合也不同,是完全互補(bǔ)的兩個(gè)協(xié)議,在 TCP/IP協(xié)議中占有同樣重要的地位,要 學(xué)好網(wǎng)絡(luò)編程,兩者缺一不可。 8.3.12 Datagram 通訊的表示方法: DatagramSocket; DatagramPacket 包 中提供了兩個(gè)類 DatagramSocket 和 DatagramPacket 用來(lái)支持?jǐn)?shù)據(jù)報(bào)通信,DatagramSocket 用于在程序之間建立傳送數(shù)據(jù)報(bào)的通信連接, DatagramPacket 則用來(lái)表示一個(gè)數(shù)據(jù)報(bào)。先來(lái)看一下 DatagramSocket 的構(gòu)造方法: DatagramSocket(); DatagramSocket( int prot) ; DatagramSocket(int port, InetAddress laddr) 其中, port 指明 socket 所使用的端口號(hào),如果未指明端口號(hào),則把 socket 連接到本地主機(jī)上一個(gè)可用的端口。 laddr 指明一個(gè)可用的本地地址。給出端口號(hào)時(shí)要保證不發(fā)生端口沖突,否則會(huì)生成 SocketException 類例外。注意:上述的兩個(gè)構(gòu)造方法都聲明拋棄非運(yùn)行時(shí)例外 SocketException,程序中必須進(jìn)行處理,或者捕獲、或者聲明拋棄。 用數(shù)據(jù)報(bào)方式編寫 client/server 程序 時(shí),無(wú)論在客戶方還是服務(wù)方,首先都要建立一個(gè)DatagramSocket 對(duì)象,用來(lái)接收或發(fā)送數(shù)據(jù)報(bào),然后使用 DatagramPacket 類對(duì)象作為傳輸數(shù)據(jù)的載體。下面看一下 DatagramPacket 的構(gòu)造方法 : DatagramPacket( byte buf,int length); DatagramPacket(byte buf, int length, InetAddress addr, int port); DatagramPacket(byte buf, int offset, int length); DatagramPacket(byte buf, int offset, int length, InetAddress address, int port); 其中, buf 中存放數(shù)據(jù)報(bào)數(shù)據(jù), length 為數(shù)據(jù)報(bào)中數(shù)據(jù)的長(zhǎng)度, addr 和 port 旨明目的地址, offset 指明了數(shù)據(jù)報(bào)的位移量。 在接收數(shù)據(jù)前,應(yīng)該采用上面的第一種方法生成一個(gè) DatagramPacket 對(duì)象,給出接收數(shù)據(jù)的緩沖區(qū)及其長(zhǎng)度。然后調(diào)用 DatagramSocket 的方法 receive()等待數(shù)據(jù)報(bào)的到來(lái),receive()將一直等待,直到收到一個(gè)數(shù)據(jù)報(bào)為止。 DatagramPacket packet=new DatagramPacket(buf, 256); Socket.receive (packet); 發(fā)送數(shù)據(jù)前,也要先生成一個(gè)新的 DatagramPacket 對(duì)象,這時(shí)要使用上面的第二種構(gòu)造方法,在給出存放發(fā)送數(shù)據(jù)的緩沖區(qū)的同時(shí),還要給出完整的目的地址,包括 IP 地址和端口號(hào)。發(fā)送數(shù)據(jù)是通過(guò) DatagramSocket 的方法 send()實(shí)現(xiàn)的, send()根據(jù)數(shù)據(jù)報(bào)的目的地址來(lái)尋徑,以傳遞數(shù)據(jù)報(bào)。 DatagramPacket packet=new DatagramPacket(buf, length, address, port); Socket.send(packet); 在構(gòu)造數(shù)據(jù)報(bào)時(shí),要給出 InetAddress 類參數(shù)。類 InetAddress 在包 中定義,用來(lái)表示一個(gè) Internet 地址,我們可以通過(guò)它提供的類方法 getByName()從一個(gè)表示主機(jī)名的字符串獲取該主機(jī)的 IP 地址,然后再獲取相應(yīng)的地址信息。 8.3.13 基于 UDP 的簡(jiǎn)單的 Client/Server 程序設(shè)計(jì) 有了上面的知識(shí),我們就可以來(lái)構(gòu)件一個(gè)基于 UDP 的 C/S 網(wǎng)絡(luò)傳輸模型 1. 客戶方程序 QuoteClient.java import java.io.*; import .*; import java.util.*; public class QuoteClient public static void main(String args) throws IOException if(args.length!=1) /如果啟動(dòng)的時(shí)候沒(méi)有給出 Server 的名字,那么出錯(cuò)退出 System.out.println(Usage:java QuoteClient ); /打印出錯(cuò)信息 return; /返回 DatagramSocket socket=new DatagramSocklet(); /創(chuàng)建數(shù)據(jù)報(bào)套接字 Byte buf=new byte256; /創(chuàng)建緩沖區(qū) InetAddress address=InetAddress.getByName(args 0); /由命令行給出的第一個(gè)參數(shù)默認(rèn)為 Server 的名字,通過(guò)它得到 Server 的 IP 信息 DatagramPacket packet=new DatagramPacket (buf, buf.length, address, 4445); /創(chuàng)建 DatagramPacket 對(duì)象 socket.send(packet); /發(fā)送 packet=new DatagramPacket(buf,buf.length); /創(chuàng)建新的 DatagramPacket 對(duì)象,用來(lái)接收數(shù)據(jù)報(bào) socket.receive(packet); /接收 String received=new String(packet.getData(); /根據(jù)接收到的字節(jié)數(shù)組生成相應(yīng)的字符串 System.out.println(Quote of the Moment:+received ); /打印生成的字符串 socket.close(); /關(guān)閉套接口 2. 服務(wù)器方程序 :QuoteServer.java public class QuoteServer public static void main(String args) throws java.io.IOException new QuoteServerThread().start(); /啟動(dòng)一個(gè) QuoteServerThread 線程 3. 程序 QuoteServerThread.java import java.io.*; import .*; import java.util.*; /服務(wù)器線程 public class QuoteServerThread extends Thread protected DatagramSocket socket=null; /記錄和本對(duì)象相關(guān)聯(lián)的 DatagramSocket 對(duì)象 protected BufferedReader in=null; /用來(lái)讀文件的一個(gè) Reader protected boolean moreQuotes=true; /標(biāo)志變量,是否繼續(xù)操作 public QuoteServerThread() throws IOException /無(wú)參數(shù)的構(gòu)造函數(shù) this(QuoteServerThread); /以 QuoteServerThread 為默認(rèn)值調(diào)用帶參數(shù)的構(gòu)造函數(shù) public QuoteServerThread(String name) throws IOException super(name); /調(diào)用父類的構(gòu)造函數(shù) socket=new DatagramSocket(4445); /在端口 4445創(chuàng)建數(shù)據(jù)報(bào)套接字 try in= new BufferedReader(new FileReader( one-liners.txt); /打開一個(gè)文件,構(gòu)造相應(yīng)的 BufferReader 對(duì)象 catch(FileNotFoundException e) /異常處理 System.err.println(Could not open quote file. Serving time instead.); /打印出錯(cuò)信息 public void run() /線程主體 while(moreQuotes) try byte buf=new byte256; /創(chuàng)建緩沖區(qū) DatagramPacket packet=new DatagramPacket(buf,buf.length); /由緩沖區(qū)構(gòu)造 DatagramPacket 對(duì)象 socket.receive(packet); /接收數(shù)據(jù)報(bào) String dString=null; if(in= =null) dString=new Date().toString(); /如果初始化的時(shí)候打開文件失敗了, /則使用日期作為要傳送的字符串 else dString=getNextQuote(); /否則調(diào)用成員函數(shù)從文件中讀出 字符串 buf=dString.getByte(); /把 String 轉(zhuǎn)換成字節(jié)數(shù)組,以便傳送 InetAddress address=packet.getAddress(); /從 Client 端傳來(lái)的 Packet 中得到 Client 地址 int port=packet.getPort(); /和端口號(hào) packet=new DatagramPacket(buf,buf.length,address,port); /根據(jù)客戶端信息構(gòu)建 DatagramPacket socket.send(packet); /發(fā)送數(shù)據(jù)報(bào) catch(IOException e) /異常處理 e.printStackTrace(); /打印錯(cuò)誤棧 moreQuotes=false; /標(biāo)志變量置 false,以結(jié)束循環(huán) socket.close(); /關(guān)閉數(shù)據(jù)報(bào)套接字 protected String getNextQuotes() /成員函數(shù),從文件中讀數(shù)據(jù) String returnValue=null; try if(returnValue=in.readLine()= =null) /從文件中讀一行,如果讀到了文件尾 in.close( ); /關(guān)閉輸入流 moreQuotes=false; /標(biāo)志變量置 false,以結(jié)束循環(huán) returnValue=No more quotes. Goodbye.; /置返回值 /否則返回字符串即為從文件讀出的字符串 catch(IOEception e) /異常處理 returnValue=IOException occurred in server; /置異常返回值 return returnValue; /返回字符串 可以看出使用 UDP 和使用 TCP 在程序上還是有很大的區(qū)別的。一個(gè)比較明顯的區(qū)別是, UDP 的 Socket編程是不提供監(jiān)聽功能的,也就是說(shuō)通信雙方更為平等,面對(duì)的接口是完全一樣的。但是為了用 UDP 實(shí)現(xiàn)C/S 結(jié)構(gòu),在使用 UDP 時(shí)可以使用 DatagramSocket.receive()來(lái)實(shí)現(xiàn)類似于監(jiān)聽的功能。因?yàn)?receive()是阻塞的函數(shù),當(dāng)它返回時(shí),緩沖區(qū)里已經(jīng)填滿了接受到的一個(gè)數(shù)據(jù)報(bào),并且可以從該數(shù)據(jù)報(bào)得到發(fā)送方的各種信息,這一點(diǎn)跟 accept()是很相象的,因而可以根據(jù)讀入的數(shù)據(jù)報(bào)來(lái)決定下一步的動(dòng)作,這
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 教育技術(shù)在行政決策支持系統(tǒng)中的應(yīng)用
- 心理輔導(dǎo)在教育中的關(guān)鍵作用
- 抖音商戶編導(dǎo)短視頻開頭吸引力制度
- 全球石油市場(chǎng)供需格局調(diào)整與價(jià)格走勢(shì)對(duì)能源市場(chǎng)供需預(yù)測(cè)技術(shù)的應(yīng)用研究報(bào)告
- 浙江省寧波市鎮(zhèn)海區(qū)仁愛中學(xué)2024-2025學(xué)年數(shù)學(xué)七年級(jí)第一學(xué)期期末質(zhì)量檢測(cè)試題含解析
- 貴州黔南經(jīng)濟(jì)學(xué)院《可摘局部義齒工藝技術(shù)》2023-2024學(xué)年第一學(xué)期期末試卷
- 浙江杭州上城區(qū)2025屆化學(xué)九年級(jí)第一學(xué)期期末達(dá)標(biāo)檢測(cè)試題含解析
- 2024-2025學(xué)年陜西省咸陽(yáng)市秦嶺中學(xué)數(shù)學(xué)七年級(jí)第一學(xué)期期末達(dá)標(biāo)檢測(cè)模擬試題含解析
- 公路客運(yùn)行業(yè)2025年轉(zhuǎn)型升級(jí)與智能停車場(chǎng)建設(shè)研究報(bào)告
- 公路貨運(yùn)行業(yè)數(shù)字化轉(zhuǎn)型效率提升的關(guān)鍵瓶頸與突破路徑報(bào)告
- 2023貴州畢節(jié)市納雍縣人民醫(yī)院招聘第一批編外專業(yè)技術(shù)人員30人考試備考題庫(kù)及答案解析
- 兒童疼痛與舒適度評(píng)估量表
- 2020年上海市中考語(yǔ)數(shù)英物化五科試卷及答案
- 新華書店讀者問(wèn)卷調(diào)查表
- GB/T 20946-2007起重用短環(huán)鏈驗(yàn)收總則
- GB/T 18391.3-2009信息技術(shù)元數(shù)據(jù)注冊(cè)系統(tǒng)(MDR)第3部分:注冊(cè)系統(tǒng)元模型與基本屬性
- GB/T 10610-2009產(chǎn)品幾何技術(shù)規(guī)范(GPS)表面結(jié)構(gòu)輪廓法評(píng)定表面結(jié)構(gòu)的規(guī)則和方法
- 濟(jì)源幼兒園等級(jí)及管理辦法
- 房地產(chǎn)開發(fā)全流程培訓(xùn)講義課件
- DB44-T 2163-2019山地自行車賽場(chǎng)服務(wù) 基本要求-(高清現(xiàn)行)
- 云南省特種設(shè)備檢驗(yàn)檢測(cè)收費(fèi)標(biāo)準(zhǔn)
評(píng)論
0/150
提交評(píng)論