Part 1 (Ogg-Vorbis format, 8Mbytes) | Part 1 (WAV format, 43Mbytes) |
Part 2 (Ogg-Vorbis format, 3Mbytes) | Part 2 (WAV format, 14Mbytes) |
The TCP/IP protocol was devised through a long-running DARPA project. This worked by implementation followed by RFCs (Request For Comment). TCP/IP is the principal Unix networking protocol. TCP/IP = Transmission Control Protocol/Internet Protocol.
The IP layer supplies a checksum that includes its own header. The header includes the source and destination addresses.
The IP layer handles routing through an Internet. It is also responsible for breaking up large datagrams into smaller ones for transmission and reassembling them at the other end.
The address is a 32 bit integer which gives the IP address. This encodes a network ID and more addressing. The network ID falls into various classes according to the size of the network address.
Monash University is registered as a Class B network, so we have a 16 bit network address with 16 bits left to identify each machine.
All networking code for all languages such as Java, Perl, Python, etc is built on low-level code in C. See e.g. Stevens "Unix Network Programming" or Client Server Computing - sockets
The Java class InetAddress
can be used to get/manipulate
internet addresses. Methods include
static InetAddress getByName(String host)
static InetAddress getLocalHost()
String getHostAddress(); // in dotted form
String getHostName();
Example
import java.net.InetAddress;
import java.net.UnknownHostException;
public class GetInetInfo{
public static void main(String[] args){
if (args.length != 1) {
System.err.println("Usage: GetInetInfo address");
// System.exit(1);
return;
}
InetAddress address = null;
try {
address = InetAddress.getByName(args[0]);
} catch(UnknownHostException e) {
e.printStackTrace();
// System.exit(2);
return;
}
System.out.println("Host name: " + address.getHostName());
System.out.println("Host address: " + address.getHostAddress());
// System.exit(0);
return;
}
} // GetInetInfo
Certain of these ports are "well known". On Unix, they are listed in the file /etc/services. For example,
There is a C function to lookup service ports from their name, but not a Java one.
If you are a server, you need to be able to create a port and listen at it. When a message comes in you need to be able to read and write to it.
The Socket
and ServerSocket
are the Java client
and server classes to do this.
It then "listens" on this socket to "accept" any incoming messages.
The other process (client) establishes a network connection to it, and then the two exchange messages.
As many messages as needed may be sent along this channel, in either direction.
The client typically creates a Socket, gets I/O streams from it, and then does consecutive writes and reads.
import java.io.*;
import java.net.*;
public class Client{
public static final int DAYTIMEPORT = 8189;
public static void main(String[] args){
if (args.length != 1) {
System.err.println("Usage: Client address");
// System.exit(1);
return;
}
InetAddress address = null;
try {
address = InetAddress.getByName(args[0]);
} catch(UnknownHostException e) {
e.printStackTrace();
// System.exit(2);
return;
}
Socket sock = null;
try {
sock = new Socket(address, DAYTIMEPORT);
} catch(IOException e) {
e.printStackTrace();
// System.exit(3);
return;
}
InputStream in = null;
try {
in = sock.getInputStream();
} catch(IOException e) {
e.printStackTrace();
// System.exit(5);
return;
}
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = null;
try {
line = reader.readLine();
} catch(IOException e) {
e.printStackTrace();
// System.exit(6);
return;
}
System.out.println(line);
// System.exit(0);
return;
}
} // Client
A server uses a ServerSocket
to bind to a port and
listen on it. When a new client request is accepted, it returns
a Socket
which is connected to the client. The server
uses this to talk to the client, typically reading requests and
responding to them
The following code is incomplete and won't compile, since it doesn't
handle exceptions
import java.io.*;
import java.net.*;
public class EchoServer {
public static int MYECHOPORT = 8189;
public static void main(String argv[]) {
ServerSocket s = new ServerSocket(MYECHOPORT);
while (true) {
Socket incoming = s.accept();
handleSocket(incoming);
incoming.close();
}
}
public static void handleSocket(Socket incoming) {
BufferedReader reader =
new BufferedReader(new InputStreamReader(
incoming.getInputStream()));
PrintStream out =
new PrintStream(incoming.getOutputStream());
out.println("Hello. Enter BYE to exit");
boolean done = false;
while ( ! done) {
String str = reader.readLine();
if (str == null)
done = true;
else {
out.println("Echo: " + str);
if (str.trim().equals("BYE"))
done = true;
}
}
}
}
IO exceptions are thrown by lots of statements in the program above
new ServerSocket()
s.accept()
incoming.close()
handleSocket()
public class EchoServer {
public static int MYECHOPORT = 8189;
public static void main(String argv[]) {
// bad code follows
try {
ServerSocket s = new ServerSocket(MYECHOPORT);
while (true) {
Socket incoming = s.accept();
handleSocket(incoming);
incoming.close();
}
} catch(IOException e) {
// ignore
}
}
Each exception must be examined to see what effect it has on the application
accept()
fails, then something has gone wrong
with a new client. Nothing more can be done with that client,
but the next one might be okay. So the next iteration of the loop
should be done
handleSocket
on any failure
A safer server is
import java.io.*;
import java.net.*;
public class EchoServer {
public static int MYECHOPORT = 2000;
//public static String CR_LF = "\r\n";
public static void main(String argv[]) {
ServerSocket s = null;
try {
s = new ServerSocket(MYECHOPORT);
} catch(IOException e) {
System.out.println(e);
System.exit(1);
}
while (true) {
Socket incoming = null;
try {
incoming = s.accept();
System.out.println("Connected");
} catch(IOException e) {
System.out.println(e);
continue;
}
handleSocket(incoming);
}
}
public static void handleSocket(Socket incoming) {
byte[] data = new byte[1024];
InputStream in;
OutputStream out;
try {
in = incoming.getInputStream();
out = incoming.getOutputStream();
} catch(IOException e) {
System.err.println(e.toString());
return;
}
int numRead = 0;
try {
while (true) {
try {
numRead = in.read(data);
} catch(IOException e) {
System.err.println(e.toString());
return;
}
if (numRead == -1) {
System.out.println("Disconnecting");
return;
}
try {
out.write(data, 0, numRead);
} catch(IOException e) {
System.err.println(e.toString());
return;
}
}
} finally {
System.out.println("Closing all");
try {
in.close();
out.close();
incoming.close();
} catch(IOException e) {
// nothing to do really
}
}
}
}
The server above will only handle one client at a time. To handle many clients at once, the server should use threads
import java.io.*;
import java.net.*;
public class MultiEchoServer {
public static int MYECHOPORT = 2000;
public static void main(String argv[]) {
ServerSocket s = null;
try {
s = new ServerSocket(MYECHOPORT);
} catch(IOException e) {
System.out.println(e);
System.exit(1);
}
System.out.println("Listening");
while (true) {
Socket incoming = null;
try {
incoming = s.accept();
System.out.println("Connected");
} catch(IOException e) {
System.out.println(e);
continue;
}
new SocketHandler(incoming).start();
}
}
}
class SocketHandler extends Thread {
Socket incoming;
SocketHandler(Socket incoming) {
this.incoming = incoming;
}
public void run() {
byte[] data = new byte[1024];
InputStream in;
OutputStream out;
try {
in = incoming.getInputStream();
out = incoming.getOutputStream();
} catch(IOException e) {
System.err.println(e.toString());
return;
}
int numRead = 0;
try {
while (true) {
try {
numRead = in.read(data);
} catch(IOException e) {
System.err.println(e.toString());
return;
}
if (numRead == -1) {
System.out.println("Disconnecting");
return;
}
try {
out.write(data, 0, numRead);
} catch(IOException e) {
System.err.println(e.toString());
return;
}
}
} finally {
System.out.println("Closing all");
try {
in.close();
out.close();
incoming.close();
} catch(IOException e) {
// nothing to do really
}
}
}
}
The server may wish to timeout a client if it does not respond quickly
enough i.e. does not write a request to the server in time. This should
be a long period (several minutes), because the user may be taking their
time. Conversely, the client may want to timeout the server (after
a much shorter time). Both do this by
before any reads on the socket. A timeout on a
try {
incoming.setSoTimeout(10000); // 10 seconds
} catch(SocketException e) {
e.printStackTrace();
}
read()
will then throw a InterruptedIOException
(a subclass
of IOException
.
A client may wish to stay connected to a server even if it has nothing to send.
It can setKeepAlive(true)
on the socket.
DatagramSocket
. There is no state maintained by these
messages, unless the client or server does so. The messages are not
guaranteed to arrive, or may arrive out of order.
/**
* UDPTimeClient.java
*
*
* Created: Sun Jul 22 19:21:13 2001
*
* @author <a href="mailto: "Jan Newmarch</a>
* @version
*/
import java.io.*;
import java.net.*;
public class UDPTimeClient{
public static final int DAYTIMEPORT = 9013;
public static final int DGRAM_BUF_LEN = 512;
public static void main(String[] args){
if (args.length != 1) {
System.err.println("Usage: Client address");
// System.exit(1);
return;
}
InetAddress address = null;
try {
address = InetAddress.getByName(args[0]);
} catch(UnknownHostException e) {
e.printStackTrace();
// System.exit(2);
return;
}
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
} catch (SocketException e) {
e.printStackTrace();
// System.exit(3);
return;
}
try {
socket.connect(address, DAYTIMEPORT);
byte[] buf = new byte[DGRAM_BUF_LEN];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.send(packet);
socket.receive(packet);
byte[] data = packet.getData();
String date = new String(data);
System.out.println(date);
} catch(IOException e) {
e.printStackTrace();
// System.exit(4);
return;
}
// System.exit(0);
return;
}
} // UDPTimeClient
/**
* UDPTimeClient.java
*
*
* Created: Sun Jul 22 19:21:13 2001
*
* @author <a href="mailto: "Jan Newmarch</a>
* @version
*/
import java.io.*;
import java.net.*;
import java.util.Date;
public class UDPTimeServer {
public static final int DAYTIMEPORT = 9013;
public static final int DGRAM_BUF_LEN = 512;
public static void main(String[] args){
DatagramSocket socket = null;
try {
socket = new DatagramSocket(DAYTIMEPORT);
} catch (SocketException e) {
e.printStackTrace();
System.exit(3);
}
while (true) {
try {
byte[] buf = new byte[DGRAM_BUF_LEN];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
String date = new Date().toString();
buf = date.getBytes();
// get client info
InetAddress clientAddr = packet.getAddress();
int port = packet.getPort();
// prepare packet for return to client
packet = new DatagramPacket(buf, buf.length, clientAddr, port);
socket.send(packet);
} catch(IOException e) {
e.printStackTrace();
}
}
}
} // UDPTimeServer
In C, the select() call lets the kernel do this work. The call takes a number of file descriptors. The process is suspended. When I/O is ready on one of these, a wakeup is done, and the process can continue. This is cheaper than busy polling. In Java, accomplish the same by using a different Thread for each port. A thread will become runnable when the lower-level select() discovers that I/O is ready for this thread.
A multicast packet is broadcast to all possible clients, and may be received by any/all of them. A multicast packet is not restricted to just one host destination. This is useful for e.g. multi-player games, or networks where you know a service is available but don't know its address.
IP addresses in the range 224.0.0.0 to 239.255.255.255 (inclusive)
are multicast addresses.
The address 224.0.0.0 is reserved and should not be used.
If you ping
224.0.0.1, all multicast-enabled hosts
should answer.
Each of these IP addresses has the full range of 65k ports.
"Ordinary" TCP and UDP addresses use a field TTL (time to live) to control the number of "hops" they are allowed to make. For example, a hop count of one will restrict them to the local network. Multicast packets have the potential to flood the network. Most network administrators will restrict the range of multicast packets. The TTL is used for this as a hack measure. There is no standard, but typically a hopcount of less than 60 will not be allowed past any gateway, and the default hopcount is usually set at 15.
Java uses MulticastSocket
to encapsulate multicast
behaviour. This is a subclass of DatagramSocket
/**
* MulticastClient.java
*
*
* Created: Sun Jul 22 19:21:13 2001
*
* @author <a href="mailto: "Jan Newmarch</a>
* @version
*/
import java.io.*;
import java.net.*;
public class MulticastClient{
public static final String MCAST_ADDR = "230.0.0.1";
public static final int MCAST_PORT = 9013;
public static final int DGRAM_BUF_LEN = 512;
public static void main(String[] args){
String msg = "Hello";
InetAddress group = null;
try {
group = InetAddress.getByName(MCAST_ADDR);
} catch(UnknownHostException e) {
e.printStackTrace();
System.exit(1);
}
try {
MulticastSocket socket = new MulticastSocket(MCAST_PORT);
socket.joinGroup(group);
DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(),
group, MCAST_PORT);
System.out.println("Sending: " + msg);
socket.send(hi);
// get their responses!
while (true) {
byte[] buf = new byte[DGRAM_BUF_LEN];
DatagramPacket recv = new DatagramPacket(buf, buf.length);
socket.receive(recv);
byte[] data = recv.getData();
System.out.println("Received: " + new String(data));
}
} catch(IOException e) {
e.printStackTrace();
System.exit(2);
}
// OK, I'm done talking - leave the group...
// s.leaveGroup(group);
// System.exit(0);
}
} // MulticastClient
/**
* UDPTimeClient.java
*
*
* Created: Sun Jul 22 19:21:13 2001
*
* @author <a href="mailto: "Jan Newmarch</a>
* @version
*/
import java.io.*;
import java.net.*;
import java.util.Date;
public class MulticastServer {
public static final String MCAST_ADDR = "230.0.0.1";
public static final int MCAST_PORT = 9013;
public static final int DGRAM_BUF_LEN = 512;
public static void main(String[] args){
InetAddress group = null;
try {
group = InetAddress.getByName(MCAST_ADDR);
} catch(UnknownHostException e) {
e.printStackTrace();
System.exit(1);
}
MulticastSocket socket = null;
try {
socket = new MulticastSocket(MCAST_PORT);
socket.joinGroup(group);
} catch (IOException e) {
e.printStackTrace();
System.exit(3);
}
while (true) {
try {
byte[] buf = new byte[DGRAM_BUF_LEN];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
System.out.println("Received: " + new String(buf));
String date = new Date().toString();
buf = date.getBytes();
// get client info
InetAddress clientAddr = packet.getAddress();
int port = packet.getPort();
// prepare packet for return to client
packet = new DatagramPacket(buf, buf.length, clientAddr, port);
System.out.println("Sending: " + new String(buf));
socket.send(packet);
} catch(IOException e) {
e.printStackTrace();
}
}
}
} // UDPTimeServer