JAVA圖文說(shuō)明教程 第8講 Java網(wǎng)絡(luò)編程(3)
發(fā)表時(shí)間:2024-05-27 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]8.3.10 據(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é)...
8.3.10 據(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就好比電話,雙方能肯定對(duì)方接受到了信息。在本章前面,我們已經(jīng)對(duì)UDP和TCP進(jìn)行了比較,在這里再稍作小節(jié):
TCP,可靠,傳輸大小無(wú)限制,但是需要連接建立時(shí)間,差錯(cuò)控制開(kāi)銷大。
UDP,不可靠,差錯(cuò)控制開(kāi)銷較小,傳輸大小限制在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è)類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類例外。注意:上述的兩個(gè)構(gòu)造方法都聲明拋棄非運(yùn)行時(shí)例外SocketException,程序中必須進(jìn)行處理,或者捕獲、或者聲明拋棄。
用數(shù)據(jù)報(bào)方式編寫client/server程序時(shí),無(wú)論在客戶方還是服務(wù)方,首先都要建立一個(gè)DatagramSocket對(duì)象,用來(lái)接收或發(fā)送數(shù)據(jù)報(bào),然后使用DatagramPacket類對(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類參數(shù)。類InetAddress在包java.net中定義,用來(lái)表示一個(gè)Internet地址,我們可以通過(guò)它提供的類方法getByName()從一個(gè)表示主機(jī)名的字符串獲取該主機(jī)的IP地址,然后再獲取相應(yīng)的地址信息。
8.3.13 基于UDP的簡(jiǎn)單的Client/Server程序設(shè)計(jì)
有了上面的知識(shí),我們就可以來(lái)構(gòu)件一個(gè)基于UDP的C/S 網(wǎng)絡(luò)傳輸模型
1. 客戶方程序 QuoteClient.java
import java.io.*;
import java.net.*;
import java.util.*;
public class QuoteClient {
public static void main(String[] args) throws IOException
{
if(args.length!=1) {
//如果啟動(dòng)的時(shí)候沒(méi)有給出Server的名字,那么出錯(cuò)退出
System.out.println("Usage:java QuoteClient
");
//打印出錯(cuò)信息
return; //返回
}
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)閉套接口
}
}
2. 服務(wù)器方程序:QuoteServer.java
public class QuoteServer{
public static void main(String args[]) throws java.io.IOException
{
new QuoteServerThread().start();
//啟動(dòng)一個(gè)QuoteServerThread線程
}
}
3. 程序QuoteServerThread.java
import java.io.*;
import java.net.*;
import java.util.*;
//服務(wù)器線程
public class QuoteServerThread extends Thread
{
protected DatagramSocket socket=null;
//記錄和本對(duì)象相關(guān)聯(lián)的DatagramSocket對(duì)象
protected BufferedReader in=null;
//用來(lái)讀文件的一個(gè)Reader
protected boolean moreQuotes=true;
//標(biāo)志變量,是否繼續(xù)操作
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)用父類的構(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() //線程主體
{
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ù)客戶端信息構(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; //返回字符串
}
}