計算機應用實現了多臺計算機間的互聯,使得它們彼此之間能夠進行數據交流。網絡應用程序就是在已連接的不同計算機上運行的程序,這些程序借助于網絡協議,相互之間可以交換數據。編寫網絡應用程序前,首先必須明確所要使用的網絡協議。TCP/IP協議是網絡應用程序的首選
21.1 網絡程序設計基礎
網絡程序設計編寫的是與其他計算機進行通信的程序。Java已經將網絡程序所需要的元素封裝成不同的類,用戶只要創建這些類的對象,使用相應的方法,即使不具備有關的網絡知識,也可以編寫出高質量的網絡通信程序。
21.1.1 局域網與互聯網
為了實現兩臺計算機的通信,必須用一個網絡線路連接兩臺計算機
服務器是指提供信息的計算機或程序,客戶機是指請求信息的計算機或程序。網絡用于連接服務器與客戶機,實現兩者間的相互通信。但是,有時在某個網絡中很難將服務器與客戶機區分開。我們通常所說的局域網(Local Area Network,LAN),就是一群通過一定形式連接起來的計算機,它可以由兩臺計算機組成,也可以由同一區域內的上千臺計算機組成。將LAN延伸到更大的范圍,這樣的網絡稱為廣域網(Wide Area Network,WAN)。我們熟悉的互聯網(Internet),就是由無數的LAN和WAN組成的。
21.1.2 網絡協議
網絡協議規定了計算機之間連接的物理、機械(網線與網卡的連接規定)、電氣(有效的電平范圍)等特征,計算機之間的相互尋址規則,數據發送沖突的解決方式,長數據如何分段傳送與接收等內容。就像不同的國家有不同的法律一樣,目前網絡協議也有多種。下面簡單地介紹幾個常用的網絡協議。
1.IP協議
IP是Internet Protocol的簡稱,是一種網絡協議。Internet網絡采用的協議是TCP/IP協議,其全稱是Transmission Control Protocol/Internet Protocol。Internet依靠TCP/IP協議,在全球范圍內實現了不同硬件結構、不同操作系統、不同網絡系統間的互聯。在Internet網絡上存在著數以億計的主機,每臺主機都用網絡為其分配的Internet地址代表自己,這個地址就是IP地址。到目前為止,IP地址用4個字節,也就是32位的二進制數來表示,稱為IPv4。為了便于使用,通常取用每個字節的十進制數,并且每個字節之間用圓點隔開來表示IP地址,如192.168.1.1。現在人們正在試驗使用16個字節來表示IP地址,這就是IPv6,但IPv6還沒有投入使用。
TCP/IP模式是一種層次結構,共分為4層,分別為應用層、傳輸層、互聯網層和網絡層。各層實現特定的功能,提供特定的服務和訪問接口,并具有相對的獨立性。
2.TCP與UDP協議
在TCP/IP協議棧中,有兩個高級協議是網絡應用程序編寫者應該了解的,即傳輸控制協議(Transmission Control Protocol,TCP)與用戶數據報協議(User Datagram Protocol,UDP)。
TCP協議是一種以固接連線為基礎的協議,它提供兩臺計算機間可靠的數據傳送。TCP可以保證數據從一端送至連接的另一端時,能夠確實送達,而且抵達的數據的排列順序和送出時的順序相同。因此,TCP協議適合可靠性要求比較高的場合。就像撥打電話,必須先撥號給對方,等兩端確定連接后,相互才能聽到對方說話,也知道對方回應的是什么。
HTTP、FTP和Telnet等都需要使用可靠的通信頻道。例如,HTTP從某個URL讀取數據時,如果收到的數據順序與發送時不相同,可能就會出現一個混亂的HTML文件或是一些無效的信息。
UDP是無連接通信協議,不保證數據的可靠傳輸,但能夠向若干個目標發送數據,或接收來自若干個源的數據。UDP以獨立發送數據包的方式進行。這種方式就像郵遞員送信給收信人,可以寄出很多信給同一個人,且每一封信都是相對獨立的,各封信送達的順序并不重要,收信人接收信件的順序也不能保證與寄出信件的順序相同。
UDP協議適合于一些對數據準確性要求不高,但對傳輸速度和時效性要求非常高的網站,如網絡聊天室、在線影片等。這是由于TCP協議在認證上存在額外耗費,可能使傳輸速度減慢,而UDP協議即使有一小部分數據包遺失或傳送順序有所不同,也不會嚴重危害該項通信。
注意
一些防火墻和路由器會設置成不允許UDP數據包傳輸,因此若遇到UDP連接方面的問題,應先確定所在網絡是否允許UDP協議。
21.1.3 端口與套接字
一般而言,一臺計算機只有單一的連到網絡的物理連接(Physical Connection),所有的數據都通過此連接對內、對外送達特定的計算機,這就是端口。網絡程序設計中的端口(port)并非真實的物理存在,而是一個假想的連接裝置。端口被規定為一個在0~65535的整數。HTTP服務一般使用80端口,FTP服務使用21端口。假如一臺計算機提供了HTTP、FTP等多種服務,那么客戶機會通過不同的端口來確定連接到服務器的哪項服務上,如圖21.3所示。
通常,0~1023的端口數用于一些知名的網絡服務和應用,用戶的普通網絡應用程序應該使用1024以上的端口數,以避免端口號與另一個應用或系統服務所用端口沖突。
網絡程序中的套接字(Socket)用于將應用程序與端口連接起來。套接字是一個假想的連接裝置,就像插座一樣可連接電器與電線,如圖21.4所示。Java將套接字抽象化為類,程序設計者只需創建Socket類對象,即可使用套接字。
21.2 TCP程序
TCP網絡程序設計是指利用Socket類編寫通信程序。利用TCP協議進行通信的兩個應用程序是有主次之分的,一個稱為服務器程序,另一個稱為客戶機程序,兩者的功能和編寫方法大不一樣。
①——服務器程序創建一個ServerSocket(服務器端套接字)對象,調用accept()方法等待客戶機來連接。
②——客戶端程序創建一個Socket對象,請求與服務器建立連接。
③——服務器接收客戶機的連接請求,同時創建一個新的Socket對象與客戶建立連接。隨后服務器繼續等待新的請求。
21.2.1 InetAddress類
java.net包中的InetAddress類是與IP地址相關的類,利用該類可以獲取IP地址、主機地址等信息。
?使用InetAddress類的getHostName()和getHostAddress()方法獲得本地主機的本機名、本機IP地址。
注意
InetAddress類的方法會拋出UnknownHostException異常,所以必須進行異常處理。這個異常在主機不存在或網絡連接錯誤時發生。
21.2.2 ServerSocket類
java.net包中的ServerSocket類用于表示服務器套接字,其主要功能是等待來自網絡上的“請求”,它可通過指定的端口來等待連接的套接字。服務器套接字一次可以與一個套接字連接。如果多臺客戶機同時提出連接請求,服務器套接字會將請求連接的客戶機存入列隊中,然后從中取出一個套接字,與服務器新建的套接字連接起來。若請求連接數大于最大容納數,則多出的連接請求被拒絕。隊列的默認大小是50。
ServerSocket類的構造方法通常會拋出IOException異常,具體有以下幾種形式:
?ServerSocket():創建非綁定服務器套接字。
ServerSocket(int port):創建綁定到特定端口的服務器套接字。
ServerSocket(int port, int backlog):利用指定的backlog創建服務器套接字,并將其綁定到指定的本地端口號上。
ServerSocket(int port, int backlog, InetAddress bindAddress):使用指定的端口、偵聽backlog和要綁定到的本地IP地址創建服務器。這種情況適用于計算機上有多塊網卡和多個IP地址的情況,用戶可以明確規定ServerSocket在哪塊網卡或哪個IP地址上等待客戶的連接請求。
調用ServerSocket類的accept()方法,會返回一個和客戶端Socket對象相連接的Socket對象。服務器端的Socket對象使用getOutputStream()方法獲得的輸出流,將指向客戶端Socket對象使用getInputStream()方法獲得的那個輸入流;同樣,服務器端的Socket對象使用getInputStream()方法獲得的輸入流,將指向客戶端Socket對象使用getOutputStream()方法獲得的那個輸出流。也就是說,當服務器向輸出流寫入信息時,客戶端通過相應的輸入流就能讀取,反之亦然。
注意
accept()方法會阻塞線程的繼續執行,直到接收到客戶的呼叫。如果沒有客戶呼叫服務器,那么System.out.println("連接中")語句將不會執行。語句如果沒有客戶請求,accept()方法沒有發生阻塞,肯定是程序出現了問題。通常是使用了一個被其他程序占用的端口號,ServerSocket綁定沒有成功。
yu = server.accept();
System.out.println("連接中");
21.2.3 TCP網絡程序設計
明白了TCP程序工作的過程,就可以編寫TCP服務器程序了。在網絡編程中,如果只要求客戶機向服務器發送消息,不要求服務器向客戶機發送消息,稱為單向通信。客戶機套接字
和服務器套接字連接成功后,客戶機通過輸出流發送數據,服務器則通過輸入流接收數據。下面是簡單的單向通信的實例。
【例21.2】創建TCP/IP協議服務器
運行服務器端程序,將輸出提示信息,等待客戶呼叫。下面再來看一下客戶端程序。
編寫客戶端程序,將用戶在文本框中輸入的信息發送至服務器端,并將文本框中輸入的信息顯示在客戶端的文本域中。
說明
當一臺機器上安裝了多個網絡應用程序時,很可能指定的端口號已被占用。還可能遇到以前運行良好的網絡程序突然運行不了的情況,這種情況很可能也是由于端口被別的程序占用了。此時可以運行netstat-help來獲得幫助,使用netstat-an命令來查看該程序所使用的端口
21.3 UDP程序
用戶數據報協議(UDP)是網絡信息傳輸的另一種形式。基于UDP的通信和基于TCP的通信不同,基于UDP的信息傳遞更快,但不提供可靠性保證。使用UDP傳遞數據時,用戶無法知道數據能否正確地到達主機,也不能確定到達目的地的順序是否和發送的順序相同。雖然UDP是一種不可靠的協議,但如果需要較快地傳輸信息,并能容忍小的錯誤,可以考慮使用UDP。
基于UDP通信的基本模式如下:
將數據打包(稱為數據包),然后將數據包發往目的地。
接收別人發來的數據包,然后查看數據包。
發送數據包的步驟如下:
(1)使用DatagramSocket()創建一個數據包套接字。
(2)使用DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)創建要發送的數據包。
(3)使用DatagramSocket類的send()方法發送數據包。
接收數據包的步驟如下:
(1)使用DatagramSocket(int port)創建數據包套接字,綁定到指定的端口。
(2)使用DatagramPacket(byte[] buf, int length)創建字節數組來接收數據包。
(3)使用DatagramPacket類的receive()方法接收UDP包。
注意
DatagramSocket類的receive()方法接收數據時,如果還沒有可以接收的數據,在正常情況下receive()方法將阻塞,一直等到網絡上有數據傳來,receive()方法接收該數據并返回。如果網絡上沒有數據發送過來,receive()方法也沒有阻塞,肯定是程序有問題,大多數情況下是因為使用了一個被其他程序占用的端口號。
21.3.1 DatagramPacket類
java.net包的DatagramPacket類用來表示數據包。DatagramPacket類的構造方法如下:
DatagramPacket(byte[] buf, int length)。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)。
第一種構造方法在創建DatagramPacket對象時,指定了數據包的內存空間和大小。第二種構造方法不僅指定了數據包的內存空間和大小,還指定了數據包的目標地址和端口。在發
送數據時,必須指定接收方的Socket地址和端口號,因此使用第二種構造方法可創建發送數據的DatagramPacket對象。
21.3.2 DatagramSocket類
java.net包中的DatagramSocket類用于表示發送和接收數據包的套接字。該類的構造方法如下:
DatagramSocket()。
DatagramSocket(int port)。
DatagramSocket(int port, InetAddress addr)。
第一種構造方法創建DatagramSocket對象,構造數據報套接字,并將其綁定到本地主機任何可用的端口上。第二種構造方法創建DatagramSocket對象,創建數據報套接字,并將其綁定到本地主機的指定端口上。第三種構造方法創建DatagramSocket對象,創建數據報套接字,并將其綁定到指定的端口和指定的本地地址上。第三種構造函數適用于有多塊網卡和多個IP地址的情況。
如果接收數據時必須指定一個端口號,不允許系統隨機產生,此時可以使用第二種構造方法。比如有個朋友要你給他寫信,那他的地址就必須確定,不確定是不行的。在發送數據時通常使用第一種構造方法,不指定端口號,而是系統為我們分配一個端口號,就像寄信不需要到指定的郵局去寄一樣。
21.3.3 UDP網絡程序設計
根據前面所講的網絡編程的基本知識以及UDP網絡編程的特點,下面創建一個廣播數據報程序。廣播數據報是一項較新的技術,其原理類似于電臺廣播。廣播電臺需要在指定的波段和頻率上廣播信息,收聽者也要將收音機調到指定的波段、頻率,才可以收聽廣播內容。
【例21.3】創建UDP協議廣播電臺程序接收廣播程序。單擊“開始接收”按鈕,系統開始接收主機播出的信息;單擊“停止接收”按鈕,系統停止接收廣播主機播出的信息。代碼如下:
說明
發出廣播和接收廣播的主機地址必須位于同一個組內,地址范圍為224.0.0.0~224.255.255.255,該地址并不代表某個特定主機的位置。加入同一個組的主機可以在某個端口上廣播信息,也可以在某個端口上接收信息。
?