IP協議是Internet上所有信息的傳播手段,UDP(User Datagram Protocol,用戶數據報協議)數據報被封裝在IP包中,發送到網絡上適當的機器。眾所周知,大多數IP使用單點發送,即從一個主機發送一個包到另一個主機上。然而,IP協議也具有多點發送的能力,若要使用多點發送時,一個報文標有一組目標主機地址,當報文發出後,整個組都能收到。為支持多點發送,一定范圍的IP地址被單獨劃分出來。這些IP地址是D類地址,其范圍是224.0.0.0至239.255.255.255。
IP多點發送(或多點廣播)是一種相當新的技術,它是對簡單廣播的改進。多點廣播功能類似於一個單一信息發送到多個接收者的廣播,但僅發送給那些等待它的接收者。其思想就是設置一組網絡地址作為多點廣播地址,其范圍是225.0.0.0到239.255.255.255。每一個多點廣播地址都被看作一個組。當用戶需要從某個地址信息時,加入到該組即可。例如,可能為其股票報價系統設置了地址225.23.32.36作為多點廣播地址,如果一個想要接收股票查詢報價程序,必須加入到225.23.32.36組中。
一、制作多點發送的基本知識
在使用Java進行多點發送時,它的MulticastSocket類是實現這一功能的關鍵。MulticastSocket類允許用戶發送和接收使用多點發送IP的數據報。若要發送或接收多點廣播數據,必須首先創建一個多點廣播套接字(多點廣播套接字類似於數據報套接字,事實上MulticastSocket是DatagramSocket的一個子類)。若要發送一個數據報時,可使用缺省的端口號創建多點廣播套接字,也可以在構造器指定端口號來創建多點廣播套接字:
public MulticastSocket()throws IOException
public MulticastSocket(int portNumber)throws IOException
要加入到一個多點廣播地址,可使用jionGroup方法;若要脫離一個組,相使用leaveGroup方法:
public void jionGroup(InetAddress multicastAddr)throws IOException
public void leaveGroup(InetAddress multicastAddr)throws IOException
在某些系統中,可能有多重網絡接口。這可能會對多點廣播帶來問題,因為用戶需要在一個指定的接口上偵聽,通過調用setInterface可選擇多點廣播套接字所使用的接口:
public void setInterface(InetAddress interface)throws SocketException
通過調用getInterface方法可查詢多點廣播套接字的接口:
public InetAddress getInterface()throws SocketException
用戶在發送多點發送的數據時,使用send方法:
public synchronized void send(DatagramPacket packet, byte timeTolive)throws IOException
其中TTL值指定了數據包應該跨過多少個網絡,當TTL為0時,指定數據包應停留在本地主機;當TTL的值為1時,指定數據包發送到本地網絡;當TTL的值為32時,意味著只應發送到本站點的網絡上;當TTL為64時,意味著數據包應保留在本地區;當TTL的值為128時,意味著數據應保留在本大洲;當TTL為255時,意味著數據包應發送到所有地方;若使用缺省的send方法時,TTL的值為1。
二、制作多點發送應用程序
下面就運用Java制作一個多點發送應用程序。其中程序1(MulticastCastSender.java)是一個發送數據報到一個指定多點發送IP地址的程序,該程序運行時使用兩個參數:第一個指定發送數據報的多點發送IP地址,另一個則指定偵聽應用程序的UDP端口。其中Main()方法保證了這些參數收到後,實例化一個MultiCastSender對象。另外,構造器使用多點發送IP地址的String對象,創建一個InetAddress實例,然後再在動態分配的端口上為發送數據報創建一個MulticastSocket,然後構造器進入一個while循環,從標准輸入上逐入。該程序將每一行的前512個字節包裝到一標有地址的DatagramPacket中,並通過MulticastSocket發送該數據報。
其中程序MultiCastSender.java的源代碼如下:
import java.net.*; // Import package names used.
import java.io.*;
class MultiCastSender {
private static final byte TTL = 1;
private static final int DATAGRAM_BYTES = 1024;
private int mulcastPort;
private InetAddress mulcastIP;
private BufferedReader input;
private MulticastSocket mulcastSocket;
public static void main(String[] args) {
// This must be the same port and IP address used by the receivers.
if (args.length != 2) {
System.out.print("Usage: MultiCastSender " + " \n\tcan be one of 224.x.x.x " + "- 239.x.x.x\n");
System.exit(1);
}
MultiCastSender send = new MultiCastSender(args);
System.exit(0);
}
public MultiCastSender(String[] args) {
DatagramPacket mulcastPacket; // UDP datagram.
String nextLine; // Line from STDIN.
byte[] mulcastBuffer; file:// Buffer for datagram.
byte[] lineData; // The data typed in.
int sendLength; file:// Length of line.
input = new BufferedReader(new InputStreamReader(System.in));
try {
// Create a multicasting socket.
mulcastIP = InetAddress.getByName(args[0]);
mulcastPort = Integer.parseInt(args[1]);
mulcastSocket = new MulticastSocket();
} catch(UnknownHostException excpt) {
System.err.println("Unknown address: " + excpt);
System.exit(1);
} catch(IOException excpt) {
System.err.println("Unable to obtain socket: " + excpt);
System.exit(1);
}
try {
file:// Loop and read lines from standard input.
while ((nextLine = input.readLine()) != null) {
mulcastBuffer = new byte[DATAGRAM_BYTES];
file:// If line is longer than your buffer, use the length of the buffer available.
if (nextLine.length() > mulcastBuffer.length) {
endLength = mulcastBuffer.length;
// Otherwise, use the line's length.
}
else {
sendLength = nextLine.length();
}
// Convert the line of input to bytes.
lineData = nextLine.getBytes();
// Copy the data into the blank byte array
file://which you will use to create the DatagramPacket.
for (int i = 0; i < sendLength; i++) {
mulcastBuffer[i] = lineData[i];
}
mulcastPacket=new DatagramPacket (mulcastBuffer, mulcastBuffer.length, mulcastIP, mulcastPort);
// Send the datagram.
try {
System.out.println("Sending:\t" + nextLine);
mulcastSocket.send(mulcastPacket,TTL);
} catch(IOException excpt) {
System.err.println("Unable to send packet: " + excpt);
}
}
} catch(IOException excpt) {
System.err.println("Failed I/O: " + excpt);
}
mulcastSocket.close(); file:// Close the socket.
}
}
程序2(MultiCastReceiver.java)通過接收多點發送的數據報實現了發送者。該應用程序有兩個參數,這兩個參數必須對應於IP地址和用來激活MultiCastSender的端口。Main()方法首先檢查命令的參數,然後創建一個MultiCastReceiver對象。該對象的構造器用在激活該應用程序的端口上創建一個InetAddress和一個MulticastSocket。在包含於InetAddress的地址加入多點發送者,然後進入一個循環。該對象的構造器從一個套接字接收數據報並打印出包含在數據報中數據,包括發送數據包的計算機和端口。
程序2(MultiCastReceiver)的源代碼如下:
import java.net.*; // Import package names used.
import java.io.*;
class MultiCastReceiver {
private static final int DATAGRAM_BYTES = 1024;
private int mulcastPort;
private InetAddress mulcastIP;
private MulticastSocket mulcastSocket;
private boolean keepReceiving = true;
public static void main(String[] args) {
// This must be the same port and IP address used by the sender.
if (args.length != 2) {
System.out.print("Usage: MultiCastReceiver can be one of " + "224.x.x.x - 239.x.x.x ");
System.exit(1);
}
multiCastReceiver send = new MultiCastReceiver(args);
System.exit(0);
}
public MultiCastReceiver(String[] args) {
DatagramPacket mulcastPacket; // Packet to receive.
byte[] mulcastBuffer; // byte[] array buffer
InetAddress fromIP; file:// Sender address.
int fromPort; // Sender port.
String mulcastMsg; // String of message.
try {
// First, set up your receiving socket.
mulcastIP = InetAddress.getByName(args[0]);
mulcastPort = Integer.parseInt(args[1]);
mulcastSocket = new MulticastSocket(mulcastPort);
// Join the multicast group.
mulcastSocket.joinGroup(mulcastIP);
} catch(UnknownHostException excpt) {
System.err.println("Unknown address: " + excpt);
System.exit(1);
} catch(IOException excpt) {
System.err.println("Unable to obtain socket: " + excpt);
System.exit(1);
}
while (keepReceiving) {
try {
// Create a new datagram.
mulcastBuffer = new byte[DATAGRAM_BYTES];
mulcastPacket = new DatagramPacket(mulcastBuffer, mulcastBuffer.length);
// Receive the datagram.
mulcastSocket.receive(mulcastPacket);
fromIP = mulcastPacket.getAddress();
fromPort = mulcastPacket.getPort();
mulcastMsg = new String(mulcastPacket.getData());
// Print out the data.
System.out.println("Received from " + fromIP + " on port " + fromPort + ": " + mulcastMsg);
} catch(IOException excpt) {
system.err.println("Failed I/O: " + excpt);
}
}
try {
mulcastSocket.leaveGroup(mulcastIP); file:// Leave the group.
} catch(IOException excpt) {
System.err.println("Socket problem leaving group: " + excpt);
}
mulcastSocket.close(); // Close the socket.
}
public void stop() {
if (keepReceiving) {
keepReceiving = false;
}
}
}
若要運用該應用程序,先編譯MultiCastSender和MultiCastReceiver,然後將MultiCastReceiver傳送到其它機器,以便能夠演示多個參與者接收報文的情況,最後Java解釋器運行該應用程序。例如:要向225.2.32.6組在端口1111上發送多點發送報文,應按如下方法進行:
-/class ->java MultiCastSender225.2.32.6 1111
Here is just to test multicast message.
Sending: Here is just to test multicast message.
Do you received it?
Sending: Do you received it?
要接收這些報文,應在一個或多個系統上運行MultiCastReceiver應用程序,這些系統應該加入同一個多點發送組225.2.32.6,並在相同的端口1111上偵聽:
-/class ->java MultiCastReceiver 225.2.32.6 1111
Received from 225.106.36.32 on port 32911: Here is just to test multicast message.
Received from 225.106.36.32 on port 32911: Do you received it?