在傳統(tǒng)的UNIX環(huán)境下可以操作TCP/IP協(xié)議的接口不止Socket一個(gè),Socket所支持的協(xié)議種類(lèi)也不光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)閷哟蔚?,Socket編程比基于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)。
其中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),既可以用來(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ù)。0~1023的端口號(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 客戶(hù)端的Socket
下面是一個(gè)典型的創(chuàng)建客戶(hù)端Socket的過(guò)程。
try{
Socket socket=new Socket("127.0.0.1",4700);
//127.0.0.1是TCP/IP協(xié)議中默認(rèn)的本機(jī)地址
}catch(IOException e){
System.out.println("Error:"+e);
}
以上的程序是Server的典型工作模式,只不過(guò)在這里Server只能接收一個(gè)請(qǐng)求,接受完后Server就退出了。實(shí)際的應(yīng)用中總是讓它不停的循環(huán)接收,一旦有客戶(hù)請(qǐng)求,Server總是會(huì)創(chuàng)建一個(gè)服務(wù)線(xiàn)程來(lái)服務(wù)新來(lái)的客戶(hù),而自己繼續(xù)監(jiān)聽(tīng)。程序中accept()是一個(gè)阻塞函數(shù),所謂阻塞性方法就是說(shuō)該方法被調(diào)用后,將等待客戶(hù)的請(qǐng)求,直到有一個(gè)客戶(hù)啟動(dòng)并請(qǐng)求連接到相同的端口,然后accept()返回一個(gè)對(duì)應(yīng)于客戶(hù)的socket。這時(shí),客戶(hù)方和服務(wù)方都建立了用于通信的socket,接下來(lái)就是由各個(gè)socket分別打開(kāi)各自的輸入/輸出流。
例如:
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中輸入輸出部分掌握如何。
盡管Java有自動(dòng)回收機(jī)制,網(wǎng)絡(luò)資源最終是會(huì)被釋放的。但是為了有效的利用資源,建議讀者按照合理的順序主動(dòng)釋放資源。
Socket socket=null;
try{
socket=server.accept();
//使用accept()阻塞等待客戶(hù)請(qǐng)求,有客戶(hù)
//請(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)輸出上打印從客戶(hù)端讀入的字符串
line=sin.readLine();
//從標(biāo)準(zhǔn)輸入讀入一字符串
while(!line.equals("bye")){
//如果該字符串為 "bye",則停止循環(huán)
os.println(line);
//向客戶(hù)端輸出該字符串
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)容和客戶(hù)機(jī),服務(wù)器的對(duì)應(yīng)關(guān)系。同時(shí)也可以修改該程序,提供更為強(qiáng)大的功能,或更加滿(mǎn)足讀者的意圖。
8.3.9 支持多客戶(hù)的client/server程序設(shè)計(jì)
前面提供的Client/Server程序只能實(shí)現(xiàn)Server和一個(gè)客戶(hù)的對(duì)話(huà)。在實(shí)際應(yīng)用中,往往是在服務(wù)器上運(yùn)行一個(gè)永久的程序,它可以接收來(lái)自其他多個(gè)客戶(hù)端的請(qǐng)求,提供相應(yīng)的服務(wù)。為了實(shí)現(xiàn)在服務(wù)器方給多個(gè)客戶(hù)提供服務(wù)的功能,需要對(duì)上面的程序進(jìn)行改造,利用多線(xiàn)程實(shí)現(xiàn)多客戶(hù)機(jī)制。服務(wù)器總是在指定的端口上監(jiān)聽(tīng)是否有客戶(hù)請(qǐng)求,一旦監(jiān)聽(tīng)到客戶(hù)請(qǐng)求,服務(wù)器就會(huì)啟動(dòng)一個(gè)專(zhuān)門(mén)的服務(wù)線(xiàn)程來(lái)響應(yīng)該客戶(hù)的請(qǐng)求,而服務(wù)器本身在啟動(dòng)完線(xiàn)程之后馬上又進(jìn)入監(jiān)聽(tīng)狀態(tài),等待下一個(gè)客戶(hù)的到來(lái)。
客戶(hù)端的程序和上面程序是完全一樣的,讀者如果仔細(xì)閱讀過(guò)上面的程序,可以跳過(guò)不讀,把主要精力集中在Server端的程序上。
8.3.10數(shù)據(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,F(xiàn)TP,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就好比電話(huà),雙方能肯定對(duì)方接受到了信息。在本章前面,我們已經(jīng)對(duì)UDP和TCP進(jìn)行了比較,在這里再稍作小節(jié):
TCP,可靠,傳輸大小無(wú)限制,但是需要連接建立時(shí)間,差錯(cuò)控制開(kāi)銷(xiāo)大。
UDP,不可靠,差錯(cuò)控制開(kāi)銷(xiāo)較小,傳輸大小限制在64K以下,不需要建立連接。
總之,這兩種協(xié)議各有特點(diǎn),應(yīng)用的場(chǎng)合也不同,是完全互補(bǔ)的兩個(gè)協(xié)議,在TCP/IP協(xié)議中占有同樣重要的地位,要學(xué)好網(wǎng)絡(luò)編程,兩者缺一不可。
8.3.12 Datagram通訊的表示方法:DatagramSocket;DatagramPacket
包java.net中提供了兩個(gè)類(lèi)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類(lèi)例外。注意:上述的兩個(gè)構(gòu)造方法都聲明拋棄非運(yùn)行時(shí)例外SocketException,程序中必須進(jìn)行處理,或者捕獲、或者聲明拋棄。
用數(shù)據(jù)報(bào)方式編寫(xiě)client/server程序時(shí),無(wú)論在客戶(hù)方還是服務(wù)方,首先都要建立一個(gè)DatagramSocket對(duì)象,用來(lái)接收或發(fā)送數(shù)據(jù)報(bào),然后使用DatagramPacket類(lèi)對(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類(lèi)參數(shù)。類(lèi)InetAddress在包java.net中定義,用來(lái)表示一個(gè)Internet地址,我們可以通過(guò)它提供的類(lèi)方法getByName()從一個(gè)表示主機(jī)名的字符串獲取該主機(jī)的IP地址,然后再獲取相應(yīng)的地址信息。
DatagramSocket socket=new DatagramSocklet();
//創(chuàng)建數(shù)據(jù)報(bào)套接字
Byte[] buf=new byte[256]; //創(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)閉套接口
}
}
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)用父類(lèi)的構(gòu)造函數(shù)
socket=new DatagramSocket(4445);
//在端口4445創(chuàng)建數(shù)據(jù)報(bào)套接字
try{
in= new BufferedReader(new FileReader(" one-liners.txt"));
//打開(kāi)一個(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() //線(xiàn)程主體
{
while(moreQuotes) {
try{
byte[] buf=new byte[256]; //創(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í)候打開(kāi)文件失敗了,
//則使用日期作為要傳送的字符串
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ù)客戶(hù)端信息構(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)聽(tīng)功能的,也就是說(shuō)通信雙方更為平等,面對(duì)的接口是完全一樣的。但是為了用UDP實(shí)現(xiàn)C/S結(jié)構(gòu),在使用UDP時(shí)可以使用DatagramSocket.receive()來(lái)實(shí)現(xiàn)類(lèi)似于監(jiān)聽(tīng)的功能。因?yàn)閞eceive()是阻塞的函數(shù),當(dāng)它返回時(shí),緩沖區(qū)里已經(jīng)填滿(mǎn)了接受到的一個(gè)數(shù)據(jù)報(bào),并且可以從該數(shù)據(jù)報(bào)得到發(fā)送方的各種信息,這一點(diǎn)跟accept()是很相象的,因而可以根據(jù)讀入的數(shù)據(jù)報(bào)來(lái)決定下一步的動(dòng)作,這就達(dá)到了跟網(wǎng)絡(luò)監(jiān)聽(tīng)相似的效果。
8.3.14 用數(shù)據(jù)報(bào)進(jìn)行廣播通訊
DatagramSocket只允許數(shù)據(jù)報(bào)發(fā)送一個(gè)目的地址,java.net包中提供了一個(gè)類(lèi)MulticastSocket,允許數(shù)據(jù)報(bào)以廣播方式發(fā)送到該端口的所有客戶(hù)。MulticastSocket用在客戶(hù)端,監(jiān)聽(tīng)服務(wù)器廣播來(lái)的數(shù)據(jù)。
我們對(duì)上面的程序作一些修改,利用MulticastSocket實(shí)現(xiàn)廣播通信。新程序完成的功能是使同時(shí)運(yùn)行的多個(gè)客戶(hù)程序能夠接收到服務(wù)器發(fā)送來(lái)的相同的信息,顯示在各自的屏幕上。
for(int i=0;i<5;i++) {
byte[] buf=new byte[256];
//創(chuàng)建緩沖區(qū)
packet=new DatagramPacket(buf,buf.length);
//創(chuàng)建接收數(shù)據(jù)報(bào)
socket.receive(packet); //接收
String received=new String(packet.getData());
//由接收到的數(shù)據(jù)報(bào)得到字節(jié)數(shù)組,
//并由此構(gòu)造一個(gè)String對(duì)象
System.out.println("Quote of theMoment:"+received);
//打印得到的字符串
} //循環(huán)5次
socket.leaveGroup(address);
//把廣播套接字從地址上解除綁定
socket.close(); //關(guān)閉廣播套接字
}
}
public void run() //重寫(xiě)父類(lèi)的線(xiàn)程主體
{
while(moreQuotes) {
//根據(jù)標(biāo)志變量判斷是否繼續(xù)循環(huán)
try{
byte[] buf=new byte[256];
//創(chuàng)建緩沖區(qū)
String dString=null;
if(in==null) dString=new Date().toString();
//如果初始化的時(shí)候打開(kāi)文件失敗了,
//則使用日期作為要傳送的字符串
else dString=getNextQuote();
//否則調(diào)用成員函數(shù)從文件中讀出字符串
buf=dString.getByte();
//把String轉(zhuǎn)換成字節(jié)數(shù)組,以便傳送send it
InetAddress group=InetAddress.getByName("230.0.0.1");
//得到230.0.0.1的地址信息
DatagramPacket packet=new DatagramPacket(buf,buf.length,group,4446);
//根據(jù)緩沖區(qū),廣播地址,和端口號(hào)創(chuàng)建DatagramPacket對(duì)象
socket.send(packet); //發(fā)送該P(yáng)acket
try{
sleep((long)(Math.random()*FIVE_SECONDS));
//隨機(jī)等待一段時(shí)間,0~5秒之間
}catch(InterruptedException e) { } //異常處理
}catch(IOException e){ //異常處理
e.printStackTrace( ); //打印錯(cuò)誤棧
moreQuotes=false; //置結(jié)束循環(huán)標(biāo)志
}
}
socket.close( ); //關(guān)閉廣播套接口
}
}
至此,Java網(wǎng)絡(luò)編程這一章已經(jīng)講解完畢。讀者通過(guò)學(xué)習(xí),應(yīng)該對(duì)網(wǎng)絡(luò)編程有了一個(gè)清晰的認(rèn)識(shí),可能對(duì)某些概念還不是十分的清楚,還是需要更多的實(shí)踐來(lái)進(jìn)一步掌握。編程語(yǔ)言的學(xué)習(xí)不同于一般的學(xué)習(xí),及其強(qiáng)調(diào)實(shí)踐的重要性。讀者應(yīng)該對(duì)URL網(wǎng)絡(luò)編程,Socket中的TCP,UDP編程進(jìn)行大量的練習(xí)才能更好的掌握本章中所提到的一些概念,才能真正學(xué)到Java網(wǎng)絡(luò)編程的精髓!
最后幾個(gè)小節(jié)所舉的例子,讀者務(wù)必要親自試驗(yàn)一下,如果遇到問(wèn)題,想辦法解決之。最好能根據(jù)自己的意圖加以改進(jìn)。這樣才能更好的理解這幾個(gè)程序,理解其中所包含的編程思想。
后續(xù)的內(nèi)容分為兩大塊,一塊是以URL為主線(xiàn),講解如何通過(guò)URL類(lèi)和URLConnection類(lèi)訪問(wèn)WWW網(wǎng)絡(luò)資源,由于使用URL十分方便直觀,盡管功能不是很強(qiáng),還是值得推薦的一種網(wǎng)絡(luò)編程方法,尤其是對(duì)于初學(xué)者特別容易接受。本質(zhì)上講,URL網(wǎng)絡(luò)編程在傳輸層使用的還是TCP協(xié)議。
另一塊是以Socket接口和C/S網(wǎng)絡(luò)編程模型為主線(xiàn),依次講解了如何用Java實(shí)現(xiàn)基于TCP的C/S結(jié)構(gòu),主要用到的類(lèi)有Socket,ServerSocket。以及如何用Java實(shí)現(xiàn)基于UDP的C/S結(jié)構(gòu),還討論了一種特殊的傳輸方式,廣播方式,這種方式是UDP所特有的,主要用到的類(lèi)有DatagramSocket , DatagramPacket, MulticastSocket。這一塊在Java網(wǎng)絡(luò)編程中相對(duì)而言是最難的(盡管Java在網(wǎng)絡(luò)編程這方面已經(jīng)做的夠"傻瓜"了,但是網(wǎng)絡(luò)編程在其他環(huán)境下的卻是一件極為頭痛的事情,再"傻瓜"還是有一定的難度),也是功能最為強(qiáng)大的一部分,讀者應(yīng)該好好研究,領(lǐng)悟其中的思想。
最后要強(qiáng)調(diào)的是要學(xué)好Java網(wǎng)絡(luò)編程,Java語(yǔ)言,最重要的還是在于多多練習(xí)!
聯(lián)系客服