第13章 網(wǎng)絡(luò)編程_第1頁(yè)
第13章 網(wǎng)絡(luò)編程_第2頁(yè)
第13章 網(wǎng)絡(luò)編程_第3頁(yè)
第13章 網(wǎng)絡(luò)編程_第4頁(yè)
第13章 網(wǎng)絡(luò)編程_第5頁(yè)
已閱讀5頁(yè),還剩21頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

1、第十三章 Java網(wǎng)絡(luò)編程一、Java套接字實(shí)現(xiàn)網(wǎng)絡(luò)編程之基礎(chǔ)篇 1用 Java 開(kāi)發(fā)網(wǎng)絡(luò)軟件非常方便和強(qiáng)大,Java 的這種力量來(lái)源于他獨(dú)有的一套強(qiáng)大的用于 網(wǎng)絡(luò)的 API,這些 API 是一系列的類(lèi)和接口,均位于包 和 中。在這篇文 章中我們將介紹套接字(Socket)慨念,同時(shí)以實(shí)例說(shuō)明如何使 用 Network API 操縱套接字, 在完成本文后,你將具備編寫(xiě)網(wǎng)絡(luò)低端通訊軟件的能力。1、什么是套接字(Socket)?Network API 是典型的用于基于 TCP/IP 網(wǎng)絡(luò) Java 程序與其他程序通訊,Network API 依靠 Socket

2、 進(jìn)行通訊。Socket 可以看成在兩個(gè)程序進(jìn)行通訊連接中的一個(gè)端點(diǎn),一個(gè)程序?qū)?一段信息寫(xiě)入 Socket 中,該 Socket 將這段信息發(fā)送給另外一個(gè) Socket 中,使這段信息能傳 送到其他程序中。如圖 1我們來(lái)分析一下圖 1,Host A 上的程序 A 將一段信息寫(xiě)入 Socket 中,Socket 的內(nèi)容被 Host A 的網(wǎng)絡(luò)管理軟件訪(fǎng)問(wèn),并將這段信息通 過(guò) Host A 的網(wǎng)絡(luò)接口卡發(fā)送到 Host B,Host B 的網(wǎng)絡(luò)接口卡接收到這段信息后,傳送給 Host B 的網(wǎng)絡(luò)管理軟件,網(wǎng)絡(luò)管理軟件將這段信息 保 存在 Host B 的 Socket 中,然后程序 B 才能在

3、Socket 中閱讀這段信息。假設(shè)在圖 1 的網(wǎng)絡(luò)中添加第三個(gè)主機(jī) Host C,那么 Host A 怎么知道信息被正確傳送到Host B 而不是被傳送到 Host C 中了呢?基于 TCP/IP 網(wǎng)絡(luò)中的每一個(gè)主機(jī)均被賦予了一個(gè)唯 一的 IP 地址,IP 地址是一個(gè) 32 位的無(wú)符號(hào)整數(shù),由于沒(méi)有轉(zhuǎn)變成二進(jìn)制,因此通常以小數(shù)點(diǎn)分隔,如:,正如所見(jiàn) IP 地址均由四個(gè)部分組成,每個(gè)部分的范圍都是 0-255, 以表示 8 位地址。值得注意的是 IP 地址都是 32 位地址,這是 IP 協(xié)議版本 4(簡(jiǎn)稱(chēng) Ipv4)規(guī)定的,目前由于 IPv4 地址已近耗盡,所以 IPv

4、6 地址正逐漸代替 Ipv4 地址,Ipv6 地址則是 128 位無(wú)符號(hào) 整數(shù)。假設(shè)第二個(gè)程序被加入圖 1 的網(wǎng)絡(luò)的 Host B 中,那么由 Host A 傳來(lái)的信息如何能被正確 的傳給程序 B 而不是傳給新加入的程序呢?這是因?yàn)槊恳粋€(gè)基于 TCP/IP 網(wǎng)絡(luò)通訊的程序都被 賦予了唯一的端口和端口號(hào),端口是一個(gè)信息緩沖區(qū),用于保留 Socket 中的輸入/輸出信息, 端口號(hào)是一個(gè) 16 位無(wú)符號(hào)整數(shù),范圍是 0-65535,以區(qū)別主機(jī)上的每一個(gè)程序(端口號(hào)就像 房屋中的房間號(hào)),低于 256 的短口號(hào)保留給標(biāo)準(zhǔn)應(yīng)用程序,比如 pop3 的端口號(hào)就是 110, 每一個(gè)套接字都組合進(jìn)了 IP 地

5、址、端口、端口號(hào),這樣形成的整體就可以區(qū)別每一個(gè)套接字 t, 下面我們就來(lái)談?wù)剝煞N套接字:流套接字和自尋址數(shù)據(jù)套接字。2、流套接字(Stream Socket) 無(wú)論何時(shí),在兩個(gè)網(wǎng)絡(luò)應(yīng)用程序之間發(fā)送和接收信息時(shí)都需要建立一個(gè)可靠的連接,流套接字依靠 TCP 協(xié)議來(lái)保證信息正確到達(dá)目的地,實(shí)際上,IP 包有可能在網(wǎng)絡(luò)中丟失或者在傳送過(guò)程中發(fā)生錯(cuò)誤,任何一種情況發(fā)生,作為接受方的 TCP 將聯(lián)系發(fā)送方 TCP 重新發(fā)送這個(gè) IP包。這就是所謂的在兩個(gè)流套接字之間建立可靠的連接。流套接字在 C/S 程序中扮演一個(gè)必需的角色,客戶(hù)機(jī)程序(需要訪(fǎng)問(wèn)某些服務(wù)的網(wǎng)絡(luò)應(yīng)用 程序)創(chuàng)建一個(gè)扮演服務(wù)器程序的主機(jī)的

6、 IP 地址和服務(wù)器程序(為客戶(hù)端應(yīng)用程序提供服務(wù)的 網(wǎng)絡(luò)應(yīng)用程序)的端口號(hào)的流套接字對(duì)象??蛻?hù)端流套接字的初始化代碼將 IP 地址和端口號(hào)傳遞給客戶(hù)端主機(jī)的網(wǎng)絡(luò)管理軟件,管理 軟件將 IP 地址和端口號(hào)通過(guò) NIC 傳遞給服務(wù)器端主機(jī);服務(wù)器端主機(jī)讀到經(jīng)過(guò) NIC 傳遞來(lái)的 數(shù)據(jù),然后查看服務(wù)器程序是否處于監(jiān)聽(tīng)狀態(tài),這種監(jiān)聽(tīng)依然是通過(guò)套接字和端口來(lái)進(jìn)行的;如 果服務(wù)器程序處于監(jiān)聽(tīng)狀態(tài),那么服務(wù)器端網(wǎng)絡(luò)管理軟件就向客戶(hù)機(jī)網(wǎng)絡(luò)管理軟件發(fā)出一個(gè)積極 的響應(yīng)信號(hào),接收到響應(yīng)信號(hào)后,客戶(hù)端流套接字初始化代碼就給客戶(hù)程序建立一個(gè)端口號(hào),并 將這個(gè)端口號(hào)傳遞給服務(wù)器程序的套接字(服務(wù)器程序?qū)⑹褂眠@個(gè)端口號(hào)

7、識(shí)別傳來(lái)的信息是否是 屬于客戶(hù)程序)同時(shí)完成流套接字的初始化。如果服務(wù)器程序沒(méi)有處于監(jiān)聽(tīng)狀態(tài),那么服務(wù)器端網(wǎng)絡(luò)管理軟件將給客戶(hù)端傳遞一個(gè)消極 信號(hào),收到這個(gè)消極信號(hào)后,客戶(hù)程序的流套接字初始化代碼將拋出一個(gè)異常對(duì)象并且不建立通 訊連接,也不創(chuàng)建流套接字對(duì)象。這種情形就像打電話(huà)一樣,當(dāng)有人的時(shí)候通訊建立,否則電話(huà) 將被掛起。這部分的工作包括了相關(guān)聯(lián)的三個(gè)類(lèi):InetAddress, Socket, 和 ServerSocket。 InetAddress 對(duì)象描繪了 32 位或 128 位 IP 地 址,Socket 對(duì)象代表了客戶(hù)程序流套接字,ServerSocket 代表了服務(wù)程序流套接字,所

8、有這 三個(gè)類(lèi)均位于包 中。3、InetAddress 類(lèi)InetAddress 類(lèi)在網(wǎng)絡(luò) API 套接字編程中扮演了一個(gè)重要角色。參數(shù)傳遞給流套接字類(lèi)和 自尋址套接字類(lèi)構(gòu)造器或非構(gòu)造器方法。 InetAddress 描述了 32 位或 64 位 IP 地址,要完成 這個(gè)功能,InetAddress 類(lèi)主要依靠?jī)蓚€(gè)支持類(lèi) Inet4Address 和 Inet6Address,這三個(gè) 類(lèi)是繼承關(guān)系,InetAddrress 是父類(lèi),Inet4Address 和 Inet6Address 是子類(lèi)。2由于 InetAddress 類(lèi)只有一個(gè)構(gòu)造函數(shù),而且不能傳遞參數(shù),所以不能直接創(chuàng)

9、建InetAddress 對(duì)象,比如下面的做法就是錯(cuò)誤的:InetAddress ia = new InetAddress ();但我們可以通過(guò)下面的 5 個(gè)工廠(chǎng)方法創(chuàng)建來(lái)創(chuàng)建一個(gè) InetAddress 對(duì)象或 InetAddress 數(shù)組:. getAllByName(String host)方法返回一個(gè) InetAddress 對(duì)象的引用,每個(gè)對(duì)象包含一個(gè) 表示相應(yīng)主機(jī)名的單獨(dú)的 IP 地址,這個(gè) IP 地址是通過(guò) host 參數(shù)傳遞的,對(duì)于指定的主機(jī)如果 沒(méi)有 IP 地址存在那么這個(gè)方法將拋出一個(gè) UnknownHostException 異常對(duì)象。. getByAddress(byt

10、e addr)方法返回一個(gè) InetAddress 對(duì)象的引用,這個(gè)對(duì)象包含了 一個(gè) Ipv4 地址或 Ipv6 地址,Ipv4 地址是一個(gè) 4 字節(jié)數(shù)組,Ipv6 地址是一個(gè) 16 字節(jié)地址數(shù) 組,如果返回的數(shù)組既不是 4 字節(jié)的也不是 16 字節(jié)的,那么方法將會(huì)拋出一個(gè) UnknownHostException 異常對(duì)象。. getByAddress(String host, byte addr)方法返回一個(gè) InetAddress 對(duì)象的引用,這個(gè) InetAddress 對(duì)象包含了一個(gè)由 host 和 4 字節(jié)的 addr 數(shù)組指定的 IP 地址,或者是 host和 16 字節(jié)的 ad

11、dr 數(shù)組指定的 IP 地址,如果這 個(gè)數(shù)組既不是 4 字節(jié)的也不是 16 位字節(jié)的, 那么該方法將拋出一個(gè) UnknownHostException 異常對(duì)象。. getByName(String host)方法返回一個(gè) InetAddress 對(duì)象,該對(duì)象包含了一個(gè)與 host參數(shù)指定的主機(jī)相對(duì)應(yīng)的 IP 地址,對(duì)于指定的主機(jī)如果沒(méi)有 IP 地址存在,那么方法將拋出一個(gè) UnknownHostException 異常對(duì)象。. getLocalHost()方法返回一個(gè) InetAddress 對(duì)象,這個(gè)對(duì)象包含了本地機(jī)的 IP 地址,考 慮到本地主機(jī)既是客戶(hù)程序主機(jī)又是服務(wù)器程序主機(jī),為避免

12、混亂,我們將客戶(hù)程序主機(jī)稱(chēng)為客 戶(hù)主機(jī),將服務(wù)器程序主機(jī)稱(chēng)為服務(wù)器主機(jī)。上面講到的方法均提到返回一個(gè)或多個(gè) InetAddress 對(duì)象的引用,實(shí)際上每一個(gè)方法都要 返回一個(gè)或多個(gè) Inet4Address/Inet6Address 對(duì)象的引用,調(diào)用者不需要知道引用的子類(lèi)型, 相反調(diào)用者可以使用返回的引用調(diào)用 InetAddress 對(duì)象的非靜態(tài)方法,包括子類(lèi)型的多態(tài)以確 保重載方法被調(diào)用。InetAddress 和它的子類(lèi)型對(duì)象處理主機(jī)名到主機(jī) IPv4 或 IPv6 地址的轉(zhuǎn)換,要完成這個(gè) 轉(zhuǎn)換需要使用域名系統(tǒng),下面的代碼示范了如何通過(guò)調(diào)用 getByName(String host)方法

13、獲得 InetAddress 子類(lèi)對(duì)象的方法,這個(gè)對(duì)象包含了與 host 參數(shù)相對(duì)應(yīng)的 IP 地址:InetAddress ia = InetAddress.getByName ();一但獲得了 InetAddress 子類(lèi)對(duì)象的引用就可以調(diào)用 InetAddress 的各種方法來(lái)獲得InetAddress 子類(lèi)對(duì)象中的 IP 地址信 息,比如,可以通過(guò)調(diào)用 getCanonicalHostName() 從域名服務(wù)中獲得標(biāo)準(zhǔn)的主機(jī)名;getHostAddress()獲得 IP 地址, getHostName()獲得主 機(jī)名,isLoopbackAddress()

14、判斷 IP 地址是否是一個(gè) loopback 地址。List1 是一段示范代碼:InetAddressDemo3/ InetAddressDemo.javaimport .*;class InetAddressDemopublic static void main (String args) throws UnknownHostExceptionString host = localhost;if (args.length = 1)host = args 0;InetAddress ia = InetAddress.getByName (host);System.out.pri

15、ntln (Canonical Host Name = +ia.getCanonicalHostName();System.out.println (Host Address = +ia.getHostAddress ();System.out.println (Host Name = +ia.getHostName ();System.out.println (Is Loopback Address = +ia.isLoopbackAddress ();當(dāng)無(wú)命令行參數(shù)時(shí),代碼輸出類(lèi)似下面的結(jié)果:Canonical Host Name = localhostHost Address = 127

16、.0.0.1Host Name = localhostIs Loopback Address = trueInetAddressDemo 給了你一個(gè)指定主機(jī)名作為命令行參數(shù)的選擇,如果沒(méi)有主機(jī)名被指定,那么將使用 localhost(客戶(hù)機(jī)的), InetAddressDemo 通過(guò)調(diào)用 getByName(String host) 方法獲得一個(gè) InetAddress 子類(lèi)對(duì)象的引用,通過(guò)這個(gè)引用 獲得了標(biāo)準(zhǔn)主機(jī)名,主機(jī)地址, 主機(jī)名以及 IP 地址是否是 loopback 地址的輸出。4、Socket 類(lèi)當(dāng)客戶(hù)程序需要與服務(wù)器程序通訊的時(shí)候,客戶(hù)程序在客戶(hù)機(jī)創(chuàng)建一個(gè) socket 對(duì)象,S

17、ocket 類(lèi)有幾個(gè)構(gòu)造函數(shù)。兩個(gè)常用的構(gòu)造函數(shù)是 Socket(InetAddress addr, int port) 和 Socket(String host, int port),兩個(gè)構(gòu)造函數(shù) 都創(chuàng)建了一個(gè)基于 Socket 的連接服務(wù)器端流套接字的流套接字。對(duì)于第一個(gè) InetAddress 子 類(lèi)對(duì)象通過(guò) addr 參數(shù)獲得服務(wù)器主機(jī)的 IP 地址,對(duì)于第二個(gè)函數(shù) host 參數(shù)包被分配到 InetAddress 對(duì)象中,如果沒(méi)有 IP 地址與 host 參數(shù)相一致,那么將拋出 UnknownHostException 異常對(duì)象。兩個(gè)函數(shù)都通過(guò)參數(shù) port 獲得服務(wù)器的端口號(hào)。假設(shè)

18、已 經(jīng)建立連接了,網(wǎng)絡(luò) API 將在客戶(hù)端基于 Socket 的流套接字中捆綁客戶(hù)程序的 IP 地址和任意 一個(gè)端口號(hào),否則兩個(gè)函數(shù)都會(huì)拋出一個(gè) IOException 對(duì)象。4如果創(chuàng)建了一個(gè) Socket 對(duì)象,那么它可能通過(guò)調(diào)用 Socket 的 getInputStream()方法 從服務(wù)程序獲得輸入流讀傳送來(lái)的信息,也可能通過(guò)調(diào)用 Socket 的 getOutputStream()方法 獲得輸出流來(lái)發(fā)送消息。在讀寫(xiě)活動(dòng)完成之后,客戶(hù)程序調(diào)用 close()方法關(guān)閉流和流套接字, 下面的代碼創(chuàng)建了一個(gè)服務(wù)程序主機(jī)地址為 ,端口號(hào)為 13 的 Socket 對(duì)象

19、, 然后從這個(gè)新創(chuàng)建的 Socket 對(duì)象中讀取輸入流,然后再關(guān)閉流和 Socket 對(duì)象。Socket s = new Socket (, 13);InputStream is = s.getInputStream ();/ Read from the stream.is.close ();s.close ();接下面我們將示范一個(gè)流套接字的客戶(hù)程序,這個(gè)程序?qū)?chuàng)建一個(gè) Socket 對(duì)象,Socket將訪(fǎng)問(wèn)運(yùn)行在指定主機(jī)端口 10000 上的服務(wù)程序,如果訪(fǎng)問(wèn)成功客戶(hù)程序?qū)⒔o服務(wù)程序發(fā)送一 系列命令并打印服務(wù)程序的響應(yīng)。List2 是我們創(chuàng)建的程序 SSClien

20、t 的源代碼:Listing 2: SSClient.java/ SSClient.javaimport java.io.*;import .*;class SSClientpublic static void main (String args)String host = localhost;/ If user specifies a command-line argument, that argument/ represents the host name.if (args.length = 1)host = args 0;BufferedReader br = null;

21、PrintWriter pw = null;Socket s = null;5try/ Create a socket that attempts to connect to the server/ program on the host at port 10000.s = new Socket (host, 10000);/ Create an input stream reader that chains to the sockets/ byte-oriented input stream. The input stream reader/ converts bytes read from

22、 the socket to characters. The/ conversion is based on the platforms default character/ set.InputStreamReader isr;isr = new InputStreamReader (s.getInputStream ();/ Create a buffered reader that chains to the input stream/ reader. The buffered reader supplies a convenient method/ for reading entire

23、lines of text.br = new BufferedReader (isr);/ Create a print writer that chains to the sockets byte-/ oriented output stream. The print writer creates an/ intermediate output stream writer that converts/ characters sent to the socket to bytes. The conversion/ is based on the platforms default charac

24、ter set.pw = new PrintWriter (s.getOutputStream (), true);6/ Send the DATE command to the server.pw.println (DATE);/ Obtain and print the current date/time.System.out.println (br.readLine ();/ Send the PAUSE command to the server. This allows several/ clients to start and verifies that the server is

25、 spawning/ multiple threads.pw.println (PAUSE);/ Send the DOW command to the server.pw.println (DOW);/ Obtain and print the current day of week.System.out.println (br.readLine ();/ Send the DOM command to the server.pw.println (DOM);/ Obtain and print the current day of month.System.out.println (br.

26、readLine ();/ Send the DOY command to the server.pw.println (DOY);/ Obtain and print the current day of year.System.out.println (br.readLine ();7運(yùn)行這段程序?qū)?huì)得到下面的結(jié)果:Tue Jan 29 18:11:51 CST 2002TUESDAY2929SSClient 創(chuàng)建了一個(gè) Socket 對(duì)象與運(yùn)行在主機(jī)端口 10000 的服務(wù)程序聯(lián)系,主機(jī)的 IP地址由 host 變量確定。SSClient 將獲得 Socket 的輸入輸出流,圍繞 Buf

27、feredReader 的輸 入流和 PrintWriter 的輸出流對(duì)字符串進(jìn)行讀寫(xiě)操作就變得非常容易,SSClient 向服務(wù)程序發(fā) 出各種 date/time 命令并得到響應(yīng),每個(gè)響應(yīng)均被打印,一旦最后一個(gè)響應(yīng)被打印,將執(zhí)行 Try/Catch/Finally 結(jié)構(gòu)的 Finally 子串,F(xiàn)inally 子串將在關(guān)閉 Socket 之前關(guān)閉 BufferedReader 和 PrintWriter。在 SSClient 源代碼編譯完成后,可以輸入 java SSClient 來(lái)執(zhí)行這段程序,如果有合適 的程序運(yùn)行在不同的主機(jī)上,采用主機(jī)名/IP 地址 為參數(shù)的輸入方式,比如 www.s

28、 是運(yùn)行服務(wù)器程序的主機(jī),那么輸入方式就是 java SSClient 。二、Java套接字實(shí)現(xiàn)網(wǎng)絡(luò)編程之基礎(chǔ)篇 21、技巧Socket 類(lèi)包含了許多有用的方法。比如 getLocalAddress()將返回一個(gè)包含客戶(hù)程序 IP 地址的 InetAddress 子類(lèi)對(duì)象的引用;getLocalPort()將返回客戶(hù)程序的端口 號(hào);getInetAddress()將返回一個(gè)包含服務(wù)器 IP 地址的 InetAddress 子類(lèi)對(duì)象的引用; getPort()將返回服務(wù)程序的端口號(hào)。2、ServerSocket 類(lèi)由于 SSClient 使用了流

29、套接字,所以服務(wù)程序也要使用流套接字。這就要?jiǎng)?chuàng)建一個(gè) ServerSocket 對(duì)象,ServerSocket 有幾個(gè)構(gòu)造函數(shù),最簡(jiǎn)單的是 ServerSocket(int port), 當(dāng)使用 ServerSocket (int port)創(chuàng)建一個(gè) ServerSocket 對(duì)象,port 參數(shù)傳遞端口號(hào),這個(gè) 端口就是服務(wù)器監(jiān)聽(tīng)連接請(qǐng)求的端口,如果在這時(shí)出現(xiàn)錯(cuò)誤將拋出 IOException 異常對(duì)象,否 則將創(chuàng)建 ServerSocket 對(duì)象并開(kāi)始準(zhǔn)備接收連接請(qǐng)求。接下來(lái)服務(wù)程序進(jìn)入無(wú)限循環(huán)之中,無(wú)限循環(huán)從調(diào)用 ServerSocket 的 accept()方法開(kāi)始, 在調(diào)用開(kāi)始后 a

30、ccept()方法將導(dǎo)致調(diào)用線(xiàn)程阻塞直到連接建立。在建立連接后 accept()返回一 個(gè)最近創(chuàng)建的 Socket 對(duì)象,該 Socket 對(duì)象綁定了客戶(hù)程序的 IP 地址或端口號(hào)。由于存在單個(gè)服務(wù)程序與多個(gè)客戶(hù)程序通訊的可能,所以服務(wù)程序響應(yīng)客戶(hù)程序不應(yīng)該花 很多時(shí)間,否則客戶(hù)程序在得到服務(wù)前有可能花很多時(shí)間來(lái)等待通訊的建立,然而服務(wù)程序和客 戶(hù)程序的會(huì)話(huà)有可能是很長(zhǎng)的(這與電話(huà)類(lèi)似),因此為加快對(duì)客戶(hù)程序連接請(qǐng)求的響應(yīng),典型 的方法是服務(wù)器主機(jī)運(yùn)行一個(gè)后臺(tái)線(xiàn)程,這個(gè)后臺(tái)線(xiàn)程處理服務(wù)程序和客戶(hù)程序的通訊。9為了示范我們?cè)谏厦嬲劦降目畈⑼瓿?SSClient 程序,下面我們創(chuàng)建一個(gè) SSSe

31、rver 程序, 程序?qū)?chuàng)建一個(gè) ServerSocket 對(duì)象來(lái)監(jiān)聽(tīng)端口 10000 的連接請(qǐng)求,如果成功服務(wù)程序?qū)⒌却?連接輸入,開(kāi)始一個(gè)線(xiàn)程處理連接,并響應(yīng)來(lái)自客戶(hù)程序的命令。下面就是這段程序的代碼:Listing 3: SSServer.java/ SSServer.javaimport java.io.*;import .*;import java.util.*;class SSServerpublic static void main (String args) throws IOExceptionSystem.out.println (Server startin

32、g.n);/ Create a server socket that listens for incoming connection/ requests on port 10000.ServerSocket server = new ServerSocket (10000);while (true)/ Listen for incoming connection requests from client/ programs, establish a connection, and return a Socket/ object that represents this connection.S

33、ocket s = server.accept ();System.out.println (Accepting Connection.n);/ Start a thread to handle the connection.new ServerThread (s).start ();10class ServerThread extends Threadprivate Socket s;ServerThread (Socket s)this.s = s; public void run ()BufferedReader br = null;PrintWriter pw = null;try/

34、Create an input stream reader that chains to the sockets/ byte-oriented input stream. The input stream reader/ converts bytes read from the socket to characters. The/ conversion is based on the platforms default character/ set.InputStreamReader isr;isr = new InputStreamReader (s.getInputStream ();/

35、Create a buffered reader that chains to the input stream/ reader. The buffered reader supplies a convenient method/ for reading entire lines of text.11br = new BufferedReader (isr);/ Create a print writer that chains to the sockets byte-/ oriented output stream. The print writer creates an/ intermed

36、iate output stream writer that converts/ characters sent to the socket to bytes. The conversion/ is based on the platforms default character set.pw = new PrintWriter (s.getOutputStream (), true);/ Create a calendar that makes it possible to obtain date/ and time information.Calendar c = Calendar.get

37、Instance ();/ Because the client program may send multiple commands, a/ loop is required. Keep looping until the client either/ explicitly requests termination by sending a command/ beginning with letters BYE or implicitly requests/ termination by closing its output stream.do/ Obtain the client prog

38、rams next command.String cmd = br.readLine ();/ Exit if client program has closed its output stream.if (cmd = null)break;12/ Convert command to uppercase, for ease of comparison.cmd = cmd.toUpperCase ();/ If client program sends BYE command, terminate.if (cmd.startsWith (BYE)break;/ If client progra

39、m sends DATE or TIME command, return/ current date/time to the client program.if (cmd.startsWith (DATE) | cmd.startsWith (TIME)pw.println (c.getTime ().toString ();/ If client program sends DOM (Day Of Month) command,/ return current day of month to the client program.if (cmd.startsWith (DOM)pw.prin

40、tln ( + c.get (Calendar.DAY_OF_MONTH);/ If client program sends DOW (Day Of Week) command,/ return current weekday (as a string) to the client/ program.if (cmd.startsWith (DOW)switch (c.get (Calendar.DAY_OF_WEEK)case Calendar.SUNDAY : pw.println (SUNDAY);break;case Calendar.MONDAY : pw.println (MOND

41、AY);13break;case Calendar.TUESDAY : pw.println (TUESDAY);break;case Calendar.WEDNESDAY: pw.println (WEDNESDAY);break;case Calendar.THURSDAY : pw.println (THURSDAY);break;case Calendar.FRIDAY : pw.println (FRIDAY);break;case Calendar.SATURDAY : pw.println (SATURDAY);/ If client program sends DOY (Day

42、 of Year) command,/ return current day of year to the client program.if (cmd.startsWith (DOY)pw.println ( + c.get (Calendar.DAY_OF_YEAR);/ If client program sends PAUSE command, sleep for three/ seconds.if (cmd.startsWith (PAUSE)tryThread.sleep (3000);14catch (InterruptedException e)while (true);cat

43、ch (IOException e)System.out.println (e.toString ();finallySystem.out.println (Closing Connection.n);tryif (br != null)br.close ();if (pw != null)pw.close ();if (s != null)s.close ();15運(yùn)行這段程序?qū)⒌玫较旅娴妮敵觯篠erver starting.Accepting Connection.Closing Connection.SSServer 的源代碼聲明了一對(duì)類(lèi):SSServer 和 ServerThread;

44、SSServer 的 main()方法創(chuàng)建了一個(gè) ServerSocket 對(duì)象來(lái)監(jiān)聽(tīng)端口 10000 上的連接請(qǐng)求,如果成功, SSServer 進(jìn)入一個(gè)無(wú)限循環(huán)中,交替調(diào)用 ServerSocket 的 accept() 方法來(lái)等待連接請(qǐng)求,同時(shí)啟動(dòng) 后臺(tái)線(xiàn)程處理連接(accept()返回的請(qǐng)求)。線(xiàn)程由 ServerThread 繼承的 start ()方法開(kāi)始, 并執(zhí)行 ServerThread 的 run()方法中的代碼。一旦 run()方法運(yùn)行,線(xiàn)程將創(chuàng)建 BufferedReader, PrintWriter 和 Calendar 對(duì)象并進(jìn) 入一個(gè)循環(huán),這個(gè)循環(huán)由讀(通過(guò) Buf

45、feredReader 的 readLine())來(lái)自客戶(hù)程序的一行文本 開(kāi)始,文本(命令)存儲(chǔ)在 cmd 引用的 string 對(duì)象中,如果客戶(hù)程序過(guò)早的關(guān)閉輸出流,會(huì)發(fā) 生什么呢?答案是:cmd 將得不到賦值。注意必須考慮到這種情況:在服務(wù)程序正在讀輸入流時(shí),客戶(hù)程序關(guān)閉了輸出流,如果沒(méi) 有對(duì)這種情況進(jìn)行處理,那么程序?qū)a(chǎn)生異常。一旦編譯了 SSServer 的源代碼,通過(guò)輸入 Java SSServer 來(lái)運(yùn)行程序,在開(kāi)始運(yùn)行SSServer 后,就可以運(yùn)行一個(gè)或多個(gè) SSClient 程序。三、Java網(wǎng)絡(luò)編程精解之ServerSocket用法詳解一 1在客戶(hù)/服務(wù)器通信模式中,服務(wù)器

46、端需要?jiǎng)?chuàng)建監(jiān)聽(tīng)特定端口的 ServerSocket, ServerSocket 負(fù)責(zé)接收客戶(hù)連接請(qǐng)求。本章首先介紹 ServerSocket 類(lèi)的各個(gè)構(gòu)造方法,以 及成員方法的用法,接著介紹服務(wù)器如何用多線(xiàn)程來(lái)處理與多個(gè)客戶(hù)的通信任務(wù)。16本章提供線(xiàn)程池的一種實(shí)現(xiàn)方式。線(xiàn)程池包括一個(gè)工作隊(duì)列和若干工作線(xiàn)程。服務(wù)器程序 向工作隊(duì)列中加入與客戶(hù)通信的任務(wù),工作線(xiàn)程不斷從工作隊(duì)列中取出任務(wù)并執(zhí)行它。本章還介 紹了 java.util.concurrent 包中的線(xiàn)程池類(lèi)的用法,在服務(wù)器程序中可以直接使用它們。3.1 構(gòu)造 ServerSocketServerSocket 的構(gòu)造方法有以下幾種重載形式

47、:ServerSocket()throws IOExceptionServerSocket(int port) throws IOExceptionServerSocket(int port, int backlog) throws IOExceptionServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException在以上構(gòu)造方法中,參數(shù) port 指定服務(wù)器要綁定的端口(服務(wù)器要監(jiān)聽(tīng)的端口),參數(shù)backlog 指定客戶(hù)連接請(qǐng)求隊(duì)列的長(zhǎng)度,參數(shù) bindAddr 指定服務(wù)器要綁定的 IP 地址。3.1.

48、1 綁定端口 除了第一個(gè)不帶參數(shù)的構(gòu)造方法以外,其他構(gòu)造方法都會(huì)使服務(wù)器與特定端口綁定,該端口由參數(shù) port 指定。例如,以下代碼創(chuàng)建了一個(gè)與 80 端口綁定的服務(wù)器:ServerSocket serverSocket=new ServerSocket(80);如果運(yùn)行時(shí)無(wú)法綁定到 80 端口,以上代碼會(huì)拋出 IOException,更確切地說(shuō),是拋出BindException,它是 IOException 的子類(lèi)。BindException 一般是由以下原因造成的:端口已經(jīng)被其他服務(wù)器進(jìn)程占用;在某些操作系統(tǒng)中,如果沒(méi)有以超級(jí)用戶(hù)的身份來(lái)運(yùn)行服務(wù)器程序,那么操作系統(tǒng)不允許服務(wù) 器綁定到 1

49、1023 之間的端口。如果把參數(shù) port 設(shè)為 0,表示由操作系統(tǒng)來(lái)為服務(wù)器分配一個(gè)任意可用的端口。由操作系 統(tǒng)分配的端口也稱(chēng)為匿名端口。對(duì)于多數(shù)服務(wù)器,會(huì)使用明確的端口,而不會(huì)使用匿名端口,因 為客戶(hù)程序需要事先知道服務(wù)器的端口,才能方便地訪(fǎng)問(wèn)服務(wù)器。在某些場(chǎng)合,匿名端口有著特 殊的用途。3.1.2 設(shè)定客戶(hù)連接請(qǐng)求隊(duì)列的長(zhǎng)度 當(dāng)服務(wù)器進(jìn)程運(yùn)行時(shí),可能會(huì)同時(shí)監(jiān)聽(tīng)到多個(gè)客戶(hù)的連接請(qǐng)求。例如,每當(dāng)一個(gè)客戶(hù)進(jìn)程執(zhí)行以下代碼:Socket socket=new Socket(,80);就意味著在遠(yuǎn)程主機(jī)的 80 端口上,監(jiān)

50、聽(tīng)到了一個(gè)客戶(hù)的連接請(qǐng)求。管理客戶(hù)連接請(qǐng)求的任務(wù)是由操作系統(tǒng)來(lái)完成的。操作系統(tǒng)把這些連接請(qǐng)求存儲(chǔ)在一個(gè)先進(jìn)先出 的隊(duì)列中。許多操作系統(tǒng)限定了隊(duì)列的最大長(zhǎng)度,一般為 50。當(dāng)隊(duì)列中的連接請(qǐng)求達(dá)到了隊(duì)列 的最大容量時(shí),服務(wù)器進(jìn)程所在的主機(jī)會(huì)拒絕新的連接請(qǐng)求。只有當(dāng)服務(wù)器進(jìn)程通過(guò)17ServerSocket的accept()方法從隊(duì)列中取出連接請(qǐng)求,使隊(duì)列騰出空位時(shí),隊(duì)列才能繼續(xù)加入 新的連接請(qǐng)求。對(duì)于客戶(hù)進(jìn)程,如果它發(fā)出的連接請(qǐng)求被加入到服務(wù)器的隊(duì)列中,就意味著客戶(hù)與服務(wù)器 的連接建立成功,客戶(hù)進(jìn)程從 Socket 構(gòu)造方法中正常返回。如果客戶(hù)進(jìn)程發(fā)出的連接請(qǐng)求被服 務(wù)器拒絕,Socket 構(gòu)造方

51、法就會(huì)拋出 ConnectionException。ServerSocket 構(gòu)造方法的 backlog 參數(shù)用來(lái)顯式設(shè)置連接請(qǐng)求隊(duì)列的長(zhǎng)度,它將覆蓋操 作系統(tǒng)限定的隊(duì)列的最大長(zhǎng)度。值得注意的是,在以下幾種情況中,仍然會(huì)采用操作系統(tǒng)限定的 隊(duì)列的最大長(zhǎng)度:backlog 參數(shù)的值大于操作系統(tǒng)限定的隊(duì)列的最大長(zhǎng)度;backlog 參數(shù)的值小于或等于 0;在 ServerSocket 構(gòu)造方法中沒(méi)有設(shè)置 backlog 參數(shù)。以下例程 3-1 的 Client.java 和例程 3-2 的 Server.java 用來(lái)演示服務(wù)器的連接請(qǐng)求隊(duì)列 的特性。例程 3-1 Client.javaimpor

52、t .*;public class Client public static void main(String args)throws Exceptionfinal int length=100;String host=localhost;int port=8000;Socket sockets=new Socketlength;for(int i=0;isocketsi=new Socket(host, port);System.out.println(第+(i+1)+次連接成功);Thread.sleep(3000);for(int i=0;isocketsi.close(

53、);/斷開(kāi)連接18例程 3-2 Server.javaimport java.io.*;import .*;public class Server private int port=8000;private ServerSocket serverSocket;public Server() throws IOException serverSocket = new ServerSocket(port,3);/連接請(qǐng)求隊(duì)列的長(zhǎng)度為 3System.out.println(服務(wù)器啟動(dòng));public void service() while (true) Socket socket=null;try socket = serverSocket.accept();/從連接請(qǐng)求隊(duì)列中取出一個(gè)連接System.out.println(New connection accepted +socket.getInetAddress() + : +socket.getPort();catch (IOException e) e.printStackTrace();finally tryif(socket!=null)socket.close(

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 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ì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論