Java Network Programming (3rd ed) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Java Network Programming (3rd ed) [Electronic resources] - نسخه متنی

Harold, Elliotte Rusty

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








14.2 Working with Multicast Sockets


Enough theory. In Java, you multicast data using the
java.net.MulticastSocket class, a subclass of
java.net.DatagramSocket:

public class MulticastSocket extends DatagramSocket

As you would expect,
MulticastSocket's behavior is
very similar to DatagramSocket's:
you put your data in DatagramPacket objects that
you send and receive with the MulticastSocket.
Therefore, I won't repeat the basics; this
discussion assumes that you already know how to work with datagrams.
If you're jumping around in this book rather than
reading it cover to cover, now might be a good time to go back and
read Chapter 13 on UDP.

To receive data that is being multicast from a remote site, first
create a MulticastSocket with the
MulticastSocket( ) constructor. Next, join a
multicast group using the
MulticastSocket's
joinGroup( ) method. This
signals the routers in the path between you and the server to start
sending data your way and tells the local host that it should pass
you IP packets addressed to the multicast group.

Once you've joined the multicast group, you receive
UDP data just as you would with a DatagramSocket.
That is, you create a DatagramPacket with a byte
array that serves as a buffer for data and enter a loop in which you
receive the data by calling the receive( ) method
inherited from the DatagramSocket class. When you
no longer want to receive data, leave the multicast group by invoking
the socket's leaveGroup() method. You can then close the socket
with the close( ) method inherited from
DatagramSocket.

Sending data to a multicast address is similar to sending UDP data to
a unicast address. You do not need to join a multicast group to send
data to it. You create a new DatagramPacket, stuff
the data and the address of the multicast group into the packet, and
pass it to the send( ) method. The one difference
is that you must explicitly specify the packet's TTL
value.

There is one caveat to all this: multicast sockets are a security hole
big enough to drive a small truck through. Consequently, untrusted
code running under the control of a
SecurityManager is not allowed to do anything
involving multicast sockets. Remotely loaded code is normally allowed
to send datagrams to or receive datagrams from the host it was
downloaded from. However, multicast sockets don't
allow this sort of restriction to be placed on the packets they send
or receive. Once you send data to a multicast socket, you have very
limited and unreliable control over which hosts receive that data.
Consequently, most environments that execute remote code take the
conservative approach of disallowing all multicasting.


14.2.1 The Constructors


The
constructors are simple. Each one calls the equivalent constructor in
the DatagramSocket superclass.

14.2.1.1 public MulticastSocket( ) throws SocketException


This constructor creates a socket that is bound to an anonymous port
(i.e., an unused port assigned by the system). It is useful for
clients (i.e., programs that initiate a data transfer) because they
don't need to use a well-known port: the recipient
replies to the port contained in the packet. If you need to know the
port number, look it up with the getLocalPort( )
method inherited from DatagramSocket. This
constructor throws a SocketException if the
Socket can't be created. For
example:

try {
MulticastSocket ms = new MulticastSocket( );
// send some datagrams...
}
catch (SocketException se) {
System.err.println(se);
}

14.2.1.2 public MulticastSocket(int port) throws SocketException


This constructor creates a socket that receives datagrams on a
well-known port. The port argument specifies the
port on which this socket listens for datagrams. As with regular TCP
and UDP unicast sockets, on a Unix system a program needs to be run
with root privileges in order to create a
MulticastSocket on a port numbered from 1 to
1,023.

This constructor throws a SocketException if the
Socket can't be created. A
Socket can't be created if you
don't have sufficient privileges to bind to the port
or if the port you're trying to bind to is already
in use. Note that since a multicast socket is a datagram socket as
far as the operating system is concerned, a
MulticastSocket cannot occupy a port already
occupied by a DatagramSocket, and vice versa. For
example, this code fragment opens a multicast socket on port 4,000:

try {
MulticastSocket ms = new MulticastSocket(4000);
// receive incoming datagrams...
}
catch (SocketException ex) {
System.err.println(ex);
}

14.2.1.3 public MulticastSocket(SocketAddress bindAddress) throws IOException // Java 1.4


Starting in Java 1.4, you can create a
MulticastSocket using a
SocketAddress object. If the
SocketAddress is bound to a port, then this is
pretty much the same as the previous constructor. For example, this
code fragment also opens a MulticastSocket on port
4000 that listens on all network interfaces and addresses:

try {
SocketAddress address = new InetSocketAddress(4000);
MulticastSocket ms = new MulticastSocket(address);
// receive incoming datagrams...
}
catch (SocketException ex) {
System.err.println(ex);
}

However, the SocketAddress can also be bound to a
specific network interface on the local host, rather than listening
on all network interfaces. For example, this code fragment also opens
a MulticastSocket on port 4000 that only listens
to packets arriving on 192.168.254.32:

try {
SocketAddress address = new InetSocketAddress("192.168.254.32", 4000);
MulticastSocket ms = new MulticastSocket(address);
// receive incoming datagrams...
}
catch (SocketException ex) {
System.err.println(ex);
}

Finally, you can pass null to this constructor to create an unbound
socket, which would later be connected with the bind() method. This is useful when setting socket options that
can only be set before the socket is bound. For example, this code
fragment creates a multicast socket with SO_REUSEADDR disabled (that
option is normally enabled by default for multicast sockets):

try {
MulticastSocket ms = new MulticastSocket(null);
ms.setReuseAddress(false);
SocketAddress address = new InetSocketAddress(4000);
ms.bind(address);
// receive incoming datagrams...
}
catch (SocketException ex) {
System.err.println(ex);
}


14.2.2 Communicating with a Multicast Group


Once a
MulticastSocket has been created, it can perform
four key operations:

Join a multicast group.

Send data to the members of the group.

Receive data from the group.

Leave the multicast group.

The MulticastSocket class has methods for
operations 1, 2, and 4. No new method is required to receive data.
The receive( ) method of the superclass,
DatagramSocket, suffices for this task. You can
perform these operations in any order, with the exception that you
must join a group before you can receive data from it (or, for that
matter, leave it). You do not need to join a group to send data to
it, and the sending and receiving of data may be freely interwoven.

14.2.2.1 public void joinGroup(InetAddress address) throws IOException


To receive data from a MulticastSocket, you must
first join a multicast group. To join a group, pass an
InetAddress object for the multicast group to the
joinGroup( ) method. If you
successfully join the group, you'll receive any
datagrams intended for that group. Once you've
joined a multicast group, you receive datagrams exactly as you
receive unicast datagrams, as shown in the previous chapter. That is,
you set up a DatagramPacket as a buffer and pass
it into this socket's receive( )
method. For example:

try {
MulticastSocket ms = new MulticastSocket(4000);
InetAddress ia = InetAddress.getByName("224.2.2.2");
ms.joinGroup(ia);
byte[] buffer = new byte[8192];
while (true) {
DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
ms.receive(dp);
String s = new String(dp.getData( ), "8859_1");
System.out.println(s);
}
}
catch (IOException ex) {
System.err.println(ex);
}

If the address that you try to join is not a multicast address (that
is, it is not between 224.0.0.0 and 239.255.255.255), the
joinGroup( ) method throws an
IOException.

A single MulticastSocket can join multiple
multicast groups. Information about membership in multicast groups is
stored in multicast routers, not in the object. In this case,
you'd use the address stored in the incoming
datagram to determine which address a packet was intended for.

Multiple multicast sockets on the same machine and even in the same
Java program can all join the same group. If so,
they'll all receive all data addressed to that group
that arrives at the local host.

14.2.2.2 public void joinGroup(SocketAddress address, NetworkInterface interface) throws IOException // Java 1.4


Java 1.4 adds this overloaded variant of joinGroup()
that allows you to join a multicast group only on a specified local
network interface. A proxy server or firewall might use this to
specify that it will accept multicast data from the interface
connected to the LAN, but not the interface connected to the global
Internet, for instance.

For example, this code fragment attempts to join the group with IP
address 224.2.2.2 on the network interface named
"eth0", if such an interface
exists. If no such interface exists, then it joins on all available
network interfaces:

MulticastSocket ms = new MulticastSocket(4000);
SocketAddress group = new InetSocketAddress("224.2.2.2", 40);
NetworkInterface ni = NetworkInterface .getByName("eth0");
if (ni != null) {
ms.joinGroup(group, ni);
}
else {
ms.joinGroup(group);
}

Other than the extra argument specifying the network interface to
listen from, this behaves pretty much like the single argument
joinGroup( ) method. For instance, passing a
SocketAddress object that does not represent a
multicast group as the first argument throws an
IOException.

14.2.2.3 public void leaveGroup(InetAddress address) throws IOException


The leaveGroup( ) method signals
that you no longer want to receive datagrams from the specified
multicast group. A signal is sent to the appropriate multicast
router, telling it to stop sending you datagrams. If the address you
try to leave is not a multicast address (that is, if it is not
between 224.0.0.0 and 239.255.255.255), the method throws an
IOException. However, no exception occurs if you
leave a multicast group you never joined.

14.2.2.4 public void leaveGroup(SocketAddress multicastAddress, NetworkInterface interface) throws IOException // Java 1.4


Java 1.4 also allows you to specify that you no longer want to
receive datagrams on one particular network interface. Perhaps you do
wish to continue receiving datagrams on other network interfaces. For
instance, you could join on all interfaces, and then leave just one.
To be honest, this is a bit of a stretch. This method was probably
included mostly for symmetry with joinGroup( ).

14.2.2.5 public void send(DatagramPacket packet, byte ttl) throws IOException


Sending data with a MulticastSocket is similar to
sending data with a DatagramSocket. Stuff your
data into a DatagramPacket object and send it off
using the send( ) method inherited from
DatagramSocket:

public void send(DatagramPacket p) throws IOException

The data is sent to every host that belongs to the multicast group to
which the packet is addressed. For example:

try {
InetAddress ia = InetAddress.getByName("experiment.mcast.net");
byte[] data = "Here's some multicast data\r\n".getBytes( );
int port = 4000;
DatagramPacket dp = new DatagramPacket(data, data.length, ia, port);
MulticastSocket ms = new MulticastSocket( );
ms.send(dp);
}
catch (IOException ex) {
System.err.println(ex);
}

However, the MulticastSocket class adds an
overloaded variant of the send(
)
method that lets you provide a value for
the Time-To-Live field ttl. By default, the
send( ) method uses a TTL of 1; that is, packets
don't travel outside the local subnet. However, you
can change this setting for an individual packet by passing an
integer from 0 to 255 as the second argument to the send() method. For example:

  DatagramPacket dp = new DatagramPacket(data, data.length, ia, port);  
MulticastSocket ms = new MulticastSocket( );
ms.send(dp, 64);

14.2.2.6 public void setInterface(InetAddress address) throws SocketException


On a multihomed host, the setInterface() method chooses the
network interface used for multicast sending and receiving.
setInterface( ) throws a
SocketException if the
InetAddress argument is not the address of a
network interface on the local machine. It is unclear why the network
interface is immutably set in the constructor for unicast
Socket and DatagramSocket
objects but is variable and set with a separate method for
MulticastSocket objects. To be safe, set the
interface immediately after constructing a
MulticastSocket and don't change
it thereafter. Here's how you might use
setInterface( ):

MulticastSocket ms;
InetAddress ia;
try {
ia = InetAddress.getByName("www.ibiblio.org");
ms = new MulticastSocket(2048);
ms.setInterface(ia);
// send and receive data...
}
catch (UnknownHostException ue) {
System.err.println(ue);
}
catch (SocketException se) {
System.err.println(se);
}

14.2.2.7 public InetAddress getInterface( ) throws SocketException


If you need to know the address of the interface the socket is bound
to, call getInterface(). It
isn't clear why this method would throw an
exception; in any case, you must be prepared for it. For example:

try {
MulticastSocket ms = new MulticastSocket(2048);
InetAddress ia = ms.getInterface( );
}
catch (SocketException se) {
System.err.println(ue);
}

14.2.2.8 public void setNetworkInterface(NetworkInterface interface) throws SocketException // Java 1.4


The setNetworkInterface() method serves the same purpose as the
setInterface( ) method; that is, it chooses the
network interface used for multicast sending and receiving. However,
it does so based on the local name of a network interface such as
"eth0" (as encapsulated in a
NetworkInterface object) rather than on the IP
address bound to that network interface (as encapsulated in an
InetAddress object). setNetworkInterface() throws a SocketException if the
NetworkInterface passed as an argument is not a
network interface on the local machine.

14.2.2.9 public NetworkInterface getNetworkInterface( ) throws SocketException // Java 1.4


The getNetworkInterface() method returns a
NetworkInterface object representing the network
interface on which this MulticastSocket is
listening for data. If no network interface has been explicitly set
in the constructor or with setNetworkInterface( ),
it returns a placeholder object with the address
"0.0.0.0" and the index -1. For
example, this code fragment prints the network interface used by a
socket:

NetworkInterface intf = ms.getNetworkInterface( );
System.out.println(intf.getName( ));

14.2.2.10 public void setTimeToLive(int ttl) throws IOException // Java 1.2


The setTimeToLive() method sets the default
TTL value used for packets sent from the socket using the
send(Datagrampacket dp) method inherited from
DatagramSocket (as opposed to the
send(Datagrampacket dp,
byte ttl) method in
MulticastSocket). This method is only available in
Java 1.2 and later. In Java 1.1, you have to use the setTTL(
)
method instead:

public void setTTL(byte ttl) throws IOException

The setTTL( ) method is deprecated in Java 2 and
later because it only allows TTL values from 1 to 127 rather than the
full range from 1 to 255.

14.2.2.11 public int getTimeToLive( ) throws IOException // Java 1.2


The getTimeToLive() method returns the default TTL value of
the MulticastSocket. It's not
needed very much. This method is also available only in Java 1.2 and
later. In Java 1.1, you have to use the getTTL( )
method instead:

public byte getTTL( ) throws IOException

The getTTL( ) method is deprecated in
Java 1.2 and later because it doesn't properly
handle TTLs greater than 127it truncates them to 127. The
getTimeToLive( ) method can handle the full range
from 1 to 255 without truncation because it returns an
int instead of a byte.

14.2.2.12 public void setLoopbackMode(boolean disable) throws SocketException // Java 1.4


Whether or not a host receives the multicast packets it sends is
platform-dependentthat is, whether or not they loop back.
Passing true to setLoopback() indicates you don't
want to receive the packets you send. Passing
false indicates you do want to receive the packets
you send. However, this is only a hint. Implementations are not
required to do as you request.

14.2.2.13 public boolean getLoopbackMode( ) throws SocketException // Java 1.4


Because loopback mode is only a hint that may not be followed on all
systems, it's important to check what the loopback
mode is if you're both sending and receiving
packets. The getLoopbackMode() method returns
true if packets are not looped back and
false if they are. (This feels backwards to me. I
suspect this method was written by a programmer following the
ill-advised convention that defaults should always be true.)

If the system is looping packets back and you don't
want it to, you'll need to recognize the packets
somehow and discard them. If the system is not looping the packets
back and you do want it to, store copies of the packets you send and
inject them into your internal data structures manually at the same
time you send them. You can ask for the behavior you want with
setLoopback( ), but you can't
count on it.


/ 164