版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 鴨貨供貨合同范本
- 未來五年太陽能源原動機(jī)維修行業(yè)直播電商戰(zhàn)略分析研究報(bào)告
- 明星代言合作合同范本
- 文字編輯崗位的面試題目解析
- 未來五年工業(yè)物聯(lián)網(wǎng)平臺軟件企業(yè)縣域市場拓展與下沉戰(zhàn)略分析研究報(bào)告
- 未來五年深海油氣田勘采成套裝置智能控制系統(tǒng)企業(yè)縣域市場拓展與下沉戰(zhàn)略分析研究報(bào)告
- 未來五年干粉飼料行業(yè)跨境出海戰(zhàn)略分析研究報(bào)告
- 水果批發(fā)訂購合同范本
- 房地產(chǎn)公司財(cái)務(wù)分析崗位面試題及答案
- 環(huán)網(wǎng)柜維保合同范本
- 2025年10月自考04184線性代數(shù)經(jīng)管類試題及答案含評分參考
- 路燈養(yǎng)護(hù)投標(biāo)方案(技術(shù)標(biāo))
- 幼兒園防火安全檢查記錄表
- 南方科技大學(xué)校聘能力測評英語測評
- 2023高效制冷機(jī)房系統(tǒng)應(yīng)用技術(shù)規(guī)程
- 第十一章靈巧彈藥
- 電力工程公司積成績效考核管理體系制度規(guī)定
- 銀行IT服務(wù)管理事件管理流程概要設(shè)計(jì)
- 地圖文化第三講古代測繪課件
- LY/T 2230-2013人造板防霉性能評價(jià)
- GB/T 34891-2017滾動軸承高碳鉻軸承鋼零件熱處理技術(shù)條件
評論
0/150
提交評論