Download UDP and IGMP

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
ENIC4023 - Networked Embedded Systems Development
UDP and IGMP Lab
Note: the examples in this lab require to be run on networked systems with IP addresses. Standalone PCs and
some laptop systems without permanent IP addresses are not suitable.
UDP Overview
UDP (User Datagram Protocol) is a datagram-centric transport protocol. It differs from TCP in that it is
connectionless. The implementation of UDP communication in Java is thus quite different from that of TCP.
Java's implementation of UDP is split into two classes: DatagramPacket and DatagramSocket. The
DatagramPacket class stuffs byte of data into UDP packets called datagrams and lets you unstuff datagrams
that you receive. A DatagramSocket sends as well as receives UDP datagrams. To send data, you put it in a
DatagramPacket and send the packet using a DatagramSocket. To receive data, you receive a
DatagramPacket object from a DatagramSocket and then read the contents of the packet.
The sockets are very simple: In UDP everything about a datagram, including the address to which it is directed,
is included in the packet itself; the socket needs to know only the local ports on which to listen or send. The
socket isn't dedicated to a single connection, as it is in TCP. UDP doesn't have any concept of a connection
between two hosts; it only knows about individual datagrams. Hence UDP sockets work with individual
datagram packets whereas TCP sockets treat a network connection as a stream.
The Java DatagramPacket Class
This class represents a datagram packet. Datagram packets are used to implement a connectionless packet
delivery service. Each message is routed from one machine to another based solely on information contained
within that packet. Multiple packets sent from one machine to another might be routed differently, and might
arrive in any order. Packet delivery is not guaranteed.
The class provides methods to get and set the source or destination address from the IP header, to get and set the
source or destination port, to get and set the data, and to get and set the length of the data. The theoretical
maximum length for the data is 65,507 bytes but some UDP implementations don't support more than 8,192
bytes, so applications should not normally exceed this.
The constructors for the class are unusual in that you use different ones whether you are going to send data to
the net of receive data from the net. All the constructors take as arguments a byte array that holds the datagram's
data, and the number of bytes in that array to use for the datagram's data.
An abridged class diagram for the DatagramPacket class is shown (see Java API docs for full listing):
DatagramPacket
<<constructors>>
+ DatagramPacket(byte[]
+ DatagramPacket(byte[]
+ DatagramPacket(byte[]
+ DatagramPacket(byte[]
int port)
....
buf,
buf,
buf,
buf,
<<query>>
+ InetAddress getAddress()
+ byte[] getData()
+ int getLength()
+ int getOffset()
int
int
int
int
length)
offset, int length)
length, InetAddress address, int port)
offset, int length, InetAddress address,
+ int getPort()
....
<<update>>
+ void setAddress(InetAddress iaddr)
+ void setData(byte[] buf)
+ void setData(byte[] buf, int offset, int length)
+ void setLength(int length)
+ void setPort(int port)
....
Using the DatagramPacket class is fairly straightforward. One difficulty that needs to be overcome though,
is dealing with the byte array that forms the data part of the UDP datagram. Often the data will represent text, so
it is useful to have means to convert between Strings and byte arrays.
The data is extracted as a byte array from a received DatagramPacket using the getData() method.
Conversion from a byte array to a String can be achieved using the following String constructor:
public String(byte[] bytes, String charsetName) //see javadocs
The first argument is the byte array to be converted; the second argument contains the name of the encoding
used for this string such as ASCII or ISO-8859-1. Thus given a DatagramPacket dp received from the network,
you can convert it to a String with:
String s = new String(dp.getData(), "ASCII");
The most convenient way to convert Strings to byte arrays is with the following method of the String class:
byte[] getBytes(String CharsetName)
//see javadocs
Thus given an ASCII String s, you can convert it to a byte array with:
byte[] data = s.getBytes("ASCII")
This byte array can then be used to construct the DatagramPacket for transmission.
The Java DatagramSocket Class
To send or receive a DatagramPacket you need to open a datagram socket. All datagram sockets are bound
to a local port on which they listen for incoming datagrams and which they place in the header of outgoing
datagrams.
If writing a client, it doesn't matter what the local ports is, so you call a constructor that lets the system assign an
unused port. This port number is placed in any outgoing datagrams and will be used by the server to address any
response datagrams.
If writing a server, clients need to know on which port the server is listening for incoming datagrams. Thus
when a server constructs a DatagramSocket it must specify the local port on which it will listen. Other than
this the sockets used by clients and servers are identical.
An abridged class diagram for the DatagramSocket class is shown (see Java API docs for full listing):
DatagramSocket
<<constructors>>
+ DatagramSocket()
+ DatagramSocket(int port)
....
<<query>>
+ int getLocalPort()
....
<<update>>
+ void send(DatagramPacket p)
+ void receive(DatagramPacket p)
+ void close()
....
Example - Port Scanner
import java.net.*;
public class UDPPortScanner {
public static void main(String[] args) {
for (int port = 0; port <= 65535; port++) {
try {
// the next line will fail and drop into the catch block if
// there is already a server running on the port
DatagramSocket server = new DatagramSocket(port);
server.close();
if ((port % 100) == 0) System.out.print(".");
}
catch (SocketException e) {
System.out.print("\nServer on UDP port " + port);
} // try
} // for
} //main
} //class
The above is a port scanner that looks for UDP ports in use on the local host. It decides that the port is in use if
the DatagramSocket constructor throws an exception.
Unlike with TCP, it is not practical to scan UDP ports on a remote system. With TCP there is always some
indication that your TCP packet has been received by a listening port regardless of application layer protocol.
With UDP, to determine if a UDP server is listening, you have to send a packet that it will recognise and
respond to.
Example - Simple UDP Client
The following is a simple UDP client application that sends a 1 byte datagram to a UDP server and outputs the
textual response from the server (if any) on the screen (simplified version of Harold p436):
import java.net.*;
import java.io.*;
public class UDPPoke {
private DatagramSocket ds;
private DatagramPacket out;
private DatagramPacket in;
public UDPPoke(InetAddress host, int port) throws SocketException {
out = new DatagramPacket(new byte[1], 1, host, port);
in = new DatagramPacket(new byte[8192], 8192);
ds = new DatagramSocket(0);
ds.connect(host, port);
}
public String poke() throws IOException {
try {
ds.send(out);
ds.receive(in);
// blocks until response is received
return(new String(in.getData(), 0, in.getLength(), "ASCII"));
}
catch (IOException e) {
return("");
} //catch
} //poke
public static void main(String[] args) {
InetAddress host;
int port = 0;
try {
host = InetAddress.getByName(args[0]);
port = Integer.parseInt(args[1]);
if (port < 1 || port > 65535) throw new Exception();
}
catch (Exception e) {
System.out.println("Usage: java UDPPoke host port");
return;
}
try {
UDPPoke poker = new UDPPoke(host, port);
String responce = poker.poke();
System.out.println(responce);
} catch (Exception e) {
System.err.println(e);
} //catch
} //main
} //class
Try the above out on a UDP Daytime server that your lab supervisor will have running for you.
Example - UDP Daytime Server
(The following two classes have been taken from Harold p440 and p444, both slightly modified)
The UDPServer class is an abstract class that can be used to build a variety of UDP servers that respond in
different ways to an incoming datagram (without consideration of the data that the incoming datagram contains):
import java.net.*;
import java.io.*;
public abstract class UDPServer extends Thread {
protected DatagramSocket ds;
public UDPServer(int port) throws SocketException {
this.ds = new DatagramSocket(port);
}
public void run() {
byte[] buff = new byte[8192];
while (true) {
DatagramPacket in = new DatagramPacket(buff, buff.length);
try {
ds.receive(in);
this.respond(in);
}
catch (IOException e) {
System.err.println(e);
} //catch
} //while
} //run
public abstract void respond(DatagramPacket request);
} //class
To use the UDPServer class, subclasses have to provide an implementation for the respond method. The
following UDPDaytimeServer class inherits from the UDPServer class in order to implement a daytime
server:
import java.net.*;
import java.io.*;
import java.util.*;
public class UDPDaytimeServer extends UDPServer {
public UDPDaytimeServer() throws SocketException {
super(13);
}
public void respond(DatagramPacket packet) {
try {
Date now = new Date();
String response = now.toString() + "\r\n";
byte[] data = response.getBytes("ASCII");
DatagramPacket out = new DatagramPacket(data, data.length,
packet.getAddress(), packet.getPort());
ds.send(out);
}
catch (IOException e) {
System.err.println(e);
} //catch
} //respond
public static void main(String[] args) {
try {
UDPDaytimeServer server = new UDPDaytimeServer();
server.start();
}
catch (SocketException e) {
System.err.println(e);
} //catch
} //main
} //class
The following shows an Ethereal trace of the network activity when a UDPPoke client accesses a
UDPDaytimeServer server. As both are running on hosts which are on the same network, the client requires the
physical address of the server for packet delivery, hence it issues an ARP broadcast. The server replies with an
ARP response. The client then sends a UDP packet to the server on the daytime protocol port (port 13). The
source port has been chosen at random from those not used on the client machine, here it is port 1683. The
server then responds by sending a single UDP packet with the requested data to the client on that port.
Note the simplicity of UDP, there being no acknowledgements. If the response packet got lost, it would be up to
the application software on the client to realise this and re-request it.
IGMP Overview
IP multicasting allows IP datagrams to be sent to explicit hosts on multiple networks simultaneously. IGMP
provides a mechanism for IP devices to register with multicast routers, indicating the multicast groups they are
interested in participating in.
Java's implementation of multicasting is similar to that of UDP. The DatagramPacket is still used to contain
the datagrams being sent and received, but instead of the DatagramSocket, the MulticastSocket class is
used to do the sending and receiving.
The Java MulticastSocket class
The MulticastSocket class is a subclass of the DatagramSocket class. To receive data that is being
multicast from a remote site, a MulticastSocket is first created with the MulticastSocket()
constructor. Next the MulticastSocket needs to join a multicast group using its joinGroup() method.
This signals the routers in the path between you (the application) 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, you leave the multicast group by calling the socket's leaveGroup()
method. The socket can then be closed 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 it, and pass it to the send() method. The one difference is that you must explicitly specify
the packets TTL value (using the setTimeToLive() method).
An abridged class diagram for the MulticastSocket class is shown (see Java API docs for full listing):
MulticastSocket
<<constructors>>
+ MulticastSocket()
+ MulticastSocket(int port)
....
<<query>>
+ int getLocalPort()
+ int getTimeToLive()
....
<<update>>
+ void joinGroup(InetAddress mcastaddr)
+ void leaveGroup(InetAddress mcastaddr)
+ void setTimeToLive(int ttl)
+ void send(DatagramPacket p)
+ void receive(DatagramPacket p)
+ void close()
....
Example - Multicast Client
(modified version of Harold p470)
import java.net.*;
import java.io.*;
public class MulticastSender {
public static void main(String[] args) {
InetAddress ia = null;
int port = 0;
// read the address from the command line
try {
ia = InetAddress.getByName(args[0]);
port = Integer.parseInt(args[1]);
} catch (Exception e) {
System.out.println("Usage: java MulticastSender mcast_addr port");
System.exit(1);
}
try {
InetAddress host = InetAddress.getLocalHost();
byte[] data = ("Multicast data from: " + host.toString()).getBytes();
DatagramPacket dp = new DatagramPacket(data, data.length, ia, port);
MulticastSocket ms = new MulticastSocket();
ms.setTimeToLive(1);
ms.send(dp);
ms.close();
} catch (SocketException se) {
System.err.println(se);
} catch (IOException ie) {
System.err.println(ie);
}
}
}
The above application sends a message to a multicast group and port specified on the command line. The
message is in text form and includes the local host name. The data to be sent is put in a DatagramPacket
together with the group address and port. A MulticastSocket is opened on an anonymous port and the TTL
field is set to 1, ie for local multicasting. The DatagramPacket is sent by the MulticastSocket and the
socket is then closed. Note that as this application only sends a packet to a multicast group, there is no need to
join and leave the group.
Example - Multicast Server
(modified version of Harold p467)
import java.net.*;
import java.io.*;
public class MulticastSniffer {
public static void main(String[] args) {
InetAddress group = null;
int port = 0;
//read the address from the command line
try {
group = InetAddress.getByName(args[0]);
port = Integer.parseInt(args[1]);
} catch (Exception e) {
System.out.println("Usage: java MulticastSniffer mcast_addr port");
System.exit(1);
}
MulticastSocket ms = null;
try {
ms = new MulticastSocket(port);
ms.joinGroup(group);
byte[] buffer = new byte[8192];
while (true) {
DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
ms.receive(dp);
String s = new String(dp.getData(), 0, dp.getLength(), "ASCII");
System.out.println(s);
}
} catch (IOException e) {
System.err.println(e);
} finally {
if (ms != null) {
try {
ms.leaveGroup(group);
ms.close();
} catch (IOException e) {}
}
}
}
}
The above application sets in a loop listening for datagrams and outputs their contents, interpreted as text, on the
console. The multicast group and port that it listens on are specified as command line arguments. The multicast
socket is opened for a named port (so that sending applications know which port to send the data to). The
joingroup() method must be called to tell the local host to capture packets for that specific group. The local
router will be informed but as we are only doing multicasting locally within the lab, this is not relevant.
This shows an Ethereal trace
of the network activity when
the MulticastSender
application is run. It has
been run on three different
hosts, each sends a packet to
the multicast group
224.0.2.1 on port 8888. The
source ports for each packet
is different as anonymous
ports have been used (ie the
MulticastSocket
constructor just chose an
unused local port at
random).
Examining the data part of
the first packet shows the
ASCII coded message being
sent.
This shows an IGMP
Membership Report packet.
It was sent when the
MulticastSniffer application
was started and the
joinGroup() method of
the MulticastSocket
class was called.
Note that the multicast group
that this report refers to is
224.0.2.1, and this is the
group address to which the
report is sent.
Nowhere is there any port
number specified. The IGMP
Membership Report requests
that the local multicast router
send all traffic addressed to
the group without regard to
the port.
This shows an IGMP Leave
Report packet. This was sent
when the MulticastSniffer
application was closed, ie
when the leaveGroup()
method of the
MulticastSocket class
was called.
Although the multicast
group that this report refers
to is 224.0.2.1, the Leave
Report is sent to the "allrouters" group address of
224.0.0.2