Corrections to Chapter 9 of Java Network Programming, UDP Datagrams and Sockets

p. 236, paragraph 3: "If the buffer length is not specified, 512 bytes is used." should be "If the buffer length is not specified 65,507 bytes is used."

p. 226, Example 9-7: In Java 1.1 the reuse of the same DatagramPacket object dp causes the UDPDiscardServer to act unexpectedly on at least some platforms. (Exact details appear to be dependent on native code.) dp is initialized to a length of 65,507 bytes but the DatagramSocket.receive() method resets this the length to reflect the number of data bytes received. When dp is reused, its length has been reset to a new and potentially smaller value. This seems to be something that changed from 1.0 to 1.1. However, the example can be written in a fashion that supports all cases in both Java 1.0 and 1.1 by moving the line that constructs the DatagramPacket into the while loop.

import java.net.*;
import java.io.*;

public class UDPDiscardServer {

  public final static int discardPort = 9;
  static byte[] buffer = new byte[65507];

  public static void main(String[] args) {

    int port;

    try {
      port = Integer.parseInt(args[0]);
    }
    catch (Exception e) {
      port = discardPort;
    }

    try {
      DatagramSocket ds = new DatagramSocket(port);
      while (true) {
        try {
          DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
          ds.receive(dp);
          String s = new String(dp.getData(), 0, 0, dp.getLength());
          System.out.println(dp.getAddress() + " at port " + dp.getPort() + " says " + s);
        }
        catch (IOException e) {
          System.err.println(e);
        }      
       } // end while
    }  // end try
    catch (SocketException se) {
      System.err.println(se);
    }  // end catch

  }  // end main

}
p. 237, Example 9-12: Same as above. In Java 1.1 the reuse of the same DatagramPacket object dp causes the UDPDiscardServer to act unexpectedly on at least some platforms. (Exact details appeuar to be dependent on native code.) dp is initialized to a length of 65,507 bytes but the DatagramSocket.receive() method resets this the length to reflect the number of data bytes received. When dp is reused, its length has been reset to a new and potentially smaller value. This seems to be something that changed from 1.0 to 1.1. However, the example can be written in a fashion that supports all cases in both Java 1.0 and 1.1 by moving the line that constructs the DatagramPacket into the while loop.
import java.net.*;
import java.io.*;

public class UDPServer {

  protected static int defaultPort = 0;
  protected static int defaultBufferLength = 65507;

  public static void main(String[] args) {
  
    DatagramPacket incoming;

    int port;
    int len;
    
    try {
      port = Integer.parseInt(args[0]);
    }
    catch (Exception e) {
      port = defaultPort;
    }
    try {
      len = Integer.parseInt(args[1]);
    }
    catch (Exception e) {
      len = defaultBufferLength;
    }

    try {
      DatagramSocket ds = new DatagramSocket(port);
      byte[] buffer = new byte[len];
      while (true) {
        incoming = new DatagramPacket(buffer, buffer.length);
        try {
          ds.receive(incoming);
          respond(ds, incoming);
        }
        catch (IOException e) {
          System.err.println(e);
        }      
      } // end while
    }  // end try
    catch (SocketException se) {
      System.err.println(se);
    }  // end catch

  }  // end main
  
  public static void respond(DatagramSocket ds, DatagramPacket dp) {
    ;
  }

}
pp. 233-239: The examples used here depend on overriding protected static fields in subclasses. In fact the fields are not overridden but rather shadowed. Also, Example 9-12 on p. 237 has the same problem noted above where DatagramSocket.receive() resets the length of the DatagramPacket object. Consequently it's necessary to rewrite pretty much all the examples to account for these problems as follows.

Simple UDP Clients

Several Internet services only need to know the client’s address and port; they discard any data the client sends in its datagrams. Daytime, quote of the day, and chargen are three such protocols. Each of these responds the same way, regardless of the data contained in the datagram, or indeed regardless of whether there actually is any data in the datagram. Clients for these protocols simply send a UDP datagram to the server, and read the response that comes back. Therefore, let’s begin with a simple client that sends an empty UDP packet to a specified host and port; we will design this class so that it can be subclassed to provide specific clients for different protocols.

The UDPPoke class has two fields, both of which are protected so their values may be changed by subclasses. The defaultPort field holds the port to which the client sends data, if no other port is specified. In this class it’s set to 0. Subclasses change this field to the default port appropriate for their protocol. bufferLength is the length of the buffer needed for incoming data. An 8192 byte buffer is large enough for most of the protocols that UDPPoke is useful for, but it can be increased in a subclass or set from the command line if necessary.

The main() method is invoked from the command line with the hostname, the port, and the buffer length passed as command line arguments. If the hostname is not included, the localhost is used. If the port is not included, the defaultPort is used. If the buffer length is not specified, 8192 bytes is used. For example, to send a packet to the daytime server on port 13 of metalab.unc.edu you would type:

% java UDPPoke metalab.unc.edu 13
Sat May 18 15:56:36 1996

Once the port and host are known, a new DatagramSocket object ds is created on an anonymous local port. We use the hostname to construct an InetAddress, which we then use to create a new DatagramPacket called outgoing aimed at the host and port specified on the command line. Although in theory, you should be able to send a datagram with no data at all, bugs in some Java implementations require that you add at least one byte of data to the datagram. The simple servers we’re currently considering ignore this data.

Next we create a new DatagramPacket with a 8192 byte buffer for the returning datagram. An 8192 byte buffer is large enough for most protocols. However, you can override it in a subclass if necessary. The DatagramSocket ds sends the outgoing packet and then waits to receive the response. When the response is received, UDPPoke converts it into a String and prints it on System.out.

Example 9-10 The UDPPoke class

import java.net.*;
import java.io.*;

public class UDPPoke {

  protected static int defaultPort = 0;
  protected static int bufferLength = 8192;

  public static void main(String[] args) {

    String hostname;
    int port;
    int len;

    if (args.length > 0) {
      hostname = args[0];
    }
    else {
      hostname = "localhost";
      port = defaultPort;
      len = bufferLength;
    }
    try {
      port = Integer.parseInt(args[1]);
    }
    catch (Exception e) {
      port = defaultPort;
    }
    try {
      len = Integer.parseInt(args[2]);
    }
    catch (Exception e) {
      len = bufferLength;
    }

    try {
      DatagramSocket ds = new DatagramSocket(0);
      InetAddress ia = InetAddress.getByName(hostname);
      DatagramPacket outgoing = new DatagramPacket(new byte[512], 1, ia, port);
      DatagramPacket incoming = new DatagramPacket(new byte[len], len);
      ds.send(outgoing);
      ds.receive(incoming);
      System.out.println(new String(incoming.getData(), 0, 0, incoming.getLength()));
    }  // end try
    catch (UnknownHostException e) {
      System.err.println(e);
    }  // end catch
    catch (SocketException e) {
      System.err.println(e);
    }  // end catch
    catch (IOException e) {
      System.err.println(e);
    }  // end catch

  }  // end main

}

A daytime client is a trivial extension of UDPPoke; all you need to do is change the default port, as in Example 9-11. chargen and quote of the day are equally trivial, and are left as exercises for the reader.

Example 9-11 The UDP daytime client

public class UDPdaytime extends UDPPoke {

 static { defaultPort = 13; }

}

Simple UDP Servers

Clients aren’t the only programs that benefit from an object-oriented implementation. The servers for these protocols are also very similar. They all wait for UDP datagrams on a specified port, and reply to each datagram with another datagram. The servers differ only in the content of the datagram that they return. Example 9-12 is a UDPServer class. It will be subclassed to provide specific servers for different protocols. The only reason it isn’t implemented as an abstract class is the difficulty of combining both static and abstract methods in one class. (In brief, a static method needs to instantiate a class before it can call a method in that class; but an abstract class can’t be instantiated.)

The UDPServer class has two fields, defaultPort and defaultBufferLength, which are protected so they can be accessed by subclasses. The main() method is invoked from the command line, with the port and the buffer length passed as command line arguments. If the port is not included, the defaultPort is used (though this will be of more use for subclasses). If the buffer length is not specified, 512 bytes is used. Once the port and buffer length are known, UDPServer creates a new DatagramSocket ds on the specified port. Then UDPServer enters an infinite loop in which the DatagramSocket receives packets. When UDPServer receives a packet, both the packet and the DatagramSocket ds are passed to the respond() method.

The respond() method sends the response to the originating host, using the DatagramSocket ds. In this class, the respond() method does nothing. It should be overridden in subclasses that implement particular protocols. It would not be a bad idea to make respond() an abstract method, except that methods may not be both abstract and static.

UDPServer is a very flexible class. Subclasses can send zero, one or many datagrams in response to each incoming datagram. If a lot of processing is required to respond to a packet, the respond() method can spawn a thread to do it. However, UDP servers tend not to have extended interactions with a client. Each incoming packet is treated independently of other packets, so the response can usually be handled directly in the respond() method without spawning a thread.

Example 9-12 The UDPServer class

import java.net.*;
import java.io.*;

public class UDPServer {

  protected static int defaultPort = 0;
  protected static int defaultBufferLength = 65507;

  public static void main(String[] args) {
  
    DatagramPacket incoming;

    int port;
    int len;
    
    try {
      port = Integer.parseInt(args[0]);
    }
    catch (Exception e) {
      port = defaultPort;
    }
    try {
      len = Integer.parseInt(args[1]);
    }
    catch (Exception e) {
      len = defaultBufferLength;
    }

    try {
      DatagramSocket ds = new DatagramSocket(port);
      byte[] buffer = new byte[len];
      while (true) {
        incoming = new DatagramPacket(buffer, buffer.length);
        try {
          ds.receive(incoming);
          respond(ds, incoming);
        }
        catch (IOException e) {
          System.err.println(e);
        }      
      } // end while
    }  // end try
    catch (SocketException se) {
      System.err.println(se);
    }  // end catch

  }  // end main
  
  public static void respond(DatagramSocket ds, DatagramPacket dp) {
    ;
  }

}

As written, UDPServer is almost a functional discard server. All that needs to be changed is the port. Example 9-13 adds the proper defaultPort for a high-performance UDP discard server that does nothing with incoming packets.

Example 9-13 A High Performance UDP discard server

public class FastUDPDiscardServer extends UDPServer {

  static { defaultPort = 9; }

}

Example 9-14 is a discard server that prints the incoming packets on System.out.

Example 9-14 A UDP discard server

import java.net.*;

public class UDPDiscardServer extends UDPServer {

  static { defaultPort = 9; }

  public static void respond(DatagramSocket ds, DatagramPacket dp) {

    String s = new String(dp.getData(), 0, 0, dp.getLength());
    System.out.println(dp.getAddress() + " at port " + dp.getPort() + " says " + s);
    
  }

}

It isn’t much harder to implement an echo server, as Example 9-15 shows:

Example 9-15 A UDP echo server

import java.net.*;
import java.io.*;

public class UDPEchoServer extends UDPServer {

  static { defaultPort = 7; }

  public static void respond(DatagramSocket ds, DatagramPacket dp) {

    DatagramPacket outgoing;
    
    try {
      outgoing = new DatagramPacket(dp.getData(), dp.getLength(), 
                                    dp.getAddress(), dp.getPort());
      ds.send(outgoing);
    }
    catch (IOException e) {
      System.err.println(e);
    }
    
  }

}

A daytime server is only mildly more complex. The server listens for incoming UDP datagrams on port 13. When a datagram is detected, a response is returned with the date and time at the server in a one-line ASCII string. Example 9-16 demonstrates this.

Example 9-16 The UDP daytime server

import java.net.*;
import java.io.*;
import java.util.Date;

public class UDPDaytimeServer extends UDPServer {

  static { defaultPort = 13; }
 
  public static void respond(DatagramSocket ds, DatagramPacket dp) {

    DatagramPacket outgoing;
    
    Date now = new Date();
    String s = now.toString();
    byte[] data = new byte[s.length()];
    s.getBytes(0, s.length(), data, 0);
    try {
      outgoing = new DatagramPacket(data, data.length, dp.getAddress(), dp.getPort());
      ds.send(outgoing);
    }
    catch (IOException e) {
      System.err.println(e);
    }
    
  }

}
p. 242: Delete the line eot.stop(); in Example 9-18. This means the program won't terminate of its own free will. I'll fix that when I get a minute, but this temporary fix will at least let the program compile and run.

p. 243, Example 9-19

Change

buffer = new byte[65507];

to

byte[] buffer = new byte[65507];


[ Java Network Programming Corrections | Java Network Programming Home Page | Table of Contents | Examples | Order from Amazon ] ]

Copyright 1998, 1999, 2003 Elliotte Rusty Harold
elharo@metalab.unc.edu
Last Modified January 8, 2003