Java開發(fā)綜合實(shí)戰(zhàn) 教案 項(xiàng)目八 網(wǎng)絡(luò)編程基礎(chǔ)_第1頁
Java開發(fā)綜合實(shí)戰(zhàn) 教案 項(xiàng)目八 網(wǎng)絡(luò)編程基礎(chǔ)_第2頁
Java開發(fā)綜合實(shí)戰(zhàn) 教案 項(xiàng)目八 網(wǎng)絡(luò)編程基礎(chǔ)_第3頁
Java開發(fā)綜合實(shí)戰(zhàn) 教案 項(xiàng)目八 網(wǎng)絡(luò)編程基礎(chǔ)_第4頁
Java開發(fā)綜合實(shí)戰(zhàn) 教案 項(xiàng)目八 網(wǎng)絡(luò)編程基礎(chǔ)_第5頁
已閱讀5頁,還剩26頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

項(xiàng)目八

網(wǎng)絡(luò)編程基礎(chǔ)

思政目標(biāo)

A理論聯(lián)系實(shí)際,培養(yǎng)舉一反三、觸類旁通的學(xué)習(xí)能力。

A追根溯源,從基礎(chǔ)著手,培養(yǎng)科學(xué)嚴(yán)謹(jǐn)?shù)乃季S方式。

技能目標(biāo)

>能夠?qū)崿F(xiàn)簡單的TCP網(wǎng)絡(luò)通信

>能夠?qū)崿F(xiàn)簡單的UDP網(wǎng)絡(luò)通信

項(xiàng)目導(dǎo)讀

將物理位置不同、且具有獨(dú)立功能的多臺計(jì)算機(jī)連接在一起

就形成r網(wǎng)絡(luò)。網(wǎng)絡(luò)可以使不司物理位置上的計(jì)算機(jī)進(jìn)行資源共

享和通信。Java提供了專門的網(wǎng)絡(luò)開發(fā)程序包java.net,包含網(wǎng)

絡(luò)程序所需要的不同的類。用戶只要創(chuàng)建這些類的對象,調(diào)用相應(yīng)

的方法,即使不具備有關(guān)的網(wǎng)絡(luò)知識,也可以編寫出網(wǎng)絡(luò)通信程

序。

201

Java開發(fā)綜合實(shí)戰(zhàn)

任務(wù)1網(wǎng)絡(luò)程序設(shè)計(jì)基礎(chǔ)

任務(wù)引入

通過前面幾個項(xiàng)目的學(xué)習(xí),小白已逐步完成了一個簡易的單機(jī)版進(jìn)銷存管理系統(tǒng)。

考慮到應(yīng)用程序的實(shí)用性,小白想將單機(jī)版的管理系統(tǒng)擴(kuò)展為網(wǎng)絡(luò)版程序。在此之前,他

有必要先了解網(wǎng)絡(luò)應(yīng)用程序常用的設(shè)計(jì)模式、常用的網(wǎng)絡(luò)協(xié)議,以及IP地址和端口的概

念。

簞?知識法備]

一、網(wǎng)絡(luò)應(yīng)用程序設(shè)計(jì)模式

在網(wǎng)絡(luò)通信過程中往往會分為兩種端點(diǎn):服務(wù)端與客戶端,圍繞這兩種端點(diǎn)產(chǎn)生了

網(wǎng)絡(luò)程序開發(fā)的兩種模式C/S模式和B/S模式。在開發(fā)過程中,應(yīng)根據(jù)實(shí)際需求,并結(jié)

合各種模式的特點(diǎn)選擇應(yīng)用程序設(shè)計(jì)模式。

1.C/S(Client/Server,客戶端/服務(wù)端)模式?

這種模式將網(wǎng)絡(luò)事務(wù)處理分為客戶端和服務(wù)器端兩個部分??蛻舳擞糜跒橛脩籼峁?/p>

操作,同時向網(wǎng)絡(luò)提供請求服務(wù)的接口;服務(wù)端負(fù)責(zé)接收并處理客戶端發(fā)出的服務(wù)請求,

并將服務(wù)處理結(jié)果返回給客戶端。因此,這種模式要開發(fā)兩套程序,一套是客戶端,另一

套是服務(wù)端。在進(jìn)行維護(hù)時,也需要維護(hù)兩套程序,而且客戶端的程序史新也必須及時。

C/S模式的主要特點(diǎn)是交互性強(qiáng),具有安全的存取模式、網(wǎng)絡(luò)通信量低、響應(yīng)速度快,程

序安全性能好。

2.B/S(Browser/Server,瀏覽器/服務(wù)器)模式

這種模式是伴隨著Internet技術(shù)的興起,對C/S模式的改進(jìn),僅使用HTP進(jìn)行通

信,主要事務(wù)邏輯在服務(wù)器端實(shí)現(xiàn),無須安裝客戶端,Web瀏覽器即客戶端,因此只需

針對服務(wù)器端開發(fā)一套程序。這種程序在日后進(jìn)行程序維護(hù)時只需維護(hù)服務(wù)器端即可,

分布性強(qiáng),開發(fā)簡單,維護(hù)方便。但此類程序使用公共端口,包括公共協(xié)議,所以安全性

很差,對于實(shí)現(xiàn)復(fù)雜的應(yīng)用構(gòu)造也有較大的困難。

二、常用的網(wǎng)絡(luò)協(xié)議

從應(yīng)用的角度出發(fā),網(wǎng)絡(luò)協(xié)議可理解為網(wǎng)絡(luò)通信規(guī)則,是網(wǎng)絡(luò)中的各臺主機(jī)傳輸數(shù)

據(jù)進(jìn)行通信的規(guī)則說明,規(guī)定了計(jì)算機(jī)之間連接的特征、相互尋址規(guī)則、數(shù)據(jù)發(fā)送沖突的

解決方式、數(shù)據(jù)傳送與接收的方式等內(nèi)容。

網(wǎng)絡(luò)通信協(xié)議有很多,目前應(yīng)用最廣泛的是TC"IP、UDP、ICMP和其他一些協(xié)議的

202

項(xiàng)目八網(wǎng)絡(luò)編程基礎(chǔ)

協(xié)議族。本節(jié)簡單地介紹本項(xiàng)目的網(wǎng)絡(luò)編程主要涉及的TCP、UDP和IP。

1.TCP協(xié)議

TCP(TransmissionControlProtocol,傳輸控制協(xié)議)提供兩臺計(jì)算機(jī)之間可靠的

數(shù)據(jù)傳送,也就是說,可以保證數(shù)據(jù)能夠確實(shí)送達(dá),而且抵達(dá)的數(shù)據(jù)的排列順序和送出時

的順序相同。因此,TCP協(xié)議常用于可靠性要求比較高的場合。

2.UDP協(xié)議

UDP(UserDatagramProtocol,用戶數(shù)據(jù)報(bào)協(xié)議)是無連接通信協(xié)議,以獨(dú)立發(fā)送

數(shù)據(jù)報(bào)的方式向若干個目標(biāo)發(fā)送數(shù)據(jù),或接收來自若干個源的數(shù)據(jù),不保證數(shù)據(jù)的可靠

傳輸。也就是說,數(shù)據(jù)不一定能送達(dá),抵達(dá)的數(shù)據(jù)的排列順序和送出時的順序也不一定相

同。因此,IDP協(xié)議適用于一些對數(shù)據(jù)準(zhǔn)確性要求不高,但對傳輸速度和時效性要求非常

高的網(wǎng)站。

3.TCP/IP協(xié)議

TCP/IP(TransmissionControlProtoco1/InternetProtocol傳輸控制協(xié)議/網(wǎng)際

協(xié)議)是Inlernel最基本的通信協(xié)議,在全球范圍內(nèi)實(shí)現(xiàn)了不同硬件結(jié)構(gòu)、不同操作系

統(tǒng)、不同網(wǎng)絡(luò)系統(tǒng)之間的互聯(lián)。其中的每臺主機(jī)都用網(wǎng)絡(luò)為其分配的Internet地址(即

IP地址)進(jìn)行唯一標(biāo)識。

在這里要提請讀者注意的是,TCP/IP協(xié)議不僅僅指的是TCP和IP兩個協(xié)議,而是

指能夠在多個不同網(wǎng)絡(luò)間實(shí)現(xiàn)信息傳輸?shù)膮f(xié)議簇,由FTP、SMTP、TCP、UDP、IP等協(xié)議構(gòu)

成。由于TCP/IP協(xié)議中TCP協(xié)議和IP協(xié)議最具代表性,所以被稱為TCP/IP協(xié)議。

TCP/IP協(xié)議共分為4層,分別為應(yīng)用層、傳輸層、網(wǎng)絡(luò)層和數(shù)據(jù)鏈路層。各層實(shí)現(xiàn)

特定的功能,提供特定的服務(wù)和訪問接口,并具有相對的獨(dú)立性,如圖8—1所示。

應(yīng)用程序,Telnet、FTP、SMTP等

可靠的傳遞服務(wù),TCP和UDP

無連接分組投遞服務(wù),IP、ICMP和IGMP

物理層、網(wǎng)絡(luò)接口層,設(shè)備驅(qū)動程序及接口卡

圖8—1TCP/IP層次結(jié)構(gòu)

三、IP地址和端口

Internet上的主機(jī)有兩種方式表示地址:域名和IP地址。域名容易記憶,在連接網(wǎng)

絡(luò)時輸入一個主機(jī)的域名(例如WWW.baidu.com.cn)后,域名服務(wù)器(DNS)負(fù)責(zé)將域名

轉(zhuǎn)化成IP地址,這樣才能和主機(jī)建立連接。

在TCP/IP中,IP地址用于唯一地標(biāo)識一臺接收或發(fā)送數(shù)據(jù)的計(jì)算機(jī)。目前,廣泛

使用的IP地址版本是IA4,用4個字節(jié)的二進(jìn)制數(shù)表示。為便于記憶和處理,通常將IP

地址寫成十進(jìn)制的形式,每個字節(jié)用一個十進(jìn)制數(shù)字(0?255)表示,數(shù)字之間用符號

分開,如127.0.0」,該地址表示本機(jī)IP地址,通常用于測試本機(jī)TCP/IP是否正

203

項(xiàng)目八網(wǎng)絡(luò)編程基礎(chǔ)

知識準(zhǔn)備

在Java中,TCP網(wǎng)絡(luò)程序設(shè)計(jì)是指利用Socket類編寫通信程序,使用此類可以方便

地建立可靠的、雙向的、持續(xù)的、點(diǎn)對點(diǎn)的通信連接。利用TCP協(xié)議進(jìn)行通信的兩個應(yīng)用

程序是有主次之分的,一個稱為服務(wù)器端程序,另一個稱為客戶端程序,兩者的功能和編

寫方法大不一樣。

通信時,首先要創(chuàng)建代表服務(wù)器端的ServerSocket對象,開啟一個服務(wù),此服務(wù)會

等待客戶端的連接請求;然后創(chuàng)建代表客戶端的Socket對象,向服務(wù)器端發(fā)出連接請求,

服務(wù)器端響應(yīng)請求后,兩者才建立連接,通過套接字的I/O流開始通信。通信完畢后,使

用套接字的close()方法關(guān)閉連接。

一、實(shí)現(xiàn)服務(wù)器端程序

ServerSocket類的實(shí)例對象用于實(shí)現(xiàn)一個服務(wù)器端程序,在指定的端口等待接收客

戶端在該端口發(fā)送的TC?連接請求。

SprvprSoc.kpt類的構(gòu)造方法有以卜.幾種形式,通常會拋出TOExc.pption異常八

(1)ServerSocket()

創(chuàng)建沒有綁定端口的服務(wù)器套接字。這種方法創(chuàng)建的ServerSocket對象創(chuàng)建的服務(wù)

器端不監(jiān)聽任何端口,因此不能直接使用。后續(xù)操作中需要調(diào)用bind。方法將其綁定到

指定的端口上,才可以正常使用。

(2)ServerSocket(intport)

創(chuàng)建綁定到特定端口的服務(wù)器套接字。

(3)ServerSocket(intport,intbacklog)

創(chuàng)建綁定到特定端口的服務(wù)器套接字,并指定在服務(wù)器忙時可以與之保持連接請求

的等待客戶數(shù)量backlogo

服務(wù)器套接字一次可以與一個套接字連接。如果多個客戶端同時提出連接請求,服

務(wù)器套接字會將請求連接的客戶端存入列隊(duì)中,然后從中取出一個套接字,與服務(wù)器新

建的套接字連接起來。如果請求連接數(shù)大于隊(duì)列的最大容納數(shù)(默認(rèn)大小為50),則多出

的連接請求將被拒絕。

(4)ServerSocket(intport,intbacklog,InctAddressbindAddrcss)

使用指定的端口、偵聽backlog和要綁定到的IP地址創(chuàng)建服務(wù)器套接字。這種情況

適用于計(jì)算機(jī)上有多塊網(wǎng)卡和多個IP地址的情況,用戶可以明確規(guī)定ServerSocket在

哪塊網(wǎng)卡或哪個IP地址上等待客戶的連接請求。

例如,下面的代碼使用常用的第(2)種構(gòu)造方法創(chuàng)建一個綁定到2022端口的服務(wù)

器套接字:

try(

ServerSocketserverForClient=newServerSocket(2022);

}catch(lOExceptione){

205

Java開發(fā)綜合實(shí)戰(zhàn)

如果2022端口已被占用,就會拋出lOExccption異常。

ServerSocket類的常用方法如表8-2所示。

表8—2ServerSocket類的常用方法

方法說明

Socketaccept()等待并接收客戶端連接。如果連接成功,返問一個與發(fā)送

請求客戶端對應(yīng)的Socket實(shí)例

voidblind(SocketAddressendpoint)將ServerSocket綁定到特定地址

InetAddressgetlnetAddress()獲取服務(wù)器套按字的本地地址

intgctLocalPort()獲取服務(wù)器套接字偵聽的端口

booleanisBoundO判斷服務(wù)器套接字的綁定狀態(tài)

booleanisClosedO返回服務(wù)器套接字的關(guān)閉狀態(tài)

voidclose()關(guān)閉服務(wù)器套接字

創(chuàng)建ServerSocket對象后,如果要接收來自客戶端的請求,需要調(diào)用ServerSocket

對象的accept。方法獲取客戶端連接。該方法會阻塞線程的執(zhí)行,直到服務(wù)器端接收到

客戶端的連接請求,返回一個與發(fā)出請求的客戶端Socket對象相連接的Socket對象實(shí)

例,程序才叮以向卜繼續(xù)執(zhí)行。例如,如果沒有接收到客戶端的連接請求,第二條語句就

不會執(zhí)行:

socket=server,accept();

System.out.printIn("Welcome!”);

提示:如果沒有客戶端請求,accept。方法沒有發(fā)生阻塞,肯定是程序出現(xiàn)了問題。

通常是使用了一個被其他程序占用的端口號,ServerSocket綁定沒有成功。

ServerSocket對象可以調(diào)用setSoTimeout(inttimeout)方法設(shè)置單位為毫秒的超

時值,一旦ServerSocket對象調(diào)用accept()方法阻塞的時間超過timeout,將觸發(fā)

SocketTimeoutException0

建立連接后,服務(wù)器端ServerSocket對象調(diào)用getlnetAddress()方法可以獲取包含

客戶端IP地址和域名的TnetAddess對象;同樣,客戶端的套接字對象調(diào)用

getlnetAddress。方法可以獲取包含服務(wù)器端IP地址和域名的InetAddess對象。

二、實(shí)現(xiàn)客戶端程序

Socket類用于實(shí)現(xiàn)TCP客戶端程序。客戶端負(fù)責(zé)創(chuàng)建連接到服務(wù)器的套接字對象,

發(fā)出連接請求,井與服務(wù)器通佶。

Sockel類的構(gòu)造方法有以下幾種形式,通常會拋出lOException異常。

(1)Socket()

創(chuàng)建沒有連接任何服務(wù)器的客戶端套接字對象c這種方法創(chuàng)建的Socket對象不能直

接使用,后續(xù)操作中需要調(diào)用connect。方法指定封裝了服務(wù)器端IP地址和端口號的

SocketAddress對象,才可以與指定的服務(wù)器端連接。

(2)Socket(Stringhost,intport)

創(chuàng)建連接到運(yùn)行在指定地址和端II上的服務(wù)器程序的Socket對象。參數(shù)host是服

206

項(xiàng)目八網(wǎng)絡(luò)編程基礎(chǔ)

務(wù)器的IP地址,port是一個端口號。

(3)Socket(InetAdressaddress,intport)

這種方法與第二種方法類似,創(chuàng)建連接到運(yùn)行在指定地址和端口上的服務(wù)器程序的

Socket對象。

例如,下面的代碼使用第(2)種構(gòu)造方法創(chuàng)建一個連接到服務(wù)器的客戶端套接字:

try(

Socketclient=newSocket("http:〃192.168.10.110",2022):

}catch(lOExceptione){)

Socket類的常用方法如表8-3所示。

表8—3Socket類的常用方法

方法說明

intgetPort()返回Socket對象與服務(wù)器端連接的端口號

InetAddressgetLocalAddress()將Socket對象綁定的本地IP地址封裝成InetAddress對

象并返回

InputstreamgetInputstream()獲取Socket對象的輸入流

OuxputStreamgetOutputStreamO獲取Socket對象的輸出流

booleanisClosed()判斷Socket連接是否已關(guān)閉

voidclose()關(guān)閉Socket連接,結(jié)束本次通信

在關(guān)閉Socket連接之前,應(yīng)先關(guān)閉與Socket相關(guān)的所有I/O流,以釋放相關(guān)資源。

三、數(shù)據(jù)交互通信

客戶端創(chuàng)建一個Socket對象向指定的主機(jī)和端口發(fā)送請求,服務(wù)器端的

ServerSocket對象監(jiān)聽客戶端在該端口發(fā)送的TCP請求。接收到TCP連接請求后,服務(wù)

器端的ServerSocket對象調(diào)用accept()方法獲取客戶端連接,并創(chuàng)建一個駐留在服務(wù)器

端的Socket實(shí)例。客戶端Socket與服務(wù)器端Socket連接成功后,通過I/O流交互數(shù)

據(jù),通過輸出流發(fā)送數(shù)據(jù),通過輸入流接收數(shù)據(jù),從而實(shí)現(xiàn)通信,如圖8—2所示。

客戶端服務(wù)器端

InputstreamInputstream

題據(jù)傳輸

SocketSocket

OutputstreamaOutputstream

圖8—2服務(wù)器端與客戶端的數(shù)據(jù)交互

客戶端Socket對象調(diào)用getlnputStreamO方法獲得一個輸入流,這個輸入流指向服

務(wù)器端的Socket對象使用getOutputStreamO方法獲得的輸出流,也就是說,客戶端用

207

Java開發(fā)綜合實(shí)戰(zhàn)

輸入流可以讀取服務(wù)器寫入到輸出流中的數(shù)據(jù)??蛻舳薙ocket對象調(diào)用

gct()utputStream()方法獲得一個輸出流,這個輸出流的目的地與服務(wù)器端的Socket對

象使用gctlnputStreamO方法獲得的輸入流的源相同,也就是說,客戶端通過輸出流寫

入的數(shù)據(jù),服務(wù)器通過輸入流可以讀取。反之亦然,

注意:從Socket連接中讀取數(shù)據(jù)與從文件中讀取數(shù)據(jù)有著很大的不同。從文件中讀

取數(shù)據(jù)時,所有的數(shù)據(jù)都已經(jīng)存在文件中。而使用Socket連接時,可能在另一端數(shù)據(jù)發(fā)

送之前,就已經(jīng)開始讀取了,這時,就會阻塞本線程,直到該讀取方法成功讀取到信息,

本線程才繼續(xù)執(zhí)行后續(xù)的操作。

雙方通信完畢后,Socket對象應(yīng)調(diào)用closeO方法關(guān)閉Socket連接。

案例一一模擬問答式交互

本案例利用Socket完成一個TCP程序,模擬向答式交互。

(1)在Eclipse中新建一個名為TCPDcm。的Java項(xiàng)目,然后在其中添加一個名為

TCPServer.java的類,編寫服務(wù)器端程序,具體代碼如下:

importjava.io.DataOutputStrcam;

importjava.io.lOExceptian;

importjava.net.ServerSocket;

importjava.net.Socket;

publicclassTCPServer{

publicstaticvoidmain(String[]args){

ServerSocketserver=null:初始化服務(wù)器套接字

Socketsk=null;〃初始化駐留服務(wù)器端的套接字

〃服務(wù)器端將要輸出到客戶端的字符串

String[]answers={"你好!我是Server。二匕京","2015年7月31口","冬夢"上

DataOutputStreamout=null;〃初始化輸出流

try{

server=newServerSocket(3000);〃創(chuàng)建綁定到3000端II的服務(wù)器套接字

}catch(IOExceptione){

System,out.println(e);

)

try(

System.out.printIn("服務(wù)器準(zhǔn)備就緒,等待客戶請求...”);

〃阻塞線程,直到接收到客戶端的連接請求,然后為請求創(chuàng)建套接字sk

sk=server,accept();

out=newDataOutputStream(sk.getOutputStreamO);,1'實(shí)例化輸出流

for(inti=0;i<answers.1ength;i++){

out.writeUTF(answers[i]);〃寫入要返回到客戶端的消息

208

項(xiàng)目八網(wǎng)絡(luò)編程基礎(chǔ)

Thread.s/eep(1000);〃休眠1秒

)

}catch(Exceptione){〃沒有連接請求,捕獲異常

System,out.println(e);

}

try{

〃關(guān)閉輸出流和socket連接

out.close();

sk.closeO;

}catch(Exceptione){}

)

}

(2)在項(xiàng)目中添加一個名為TCPClient的類,編寫客戶端程序,具體代碼如下:

importjava.io.DatalnputStream;

importjava.io.DataOutputStream;

import.Socket;

publicclassTCPC1ient{

publicstaticvoidmain(String口args){

Socketcsk=null;〃聲明客戶端套接字

〃初始化輸入流和輸出流

DataInputStreairin=null;

DataOutputStreamout=null;

〃客戶端將要發(fā)送到服務(wù)器端的字符串

String[]questions={"你好!我是Client.哪個城市是世界上首個雙奧之城?”,

”北京成功獲得2022年冬奧會舉辦權(quán)是哪一天?北京2022年冬殘奧會會徽的名字是什么?”};

try(

〃使用客戶端Sockel對象也連接到運(yùn)行在指定地址和端口上的服務(wù)器程序

csk=newSocketf127.0.0.1”,3000);

〃如果接收到客戶請求,獲取客戶端的IP地址并輸出

Stringaddr=csk.getlnctAddrcss().getllostAddressO;

System.out.printIn(addr+"響應(yīng)請求”);

〃實(shí)例化輸入流和輸出流

in=newDatalnputStream(csk.getInputstream0);

out=newDataOutputStream(csk.getOutputStreamO);

for(inti=0;i<questions.length;i++){

Systen.out.printin(z/Client|SJ:/z+questions[i]);

Thread.s/eM(500);

209

Java開發(fā)綜合實(shí)戰(zhàn)

out.writcUTF(questions[i]);/7寫入要發(fā)送到服務(wù)器端的消息

Stringinstr=in.readUTFO;〃讀取服務(wù)器端返回的消息,會阻塞狀態(tài)

Syston.ouC.printlnC'server答:"+instr);輸出讀取的消息

Thread,s/e改(500);

}

}catch(Exceptione){//連接失敗

System.out.printin(e);

)

try{

〃關(guān)閉輸入流、輸出流和socket連接

in.closeO;

out.closeO;

csk.closeO;

}catch(Exceptione){}

)

)

(3)運(yùn)行TCPServer.java啟動服務(wù)器,等候客戶端的請求,如圖8—3所示。然后

運(yùn)行TCPClient.java,客戶端與服務(wù)器連接,通過輸入流讀取服務(wù)器返回的消息并輸出,

如圖8—4所示。

日ConsoleX@DeclarationD□

日ConsoleX島Declarati...a□-*派|?&地?母馴

圖砧翩母喇

■X-1已日▼n▼

<termnated>TCPClient(1)[JavaApplication]D:\S

-日▼F3▼

=??e^3JA

TCPServer(1)[JavaApplication]D:\R件\e<

Client?)i仁慶盤Client,

蕓務(wù)器專缶?等符尸,求一-

tit.5servers:體好:我是Server.

Client同:更個w七是M界上口個五具之城:

servers:故

Client閭:北京比5:天用2022=多莢金=分灰懸石一大:

servers:2015=7.531=

ClientiC,北京2022三冬殘禹會會名韻^字專一幺?

servers冬》

圖8—3服務(wù)器端運(yùn)行效果圖8-4客戶端運(yùn)行效果

任務(wù)3實(shí)現(xiàn)UDP網(wǎng)絡(luò)程序

210

項(xiàng)目八網(wǎng)絡(luò)編程基礎(chǔ)

任務(wù)引入

掌握了TCP網(wǎng)絡(luò)程序的實(shí)現(xiàn)方法,小白想知道常見的UDP程序與TCP程序有哪些異

同,以及實(shí)現(xiàn)UDP程序的方法。

知識準(zhǔn)備

UDP程序也分為服務(wù)器端和客戶端兩部分。與TCP必須建立可靠連接,提供端到端的

服務(wù)不同,UDP是一種面向無連接的協(xié)議,UDP服務(wù)器為所有通信使用同一套接字,因此,

在通信時發(fā)送端和接收端不用建立連接,發(fā)送的消息對方不一定能接收到,是相對不可

靠的傳輸控制協(xié)議。

基于UDP通信的基本模式為:將數(shù)據(jù)打包發(fā)送一接收數(shù)據(jù)報(bào)??蛻舳撕头?wù)器端都

使用DatagramPacket對象封裝需要發(fā)送或者接收的數(shù)據(jù),通過DatagramSocket對象的

send。方法和receive。方法發(fā)送和接收數(shù)據(jù)。通信完成后,客戶端調(diào)用DatagramSocket

實(shí)例的close。方法關(guān)閉該套接字。

一、發(fā)送數(shù)據(jù)報(bào)

UDP通信中,發(fā)送數(shù)據(jù)報(bào)的步驟如下:

(1)使用DatagramSocket類的構(gòu)造方法創(chuàng)建一個數(shù)據(jù)報(bào)套接字。

(2)使用DatagramPacket類的構(gòu)造方法將要發(fā)送的數(shù)據(jù)封裝成數(shù)據(jù)報(bào)。

(3)使用DatagramSocket對象的send。方法發(fā)送數(shù)據(jù)報(bào)。

1.創(chuàng)建數(shù)據(jù)報(bào)套接字

Java在java,net包中提供DatagramSocket類用于表示發(fā)送或接收數(shù)據(jù)報(bào)的套接字。

在發(fā)送端通常使用DatagramSocket類的尢參構(gòu)造方法DatagramSocket()創(chuàng)建發(fā)送端的

數(shù)據(jù)報(bào)套接字,由于沒有指定端口號,系統(tǒng)會分配一個沒有被占用的端口號。

在客戶端創(chuàng)建DatagramSocket實(shí)例時,也可以有選擇地對本地地址和端口號進(jìn)行設(shè)

置。如果設(shè)置了端口號,則客戶端會在該端口號上監(jiān)聽從服務(wù)器端發(fā)送來的數(shù)據(jù)。在服務(wù)

器端創(chuàng)建DatagramSocket實(shí)例時,指定本地端口號,并可以有選擇地指定本地地址,此

時,服務(wù)器已經(jīng)準(zhǔn)備好從任何客戶端接收數(shù)據(jù)。

創(chuàng)建DatagramSocket對象時可能會拋出SocketException異常。

2.創(chuàng)建數(shù)據(jù)報(bào)

Java使用DatagramPacket對象表示數(shù)據(jù)報(bào),封裝UDP逋信中發(fā)送或接收的數(shù)據(jù)。

在創(chuàng)建用于包裝待發(fā)送的數(shù)據(jù)的DatagramPacket實(shí)例時,要指定要發(fā)送到的目的主

機(jī)和端口。因此,通常采用以下的構(gòu)造方法:

DatagramPacket(byte[]data,intlength,InetAddressaddr,intport)

這種方法創(chuàng)建數(shù)據(jù)報(bào),并指定封裝的數(shù)據(jù)、大小以及數(shù)據(jù)報(bào)的目標(biāo)IP地址和端口號。

用于將含有data數(shù)組的數(shù)據(jù)封裝成數(shù)據(jù)報(bào),發(fā)送到地址為addr的主機(jī)的指定端口port。

DatagramPacket類還提供了一些用于獲取數(shù)據(jù)報(bào)的內(nèi)容信息的方法,如表8-4所

不。

211

Java開發(fā)綜合實(shí)戰(zhàn)

表8—4DatagramPackct類的常用方法

方法說明

intgetPort()如果是發(fā)送端的數(shù)據(jù)報(bào),則返回接收端的端口號,否則返回發(fā)送端的端口號

InetAddress如果是發(fā)送端的數(shù)據(jù)報(bào),則返回接攸端的IP,否則返回發(fā)送端的IP

getAddressO

byte[]getDataO如果是發(fā)送端的數(shù)據(jù)報(bào),則返問將要發(fā)送的數(shù)據(jù),否則返P1將要接收的數(shù)據(jù)

inigetLenglh()如果是發(fā)送端的數(shù)據(jù)報(bào),則返回將要發(fā)送的數(shù)據(jù)的長度,否則返回將要接收

的數(shù)據(jù)的長度

3.發(fā)送數(shù)據(jù)報(bào)

創(chuàng)建數(shù)據(jù)報(bào)后,使用DatagramSocket對象的send(DatagramPakcetdp)方法,即可

發(fā)送指定的數(shù)據(jù)報(bào)dp,該數(shù)據(jù)報(bào)中不僅包含要發(fā)送的數(shù)據(jù),還包含數(shù)據(jù)的長度、目標(biāo)主

機(jī)的IP地址和端口號。

發(fā)送數(shù)據(jù)報(bào)時可能會產(chǎn)生lOException異常。

二、接收數(shù)據(jù)報(bào)

UDP通信中,接收數(shù)據(jù)報(bào)的步驟如下:

(1)使用DatagramSocket類的構(gòu)造方法創(chuàng)建一個數(shù)據(jù)報(bào)套接字,綁定到指定的端

口。

(2)使用DatagramPacket類的構(gòu)造方法創(chuàng)建數(shù)據(jù)報(bào)封裝接收到的數(shù)據(jù)。

(3)使用DatagramSocket對象的receive()方法接收數(shù)據(jù)報(bào)。

1.創(chuàng)建數(shù)據(jù)報(bào)套接字

與發(fā)送端不同,接收端的DatagramSocket對象必須指定--個端口號進(jìn)行監(jiān)聽,不能

是系統(tǒng)隨機(jī)分配的端口號。創(chuàng)建接收端的數(shù)據(jù)報(bào)套接字可以采用以下兩種構(gòu)造方法:

/DatagramSocket(intport):創(chuàng)建DatagramSocket對象,并將其綁定到本地主

機(jī)指定的端口上。

/DatagramSocket(intport,TnetAddressaddress):倉U建DatagramSocket對

象,并將其綁定到指定的本地地址上。這種方法常用于有多塊網(wǎng)卡和多個IP地

址的情況。

2.創(chuàng)建接收信息的數(shù)據(jù)報(bào)

在創(chuàng)建用于封裝待接收的數(shù)據(jù)的DatagramPackct實(shí)例時,不需要指定數(shù)據(jù)來源的遠(yuǎn)

程主機(jī)和端口,只需指定一個緩存數(shù)據(jù)的byte數(shù)組即可。通常使用如下的構(gòu)造方法實(shí)現(xiàn):

DatagramPacket(byte[]data,intlength)

這種方法創(chuàng)建一個%lagramPacket實(shí)例,并預(yù)先分配空間和大小,以將后續(xù)接收到

的數(shù)據(jù)存放在該空間中。發(fā)送數(shù)據(jù)的源地址和端口等信息會自動包含在DatagramPacket

實(shí)例中。

3.接收數(shù)據(jù)報(bào)

使用DatagramSocket對象的receive(DatagrauPakcetdp)方法,即可將接收到的數(shù)

據(jù)填充到指定的數(shù)據(jù)報(bào)如中。

212

項(xiàng)目八網(wǎng)絡(luò)編程基礎(chǔ)

在接收到數(shù)據(jù)之前,receive。方法會一直處于阻塞狀態(tài),直到接收到數(shù)據(jù)報(bào)才會返

回。因此,如果網(wǎng)絡(luò)上沒有數(shù)據(jù)發(fā)送過來,receive。方法也沒有阻塞,肯定是程序中有

問題,大多數(shù)情況下是使用了一個被其他程序占用的端口號。

案例一一簡易聊天程序

本案例用本地主機(jī)的不同端口模擬兩臺主機(jī)互相發(fā)送和接收數(shù)據(jù)報(bào),利用UDP和多

線程技術(shù)制作一個簡易的聊天程序.

(1)在Eclipse中新建一個名為NetDemo的Java項(xiàng)目,然后在項(xiàng)目中添加一個名

為UserA的類,用于模擬主機(jī)A發(fā)送和接收數(shù)據(jù)報(bào),具體代碼如下:

importjava.net.Datagrampacket;

importjava.net.DatagramSocket;

importjava.net.InetAddress;

importjava.net.SocketException;

importjava.util.Scanner;

publicclassUserA{

publicstaticvoidmain(String口args){

Scannersc=newScanner(System,in);/創(chuàng)建掃描器

ThreadreadData;//聲明線程

ReceiveForAreceiver=newReceiveForA();//創(chuàng)建Runnable實(shí)現(xiàn)類對象

DatagramSocketsocketA=null;

try)

readData=newThread(receiver);//創(chuàng)建負(fù)責(zé)接收信息的線程

readData.start();//啟動線程

byte[]buf=newbyte[1];〃定義接收數(shù)據(jù)的字行數(shù)組

Stringstr="127.0.0.1”;〃服務(wù)器端IP地址

intport=9000;//服務(wù)器端的端口號

//將IP地址封裝為InelAddress對象

InetAddressaddr=InetAddress.gelByName(slv);

try(

socketA=newDatagramSocket。;//創(chuàng)建發(fā)送端的數(shù)據(jù)報(bào)套接字

}catch(SockctExccptione){}

〃創(chuàng)建數(shù)據(jù)報(bào),發(fā)送到地址為辿I的主機(jī)的port端口

DatagramPaikctdp=newDatagramPackot(buf,buf.length,addr,port);

System.out.print(“我對B說:”);

while(sc.hasNext()){//如果輸入了內(nèi)容

Stringinfo=sc.nextLineO;/獲取輸入的內(nèi)容

buf=info.getBytcsO;〃將字符串轉(zhuǎn)換為字節(jié)數(shù)組

213

Java開發(fā)綜合實(shí)戰(zhàn)

if(info.length()==0)〃如果沒有輸入內(nèi)容

System.exitiff);〃強(qiáng)制關(guān)閉JVM,退出程序

buf=info.getBytesO;

dp.setData(buf);〃將字節(jié)數(shù)組放入數(shù)據(jù)報(bào)打包

socketA.send(dp);//發(fā)送數(shù)據(jù)報(bào)

Systen.out.print("我繼續(xù)對B說:");

)

}catch(Exceptione){捕獲異常

System,out.println(e);

)

sc.closeO;

)

)

上面的代碼使用hasNext。方法判斷是否輸入了信息。讀者要注意的是,盡管

hasNext()的返回值類型為boolean,但是不會返回false?如果hasNexl()在緩沖區(qū)內(nèi)掃

描到字符就返回iruc,否則會發(fā)生阻塞,等待數(shù)據(jù)輸入。因此,在要輸入多組數(shù)據(jù)的情

況下,可以使用while+hasNextO的形式判斷是否輸入了字符。

(2)在項(xiàng)目中添加一個名為ReceiveForA的類,該類實(shí)現(xiàn)Runnable接口,用于處

理主機(jī)A接收數(shù)據(jù)包并顯示的操作。具體代碼如下:

importjava.net.DatagramPacket;

importjava.net.DatagramSocket;

〃實(shí)現(xiàn)Runnable接口,為A主機(jī)接收數(shù)據(jù)

publicclassReceivoForAimplementsRunnable{

publicvoidrun(){//實(shí)現(xiàn)run()方法

//初始化數(shù)據(jù)報(bào)套接字和數(shù)據(jù)報(bào)對象

DatagramSocketsocket=null;

DatagramPacketdp=null;

byte[]data=newbyte[8192];〃為接收的數(shù)據(jù)預(yù)分配空間

try{

socket=newDatagramSocket(3000);//創(chuàng)建監(jiān)聽端口3000的數(shù)據(jù)報(bào)套接字

dp=newDatagramPacket(data,data,length);//封裝數(shù)據(jù)

)

catch(Exceptionc){}

while(true){

if(socket==null)break;〃沒有接收到數(shù)據(jù),結(jié)束線程

else

try(

socket.receive(dp);//接收數(shù)據(jù)報(bào)

214

項(xiàng)目八網(wǎng)絡(luò)編程基礎(chǔ)

〃將數(shù)據(jù)報(bào)中的字節(jié)數(shù)據(jù)轉(zhuǎn)換為字符串

Stringmess=newString(dp.getDataO,0,dp.getLengthO);

〃按指定格式輸出接收到的信息

System,out.printf(//%40s\n,z,"B對我說:"+mess);

}catch(Exceptione){}

}

)

}

上面的代碼使用System.也力.printf()格式化輸出接收到數(shù)據(jù)信息,第一個參數(shù)表示

字符串的最小寬度為40,輸出后換行。

(3)在項(xiàng)目中添加一個名為UserB的類,用于模擬主機(jī)B發(fā)送和接收數(shù)據(jù)報(bào)。具體

代碼如下:

importjava.net.DatagramPacket;

importjava.net.DatagramSocket;

importjava.net.Inet/\ddress;

importjava.net.SockotKxccption;

importjava.util.Scanner;

publicclassUserB{

publicstaticvoidmain(String口args){

Scannersc=newScanner(System,in);,/倉U建』」描器

ThreadreadData;//聲明線程

ReceiveForBreceiver=newReceiveForB();//創(chuàng)建Runnable實(shí)現(xiàn)類對象

DatagramSocketsocketB=null;

try{readData=newThread(receiver);〃創(chuàng)建負(fù)責(zé)接收信息的線程

readData.start();//啟動線程

byte[]buf=newbyte[l];〃定義接收數(shù)據(jù)的字行數(shù)組

Stringstr="127.0.0.1”;〃服務(wù)器端IP地址

intport=3000;//服務(wù)器端的端口號

〃將IP地址封裝為InclAddress對象

InetAddrcssaddr=InctAddrcss.gctByName(slr);

try(

socketB=newDatagramSocket();〃創(chuàng)影連接的套接字對象

}catch(SocketExceptione){}

〃創(chuàng)建數(shù)據(jù)報(bào),發(fā)送到地址為辿£的主機(jī)的port端口

DatagramPacketdp=newDatagramPacket(buf,buf.length,addr,port);

System.out.print(“我對A說:");

while(sc.hasNext()){//如果輸入/內(nèi)容

Stringinfo=sc.nextLineO;/.獲取輸入的內(nèi)容

215

Java開發(fā)綜合實(shí)戰(zhàn)

buf=info.gctBytosO;〃將字符串轉(zhuǎn)換為字節(jié)數(shù)組

if(info,length()==0)

System.;3雖制關(guān)閉JVM,退出程序

buf=info.getBytesO;

dp.setData(buf);了將字節(jié)數(shù)組放入數(shù)據(jù)報(bào)打包

socketB.send(dp);.7發(fā)送數(shù)據(jù)報(bào)

Systen.out.print("我繼續(xù)對A說:”);

}

}catch(Exceptione){〃捕獲異常

System.outprintln(e);

}

sc.closeO;

)

}

(4)在項(xiàng)目中添加一個名為ReceiveForB的類,該類實(shí)現(xiàn)Runnable接口,用于處

理主機(jī)B接收數(shù)據(jù)包并顯示的操作。具體代碼如下;

importjava.net.DatagramPacket;

importjava.net.DatagramSocket;

〃實(shí)現(xiàn)Runnable接口,為B主機(jī)接收數(shù)據(jù)

publicclassReceiveForBimplementsRunnable{

publicvoidrun(){〃實(shí)現(xiàn)run()方法

//初始化數(shù)據(jù)報(bào)套接字和數(shù)據(jù)報(bào)對象

DatagramSockotsocket-null;

DatagramPacketdp=null;

byte[]data=newbyte[8192];〃為接收的數(shù)據(jù)預(yù)分配空間

try{〃創(chuàng)建監(jiān)聽端口9000的數(shù)據(jù)報(bào)套接字

socket=newDatagramSocket(9000);

dp=newDatagramPacket(data,data,length);封裝數(shù)據(jù)

}catch(Excepticne){}

while(true){

if(socket==null)break:/沒有接收到數(shù)據(jù)包,結(jié)束線程

else

try(

socket,receive(dp);/,接收數(shù)據(jù)報(bào)

〃將數(shù)據(jù)報(bào)中的字節(jié)數(shù)據(jù)轉(zhuǎn)換為字符串

Stringmess=newString(dp.getData(),0,dp.getLcngth0);

〃按指定格式輸出接收到的信息

System.out.printf(/,%40s\n,\'A對我說:"+mcss);

216

項(xiàng)目八網(wǎng)絡(luò)編程基礎(chǔ)

}catch(Exceptione){}

)

)

)

(5)運(yùn)行程序UserA.java,在控制臺中輸入信息,按Enter鍵,發(fā)送消息。此時的

控制臺如圖8—5所示。

日ConsoleX■3C/|邕/|

UserA[JavaApplication]D:\軟件\eclipse-java-2021-12-R-win32-x1

welldone!congratulations!

圖8—5主機(jī)A發(fā)送消息

(6)運(yùn)行程序UserB.java,在控制臺中輸入信息,如圖8—6所示。按Enter鍵,

即可發(fā)送消息。此時自動切換到主機(jī)A的消息界面,顯示接收到的消息,如圖8-7所示。

SConsoleX■X-|七砧屆0ConsoleX■?一|國砧[£

UserB(JavaApplication]D:\5XWeclipse-java-2021-12-R-win32-xUserA(JavaApplication]D:^XI+\eclipse-java-2021-12-R-/^n32-x

衣CA法,Thankyou!|發(fā)CB泛,uelldone!Congratulations!

kdd廠B於Thankyou!

圖8-6主機(jī)B輸入消息圖8—7主機(jī)A接收到消息

(7)輸入消息,如圖8—8所示,按Enter鍵發(fā)送消息。此時自動切換到主機(jī)B的

消息界面,顯示接收到的消息,如圖8—9所示.

SConsoleX■*■|Sc砧>QConsoleX■,—|It&,

UserA(JavaApplication]D:^Xtt^eclipse-java-2021-12-R-win32-xUserBjJavaApplication]D:\K<4\eclipse-java-2021-12-R-win32->

友CB泛,welldone!Congratulations!友HA法,Thankyou!

匯B次;Thankyou!kawtAa,AZ復(fù)次,wonderful!

wonderful!

圖8—8主機(jī)A輸入消息圖8-9主機(jī)B接收到消息

項(xiàng)目總結(jié)

217

Java開發(fā)綜合實(shí)戰(zhàn)

在上一章的項(xiàng)目實(shí)戰(zhàn)中已實(shí)現(xiàn)了一個簡單的單機(jī)版進(jìn)銷存管理系統(tǒng)。本章將分離客

戶端和服務(wù)器端,利用TCPSocket制作一個簡易的網(wǎng)絡(luò)版進(jìn)銷存管理系統(tǒng)。

(1)復(fù)制并粘貼“進(jìn)銷存管理系統(tǒng)V7.0”,在CopyProject對話框中修改項(xiàng)目名稱

為“進(jìn)銷存管理系統(tǒng)V8.0”,然后單擊Copy按鈕關(guān)閉對話框。

首先設(shè)計(jì)客戶端程序.

(2)在項(xiàng)目中添加一個名為net的包,在包中添加一個名為Client的類,用于實(shí)

現(xiàn)客戶端主機(jī),并與服務(wù)器通信。具體代碼如下:

packagenet;

importjava.io.lOExcoption;

importjava.io.Inputstream;

importjava.io.OutputStream;

importjava.net.Socket:

importjava.rmi.UnknownHostException;

publicclassClient{

staticSocketconn\迎接套接字

staticInputStreamin;//輸入流

staticOutputStreamout;〃輸出流

staticbyte[]buf=nenbyte[20000];

218

項(xiàng)目八網(wǎng)絡(luò)編程基礎(chǔ)

〃靜態(tài)代碼塊初始化客戶端套接字和10流,只在類第一次加載時執(zhí)行

static{

try{

〃爐newSoc

溫馨提示

  • 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

提交評論