Java高級編程-網絡編程祥解 (3)
MI LY: courier new"> Datagram(數據報)是一種盡力而為的傳送數據的方式,它只是把數據的目的地記錄在數據包中,然后就直接放在 網絡 上,系統不保證數據是否能 安全 送到,或者什么時候可以送到,也就是說它并不保證傳送 質量 。 1 UDP套接字 數據報(Dat
MILY: courier new">Datagram(數據報)是一種盡力而為的傳送數據的方式,它只是把數據的目的地記錄在數據包中,然后就直接放在網絡上,系統不保證數據是否能安全送到,或者什么時候可以送到,也就是說它并不保證傳送質量。
1 UDP套接字
數據報(Datagram)是網絡層數據單元在介質上傳輸信息的一種邏輯分組格式,它是一種在網絡中傳播的、獨立的、自身包含地址信息的消息,它能否到達目的地、到達的時間、到達時內容是否會變化不能準確地知道。它的通信雙方是不需要建立連接的,對于一些不需要很高質量的應用程序來說,數據報通信是一個非常好的選擇。還有就是對實時性要求很高的情況,比如在實時音頻和視頻應用中,數據包的丟失和位置錯亂是靜態的,是可以被人們所忍受的,但是如果在數據包位置錯亂或丟失時要求數據包重傳,就是用戶所不能忍受的,這時就可以利用UDP協議傳輸數據包。在Java的java.net包中有兩個類DatagramSocket和DatagramPacket,為應用程序中采用數據報通信方式進行網絡通信。
使用數據包方式首先將數據打包,Java.net包中的DategramPacket類用來創建數據包。數據包有兩種,一種用來傳遞數據包,該數據包有要傳遞到的目的地址;另一種數據包用來接收傳遞過來的數據包中的數據。要創建接收的數據包,通過DatagramPackett 類的方法構造:
public DatagramPacket(byte ibuft[],int ilength)
public DatagramPacket( byte ibuft[],int offset ,int ilength)
ibuf[]為接受數據包的存儲數據的緩沖區的長度,ilength為從傳遞過來的數據包中讀取的字節數。當采用第一種構造方法時,接收到的數據從ibuft[0]開始存放,直到整個數據包接收完畢或者將ilength的字節寫入ibuft為止。采用第二種構造方法時,接收到的數據從ibuft[offset]開始存放。如果數據包長度超出了ilength,則觸發IllegalArgument-Exception。不過這是 RuntimeException,不需要用戶代碼捕獲。示范代碼如下:
byte[ ] buffer=new byte[8912];
DatagramPacket datap=new DatagramPacket(buffer ,buffer.length( ));
創建發送數據包的構造方法為:
public DatagramPacket(byt ibuf[],int ilength,InetAddrss iaddr,int port)
public DatagramPacket(byt ibuf[],int offset , int ilength,InetAddrss iaddr,int port)
iaddr為數據包要傳遞到的目標地址,port為目標地址的程序接受數據包的端口號(即目標地址的計算機上運行的客戶程序是在哪一個端口接收服務器發送過來的數據包)。ibuf[]為要發送數據的存儲區,以ibuf數組的offset位置開始填充數據包ilength字節,如果沒有offset,則從ibuf數組的0位置開始填充。以下示范代碼是要發送一串字符串:
String s = new String("java networking");
byte[ ] data=s.getbytes();
int port=1024;
try{
InetAddress ineta= InetAddress.getByName(" 169.254.0.14");
DatagramPacket datap=new DatagramPacket
(data ,data.length( ),ineta,port);
}
catch(IOException e) {
}
|
數據包也是對象,也有操作方法用來獲取數據包的信息,這是很有用的。其方法如下:
public InetAddress getAddress() 如果是發送數據包,則獲得數據包要發送的目標地址,但是如果是接收數據包則返回發送此數據包的源地址。
public byte[]getData()
返回一個字節數組,其中是數據包的數據。如果想把字節數組轉換成別的類型就要進行轉化。如果想轉化成String類型,可以進行以下的處理,設DatagramPacket datap為:
String s = new String(datap.getbytes());
public int getLength() 獲得數據包中數據的字節數。
pubic int getPort( ) 返回數據包中的目標地址的主機端口號。
發送和接收數據包還需要發送和接收數據包的套接字,即DatagramSocket對象,DatagramSocket套接字在本地機器端口監聽是否有數據到達或者將數據包發送出去。其構造方法如下。
public DatagramSocket() 用本地機上任何一個可用的端口創建一個套接字,這個端口號是由系統隨機產生的。使用方法如下:
try{
DatagramSocket datas=new DatagramSocket( );
//發送數據包
}
catch(SocketException e){
}
|
這種構造方法沒有指定端口號,可以用在客戶端。如果構造不成功則觸發SocketException異常。
public DatagramSocket(int port)
用一個指定的端口號port創建一個套接字。
當不能創建套接字時就拋出SocketException異常,其原因是指定的端口已被占用或者是試圖連接低于1024的端口,但是又沒有具備權限。
2 實例:利用DatagramSocket查詢端口占用情況
我們可以利用這個異常探查本地機的端口號有沒有服務。見示例12-9。
【程序源代碼】
1 // ==================== Program Description =====================
2 // 程序名稱:示例12-9: UDPScan.java
3 // 程序目的:熟悉DatagramSocket的基本用法,查詢端口的占用情況
4 //=========================================================
5 import java.net.*;
6
7 public class UDPScan
8 {
9 public static void main(String args[])
10 {
11 for (int port=1024;port<=65535;port++) {
12 try {
13 DatagramSocket server=new DatagramSocket(port);
14 server.close();
15 }
16 catch(SocketException e) {
17 System.out.println("there is a server in port "+port+".");
18 }
19 }
20 }
21 }
|
【程序輸出結果】
there is a server in port 1026.
there is a server in port 1028.
there is a server in port 1046.
there is a server in port 1900.
|
【程序注解】
在第11~19行我們用for循環以端口號為參數實例化DatagramSocket,其中端口號從1024到65535。如果在實例過程中出錯,會拋出SocketException異常。我們根據這個異常就可以判斷出哪些端口被占用,哪些還是空閑的。值得一提的是,我們在實例化了DatagramSocket后,調用了close()關閉它。作為一種好的作風,應該遵循。端口號在1024以下的系統可能會用到,比如HTTP默認為80端口,FTP默認為21端口,等等,所以我們從1024端口開始探查。
套接字對象也有相應的方法,例如發送數據包的方法還有接收數據包的方法,介紹如下。
pubic void close() 當我們創建一個套接字后,用該方法關閉套接字。
public int getLocalPort() 返回本地套接字的正在監聽的端口號。
public void receive(DatagramPacket p) 從網絡上接收數據包并將其存儲在DatagramPacket對象p中。p中的數據緩沖區必須足夠大,receive()把盡可能多的數據存放在p中,如果裝不下,就把其余的部分丟棄。接收數據出錯時會拋出IOException異常。
public Void Send(DatagramPacket p) 發送數據包,出錯時會發生IOException異常。
下面,我們詳細解釋在Java中實現客戶端與服務器之間數據報通信的方法。
應用程序的工作流程如下:
(1)首先要建立數據報通信的Socket,我們可以通過創建一個DatagramSocket對象實現它,在Java中DatagramSocket類有如下兩種構造方法:
public DatagramSocket() 構造一個數據報socket,并使其與本地主機任一可用的端口連接。若打不開socket則拋出SocketException異常。
public DatagramSocket(int port) 構造一個數據報socket,并使其與本地主機指定的端口連接。若打不開socket或socket無法與指定的端口連接則拋出SocketException異常。
(2)創建一個數據報文包,用來實現無連接的包傳送服務。每個數據報文包用DatagramPacket類創建, DatagramPacket對象封裝了數據報包數據、包長度、目標地址和目標端口??蛻舳艘l送數據報文包,要調用DatagramPacket類以如下形式的構造函數創建DatagramPacket對象,將要發送的數據和包文目的地址信息放入對象之中。DatagramPacket(byte bufferedarray[],int length,InetAddress address,int port)即構造一個包長度為length的包傳送到指定主機指定端口號上的數據報文包,參數length必須小于等于 bufferedarry.length。
DatagramPacket類提供了4個類獲取信息:
public byte[] getData() 返回一個字節數組,包含收到或要發送的數據報中的數據。
public int getLength() 返回發送或接收到的數據的長度。
public InetAddress getAddress() 返回一個發送或接收此數據報包文的機器的IP地址。
public int getPort() 返回發送或接收數據報的遠程主機的端口號。
(3)創建完DatagramSocket和DatagramPacket對象,就可以發送數據報文包了。發送是通過調用 DatagramSocket對象的send方法實現,它需要以DatagramPacket對象為參數,將剛才封裝進DatagramPacket對象中的數據組成數據報發出。
(4)當然,我們也可以接收數據報文包。為了接收從服務器返回的結果數據報文包,我們需要創建一個新的 DatagramPacket對象,這就需要用到DatagramPacket的另一種構造方式DatagramPacket(byte bufferedarray[],int length),即只需指明存放接收的數據報的緩沖區和長度。調用DatagramSocket對象的receive()方法完成接收數據報的工作,此時需要將上面創建的DatagramPacket對象作為參數,該方法會一直阻塞直到收到一個數據報文包,此時DatagramPacket的緩沖區中包含的就是接收到的數據,數據報文包中也包含發送者的IP地址,發送者機器上的端口號等信息。
(5)處理接收緩沖區內的數據,獲取服務結果。
(6)當通信完成后,可以使用DatagramSocket對象的close()方法關閉數據報通信Socket。當然, Java會自動關閉Socket,釋放DatagramSocket和DatagramPacket所占用的資源。但是作為一種良好的編程習慣,還是要顯式地予以關閉。
3 實例:利用數據報通信的C/S程序
示例12-10給出了一個簡單的利用數據報通信的客戶端程序,它能夠完成與服務器簡單的通信。
【程序源代碼】
1 // ==================== Program Description ===================
2 // 程序名稱:示例12-10: UDPServer.java
3 // 程序目的:創建UDP服務器
4 //=============================================================
5 import java.net.*;
6 import java.io.*;
7
8 public class UDPServer
9 {
10 static public void main(String args[])
11 {
12 try {
13 DatagramSocket receiveSocket = new DatagramSocket(5000);
14 byte buf[]=new byte[1000];
15 DatagramPacket receivePacket=new DatagramPacket(buf,buf.length);
16 System.out.println("startinig to receive packet");
17 while (true)
18 {
19 receiveSocket.receive(receivePacket);
20 String name=receivePacket.getAddress().toString();
21 System.out.println("
來自主機:"+name+"
端口:"
22 +receivePacket.getPort());
23 String s=new
String(receivePacket.getData(),0,receivePacket.getLength());
24 System.out.println("the received data: "+s);
25 }
26 }
27 catch (SocketException e) {
28 e.printStackTrace();
29 System.exit(1);
30 }
31 catch(IOException e) {
32 System.out.println("網絡通信出現錯誤,問題在"+e.toString());
33 }
34 }
35 }
|
【程序輸出結果】
startinig to receive packet
來自主機:/166.111.172.20
端口:3456
the received data: hello! this is the client
|
【程序注解】
第13行和第15行分別實例化了一個DatagramSocket對象receiveSocket 和一個DatagramPacket對象receivePacket,都是通過調用各自的構造函數實現的,為建立服務器做好準備。在while這個永久循環中,receiveSocket這個套接字始終嘗試receive()方法接收DatagramPacket數據包,當接收到數據包后,就調用 DatagramPacket的一些成員方法顯示一些數據包的信息。在程序中調用了getAddress()獲得地址,getPort()方法獲得客戶端套接字的端口,getData()獲得客戶端傳輸的數據。注意getData( )返回的是字節數組,我們把它轉化為字符串顯示。在第27~33行我們對程序中發生的SocketException和IOException異常進行了處理。
示例12-11是UDP客戶端的程序。
【程序源代碼】
1 // ==================== Program Description ===================
2 // 程序名稱:示例12-11: UDPClient.java
3 // 程序目的:創建UDP客戶端
4 //=============================================================
5 import java.net.*;
6 import java.io.*;
7
8 public class UDPClient
9 {
10 public static void main(String args[])
11 {
12 try {
13 DatagramSocket sendSocket=new DatagramSocket(3456);
14 String string="asfdfdfggf";
15 byte[] databyte=new byte[100];
16 databyte=string.getBytes();
17 DatagramPacketsendPacket=new
DatagramPacket(databyte,string.length(),
18 InetAddress.getByName("163.121.139.20"),
5000);
19 sendSocket.send(sendPacket);
20 System.out.println("send the data: hello ! this is the client");
21 }
22 catch (SocketException e) {
23 System.out.println("不能打開數據報Socket,或數據報Socket無法與指定
24 端口連接!");
25 }
26 catch(IOException ioe) {
27 System.out.println("網絡通信出現錯誤,問題在"+ioe.toString());
28 }
29 }
30 }
|
【程序輸出結果】
send the data: hello !this is the clientsend the data: hello !this is the client
【程序注解】
第13行用DatagramSocket的構造函數實例化一個發送數據的套接字 sendSocket。第17~18行實例化了一個DatagramPacket,其中數據包要發往的目的地是163.121.139.20,端口是 5000。當構造完數據包后,就調用send( )方法將數據包發送出去。
4 組播套接字
在Java中,可以用java.net.MulticastSocket類組播數據。組播套接字是DatagramSocket的子類,定義如下:
public class MulticastSocket extends DatagramSocket
構造方法有兩個:
public MulticastSocket ( ) throws SocketException
public MulticastSocket (int port ) throws SocketException
以上兩個方法都是創建組播套接字,第一個方法沒有端口號,第二個指定了端口號。
常用的方法如下:
public void joinGroup(InetAddress address) throws IOException
建立了MulticastSocket對象后,為了發送或者接收組播包,必須用joinGroup方法加入一個組播組。若加入的不是組播地址將觸發IOException異常。
public void leaveGroup(InetAddress address)throws IOException
如果不想接收組播包了,就調用leaveGroup方法。程序就發信息到組播路由器,通知它向此用戶發送數據。若想離開的地址不是組播地址就觸發IOException異常。
public void send(DatagramPacket packet, byte, ttl) throws IOExceptin
發送組播包的方法與DatagramSocket發送數據相似。其中ttl是生存時間,大小在0~255之間。
public void receive(DatagramPacket p) 與DatagramSocket的接收方法沒有差別。
public void setTimeToLive(int ttl )throws IOException 設置套接字發出的組播包中的默認ttl數值。
public int getTimeToLive( ) throws IOException 返回ttl數值。
使用組播套接字發送數據的過程是首先用MulticastSocket()構造器創建MulticastSocket類,然后利用MulticastSocket類的joinGroup()方法加入一個組播組,之后創建DatagramPacket數據包,最后調用 MulticastSocket類的send()方法發送組播包。
發送組播包的代碼如下:
try {
InetAddress address = InetAddress.getByName (www.mmm.net) ;
byte[ ] data=" java networking";
int port =5000;
DatagramPacket datap =new DatagramSocket
(data ,data.length( ),address,port);
MulticastSocket muls =new MulticastSocket ( );
muls.send(datap );
}
catch(IOException ie) {
}
|
使用組播套接字接收數據的過程是首先用MulticastSocket()構造器創建MulticastSocket類,然后利用MulticastSocket類的joinGroup()方法加入一個組播組,之后用receive()方法接收組播包。我們發現其過程與UDP包的過程很相似,區別是要加入一個組播組。
5 實例:組播套接字C/S程序
下面的程序示例12-12說明了組播套接字的基本用法。
【程序源代碼】
1 // ==================== Program Description =====================
2 // 程序名稱:示例12-12: MulticastServer.java
3 // 程序目的:創建一個組播服務器
4 //==========================================================
5 import java.io.*;
6 import java.net.*;
7 import java.util.*;
8
9 class QuoteServerThread extends Thread
10 {
11 protected DatagramSocket socket = null;
12 protected BufferedReader in = null;
13 protected boolean moreQuotes = true;
14
15 public QuoteServerThread() throws IOException {
16 this("QuoteServerThread");
17 }
18
19 public QuoteServerThread(String name) throws IOException {
20 super(name);
21 socket = new DatagramSocket(4445);
22
23 try {
24 in = new BufferedReader(new FileReader("one-liners.txt"));
25 } catch (FileNotFoundException e) {
26 System.err.println("Could not open quote file.
Serving time instead.");
27 }
28 }
29
30 public void run() {
31 while (moreQuotes) {
32 try {
33 byte[] buf = new byte[256];
34
35 // 獲取請求
36 DatagramPacket packet = new DatagramPacket(buf, buf.length);
37 socket.receive(packet);
38
39 // 進行響應
40 String dString = null;
41 if (in == null)
42 dString = new Date().toString();
43 else
44 dString = getNextQuote();
45 buf = dString.getBytes();
46
47 // 向用戶發送響應
48 InetAddress address = packet.getAddress();
49 int port = packet.getPort();
50 packet = new DatagramPacket(buf, buf.length, address, port);
51 socket.send(packet);
52 }
53 catch (IOException e) {
54 e.printStackTrace();
55 moreQuotes = false;
56 }
57 }
58 socket.close();
59 }
60
61 protected String getNextQuote() {
62 String returnValue = null;
63 try {
64 if ((returnValue = in.readLine()) == null) {
65 in.close();
66 moreQuotes = false;
67 returnValue = "No more quotes. Goodbye.";
68 }
69 } catch (IOException e) {
70 returnValue = "IOException occurred in server.";
71 }
72 return returnValue;
73 }
74 }
75
76 class MulticastServerThread extends QuoteServerThread
77 {
78 private long FIVE_SECONDS = 5000;
79
80 public MulticastServerThread() throws IOException {
81 super("MulticastServerThread");
82 }
83
84 public void run() {
85 while (moreQuotes) {
86 try {
87 byte[] buf = new byte[256];
88
89 // 構造引用
90 String dString = null;
91 if (in == null)
92 dString = new Date().toString();
93 else
94 dString = getNextQuote();
95 buf = dString.getBytes();
96
97 // 發送
98 InetAddress group = InetAddress.getByName("136.122.133.1");
99 DatagramPacket packet =new
DatagramPacket(buf,buf.length,group,
100 4446);
101 socket.send(packet);
102
103 // 休眠
104 try {
105 sleep((long)(Math.random() * FIVE_SECONDS));
106 }
107 catch (InterruptedException e) { }
108 }
109 catch (IOException e) {
110 e.printStackTrace();
111 moreQuotes = false;
112 }
113 }
114 socket.close();
115 }
116 }
117
118 public class MulticastServer {
119 public static void main(String[] args) throws java.io.IOException {
120 new MulticastServerThread().start();
121 }
122 }
|
【程序注解】
服務器程序由3個類組成:QuoteServerThread, MulticastServerThread和MulticastServer。它們的關系是:QuoteServerThread繼承自線程類,而 MulticastServerThread類繼承自類QuoteServerThread。這個程序主要的部分在QuoteServerThread和 MulticastServerThread。QuoteServerThread類有兩個構造函數,其中在構造函數QuoteServerThread (String name)中,初始化了DatagramSocket套接字并打開了文件one-liners.txt,在這個文件中存有服務器發送的字符串。
在QuoteServerThread類的run()函數中,服務器端套接字接收來自客戶端的數據包,并從文件中讀取數據,把信息發給客戶端。
MulticastServerThread類中重載了run( )方法,實現的功能基本相同,在發完服務器的信息后,用sleep( )函數停止處理了一個隨機的時間。
在MultiServer類中,用 new MulticastServerThread().start()開始服務器線程。我們現在只是關注其基本思想。
示例12-13是UDP組播的客戶端程序。
【程序源代碼】
1 // ==================== Program Description =====================
2 // 程序名稱:示例12-13: MulticastClient.java
3 // 程序目的:UDP組播客戶端
4 //=========================================================
5 import java.io.*;
6 import java.net.*;
7 import java.util.*;
8
9 public class MulticastClient
10 {
11 public static void main(String[] args) throws IOException
12 {
13 MulticastSocket socket = new MulticastSocket(4446);
14 InetAddress address = InetAddress.getByName("136.122.133.1");
15 socket.joinGroup(address);
16 DatagramPacket packet;
17
18 for (int i = 0; i < 5; i++)
19 {
20 byte[] buf = new byte[256];
21 packet = new DatagramPacket(buf, buf.length);
22 socket.receive(packet);
23 String received = new String(packet.getData());
24 System.out.println("Quote of the Moment: " + received);
25 }
26
27 socket.leaveGroup(address);
28 socket.close();
29 }
30 }
|
【程序輸出結果】
Quote of the Moment: Give me ambiguity or give me something else.
Quote of the Moment: I.R.S.: We′ve got what it takes to take what you′ve got!
Quote of the Moment: We are born naked, wet and hungry. Then things get worse.
Quote of the Moment: Make it idiot proof and someone will make a better idiot.
Quote of the Moment: He who laughs last thinks slowest!
|
【程序注解】
在客戶端的main()方法中,第13行實例化了一個MulticastSocket對象 socket,然后用join()方法加入了組播組136.122.133.1。在for循環中接收了5個數據包,并把數據包中的內容顯示出來(第 18~25行)。最后在第27行離開組播組(leaveGroup()),第28行關閉套接字。
原文轉自:http://www.anti-gravitydesign.com
- 評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
-
国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97
|