Google Code Prettify

2014年5月31日 星期六

java.io: UDP 網路程式設計 (multicast)

繼續看 Oracle 的範例程式,在裡面舉出 UDP 最重要的運用 -- 群播,之前兩個範例程式測試的方式都是先執行 server,再執行 client,這個群播程式要反過來,先執行 client 以等待 server 廣播出來的訊息。
1 package idv.steven;
2 
3 import java.io.IOException;
4 
5 public class MulticastServer {
6     public static void main(String[] args) throws IOException {
7         new MulticastServerThread().start();
8     }
9 }
 1 package idv.steven;
 2 
 3 import java.io.*;
 4 import java.net.*;
 5 import java.util.*;
 6  
 7 public class MulticastServerThread extends QuoteServerThread {
 8  
 9     private long FIVE_SECONDS = 5000;
10  
11     public MulticastServerThread() throws IOException {
12         super("MulticastServerThread");
13     }
14  
15     public void run() {
16         while (moreQuotes) {
17             try {
18                 byte[] buf = new byte[256];
19  
20                 // construct quote
21                 String dString = null;
22                 if (in == null)
23                     dString = new Date().toString();
24                 else
25                     dString = getNextQuote();
26                 buf = dString.getBytes();
27  
28                 // send it
29                 InetAddress group = InetAddress.getByName("230.0.0.1");
30                 DatagramPacket packet = new DatagramPacket(buf, buf.length, group, 4446);
31                 socket.send(packet);
32                 
33                 // sleep for a while
34                 try {
35                     sleep((long)(Math.random() * FIVE_SECONDS));
36                 } catch (InterruptedException e) { 
37                     
38                 }
39             } catch (IOException e) {
40                 e.printStackTrace();
41                 moreQuotes = false;
42             }
43         }
44         socket.close();
45     }
46 }
注意看程式第 29 行,裡面有個 IP -- 230.0.0.1,這是做什麼的? 這個值只要是 UDP 群播的合理 IP 值就可以了,我們可以任意的選擇,也可以傳入 null 表示將作 loopback。這個值會成為群播的"群組",client 端倒底要傾聽那裡傳來的群播訊息? 即是透過指定群組來決定。程式第 30 行的 4446 這個 port 值也很重要,client 端必須傾聽這個 port 才收的到群播的訊息。程式第 31 行,將訊息廣播出去… 程式第 35 行只是停頓數秒,讓我們查看程式的執行比較方便罷了,整個廣播的過程會持續到所有文字檔內容被讀取並傳送完畢為止。
【註】在 IPv4 裡,224.0.0.1 ~ 239.255.255.255 這個網段是被指定作為 multicast 群組。
 1 package idv.steven;
 2 
 3 import java.io.*;
 4 import java.net.*;
 5 import java.util.*;
 6  
 7 public class MulticastClient {
 8  
 9     public static void main(String[] args) throws IOException {
10  
11         MulticastSocket socket = new MulticastSocket(4446);
12         InetAddress address = InetAddress.getByName("230.0.0.1");
13         socket.joinGroup(address);
14  
15         DatagramPacket packet;
16      
17         // get a few quotes
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  
24             String received = new String(packet.getData(), 0, packet.getLength());
25             System.out.println("Quote of the Moment: " + received);
26         }
27  
28         socket.leaveGroup(address);
29         socket.close();
30     }
31 }
client 端程式要注意的就是第 11~13 行,指定要傾聽那個群組、那個 port 的訊息,透過呼叫 joinGroup 後加入這個群組,真正開始傾聽是在程式第 22 行。

java.io: UDP 網路程式設計 (request / response)

在 Oracle 網站上,有個簡單的 UDP 程式範例 --- Lesson: All About Datagrams,這個程式和前一個程式基本上是差不多的,差別在於,它是由 client 先送個 request 給 server 端,server 端接收到後,回應訊息給 client 端,下面是 Oracle 的範例程式。
 1 package idv.steven;
 2 
 3 import java.io.*;
 4 import java.net.*;
 5 import java.util.*;
 6  
 7 public class QuoteClient {
 8     public static void main(String[] args) throws IOException {
 9  
10         if (args.length != 1) {
11              System.out.println("Usage: java QuoteClient <hostname>");
12              return;
13         }
14  
15         //get a datagram socket
16         DatagramSocket socket = new DatagramSocket();
17  
18         //send request
19         byte[] buf = new byte[256];
20         InetAddress address = InetAddress.getByName(args[0]);
21         DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 4445);
22         socket.send(packet);
23      
24         //get response
25         packet = new DatagramPacket(buf, buf.length);
26         socket.receive(packet);
27  
28         //display response
29         String received = new String(packet.getData(), 0, packet.getLength());
30         System.out.println("Quote of the Moment: " + received);
31      
32         socket.close();
33     }
34 }
client 端程式在送出 request 後,會停在第 26 行,以等待 server 端的 response。
 1 package idv.steven;
 2 
 3 import java.io.IOException;
 4 
 5 public class QuoteServer {
 6 
 7     public static void main(String[] args) throws IOException {
 8         new QuoteServerThread().start();
 9     }
10 }
 1 package idv.steven;
 2 
 3 import java.io.*;
 4 import java.net.*;
 5 import java.util.*;
 6  
 7 public class QuoteServerThread extends Thread {
 8  
 9     protected DatagramSocket socket = null;
10     protected BufferedReader in = null;
11     protected boolean moreQuotes = true;
12  
13     public QuoteServerThread() throws IOException {
14     this("QuoteServerThread");
15     }
16  
17     public QuoteServerThread(String name) throws IOException {
18         super(name);
19         socket = new DatagramSocket(4445);
20  
21         try {
22             in = new BufferedReader(new FileReader("one-liners.txt"));
23         } catch (FileNotFoundException e) {
24             System.err.println("Could not open quote file. Serving time instead.");
25         }
26     }
27  
28     public void run() {
29  
30         while (moreQuotes) {
31             try {
32                 byte[] buf = new byte[256];
33  
34                 // receive request
35                 DatagramPacket packet = new DatagramPacket(buf, buf.length);
36                 socket.receive(packet);
37  
38                 // figure out response
39                 String dString = null;
40                 if (in == null)
41                     dString = new Date().toString();
42                 else
43                     dString = getNextQuote();
44  
45                 buf = dString.getBytes();
46  
47                 // send the response to the client at "address" and "port"
48                 InetAddress address = packet.getAddress();
49                 int port = packet.getPort();
50                 packet = new DatagramPacket(buf, buf.length, address, port);
51                 socket.send(packet);
52             } catch (IOException e) {
53                 e.printStackTrace();
54                 moreQuotes = false;
55             }
56         }
57         socket.close();
58     }
59  
60     protected String getNextQuote() {
61         String returnValue = null;
62         try {
63             if ((returnValue = in.readLine()) == null) {
64                 in.close();
65         moreQuotes = false;
66                 returnValue = "No more quotes. Goodbye.";
67             }
68         } catch (IOException e) {
69             returnValue = "IOException occurred in server.";
70         }
71         return returnValue;
72     }
73 }
server 端程式啟動後,會先停在第 36 行以等待 request,當收到 request 後程式立即往下執行,server 端的回應內容只是由文字檔 one-liners.txt 中抓取一行文字將它回傳給 client 端。至於 client 端在那? 從程式第 48、49 行可以看到,由 client 端傳來的封包中,可以解析出 client 端的網址和 port 號,因此,server 端知道要將回應值傳向那裡。

java.io: UDP 網路程式設計 getting started

在大部份的網路應用裡,TCP 似乎比 UDP 來的多許多,程式人員通常也是對撰寫 TCP 程式比較熟悉,這裡將整理一些 UDP 程式設計的說明,希望對大家有幫助。
一開始先介紹三個最重要的類別,如下:
  • DatagramPacket: UDP 的封包。
  • DatagramSocket: 用來建立 UDP socket 的類別,包含 client 及 server。
  • MulticastSocket: 用來建立群播 UDP socket 的類別,包含 client 和 server。
第一個程式,先寫個比較簡單的,只是點對點的 UDP 傳送,由 client 端送"Hello"這個訊息到 server 端,由 server 端將它顯示出來。
 1 package idv.steven;
 2 
 3 import java.io.IOException;
 4 import java.net.DatagramPacket;
 5 import java.net.DatagramSocket;
 6 import java.net.InetAddress;
 7 
 8 public class UDPClient {
 9     public void run() throws IOException {
10         InetAddress addr = InetAddress.getLocalHost();
11         String data = "Hello";
12         byte[] buffer = data.getBytes();
13         DatagramPacket packet = new DatagramPacket(buffer, buffer.length, addr, 12345);
14         
15         DatagramSocket dgSocket = new DatagramSocket();
16         dgSocket.send(packet);
17     }
18 
19     public static void main(String[] args) throws IOException {
20         new UDPClient().run();
21     }
22 }
上面的程式是 client 端程式,會將"Hello"的訊息送到本機端 port 12345 的 UDP server,DatagramPacket 是在 java.net.* 這個 package 中,所規範的 UDP 網路連線方式,要傳送的封包,封包中除了要傳送的內容外,也封存了要傳送的位址,產生好封包後,透過 DatagramSocket 送出。
 1 package idv.steven;
 2 
 3 import java.io.IOException;
 4 import java.net.DatagramPacket;
 5 import java.net.DatagramSocket;
 6 
 7 public class UDPServer {
 8     public void run() throws IOException {
 9         byte[] buffer = new byte[10];
10         DatagramPacket packet = new DatagramPacket(buffer, 10);
11         DatagramSocket dgSocket = new DatagramSocket(12345);
12         System.out.println("waiting ... (port: " + dgSocket.getLocalPort() +")");
13         dgSocket.receive(packet);
14         System.out.println("received, content below:");
15         for(int i = 0; i < buffer.length; i++) {
16             System.out.println(buffer[i]);
17         }
18     }
19 
20     public static void main(String[] args) throws IOException {
21         new UDPServer().run();
22     }
23 }
Server 端的寫法也很簡單,同樣是使用 DatagramSocket 來產生 UDP 服務,建立這個類別時,指定要傾聽的 port 號後,呼叫 receive method,即可進行傾聽,程式在 block 在 receive method,以等待 client 端送來的訊息。當收到訊息時,訊息會被存入 DatagramPacket 中,第 15~17 行將收到的內容顯示出來,這裡顯示出來的是 ASCII 碼。測試時,先啟動 server,再啟動 client。