December, 1996
In the first column, I promised that I'd look for other ports to
Java. I did find a few. The OSF has ports to HP/UX 10.01, DEC UNIX
3.2, Sony NeWs, and AIX 4.1 (for Bull). I don't have any of these
platforms, but I know that DEC signed a license with Sun at the end of
September. Try www.gr.osf.org/java/ javaport/JDK-1.0.1/.
There is also guava (don't laugh) at
ftp://summit.stanford.edu/pub/guavac/guavac.tar.gz.
Guavac is written for GCC 2.7.2 and will (probably) run under
SunOS. There is also a precompiled binary for Linux there. I won't
have time to try this before I leave for Iceland.
Networking Made Easy
The topic for this month is networking. I noticed Hal Pomeranz's
coverage of Perl networking in his column and realized how much Perl
networking looked like C. Not surprisingly, Java doesn't look at all
like C.
For one thing, the Java developers were writing with one protocol in
mind - TCP/IP. This eliminates the need to specify the protocol type
when creating a socket. Also, Java provides input and output streams
that make reading and writing, whether from a socket or a file, easy.
On the client side, you must create a socket, which requires the
hostname and port addresses. The hostname must be a string and can
also be the dotted decimal notation quoted to turn it into a
string. Creating the socket will succeed if the host is reachable and
has a server running on the given port.
Once the port is open, the client can get the socket's InputStream for
reading or its OutputStream for writing. These streams permit reading
one or more bytes of data. But wrapping these streams in other Input
or OutputStream classes makes life easier.
import java.io.*;
import java.net.*;
public class DateClient {
public static final int PORT = 13;
// the daytime port
public static void main(String[] args) {
Socket s = null;
String line = null;
try {
// Create a socket
s = new Socket("localhost", PORT);
// Create stream for reading this socket.
DataInputStream sin = new
DataInputStream(s.getInputStream());
line = sin.readLine();
if (line != null) System.out.println(line);
}
catch (IOException e) {System.err.println(e);}
// Always be sure to close the socket
finally {
try ( if (s != null) s.close(); }
catch (IOException e2) ;
}
}
}
This example, DateClient.java, creates a
socket connected to the daytime port on the localhost. It could be
fancier by accepting a command line argument for the hostname, but
this example keeps things simple. If creating the socket is
successful, you get its InputStream, and use it as the argument for
creating a new DataInputStream. The advantage here is that you can
read a line at a time from the DataInputStream as a string, then print
it to the standard output, which is referenced by the System.out class
variable. Being good citizens, we close the socket when we are
finished.
The try and catch statements handle Exceptions, the Java (and C++) way
of receiving error notification. If any statement within the curly
braces following the try generates an IOException, the catch clause is
executed immediately. The finally clause always gets executed, whether
the catch clause does or not. The Java compiler enforces catching
exceptions, so you will be reminded if you call a method that may
throw an exception and you have forgotten to catch it.
UNIX systems have a daytime server built into the inetd program. On
most systems, the daytime server will not be disabled. Other internal
servers, such as chargen (the character generator) and echo (pretty
obvious) have been disabled on many UNIX systems because they can be
abused by a denial of service attack.
If you don't have a daytime server, you can write your own in Java
with a little effort. The server has more work to do than the
client. You create a ServerSocket, and start a Thread that listens
(using accept()) for connections. On a more complicated
server, each connection would run in its own Thread. But all we want
to do is return the date, which can be done quickly without a separate
Thread.
import java.io.*;
import java.net.*;
import java.util.Date;
public class DateServer extends Thread {
public final static int PORT = 13;
protected ServerSocket listen_socket;
// Create a ServerSocket to listen to;
public DateServer() {
try { listen_socket = new ServerSocket(PORT); }
catch (IOException e) {
System.err.println("Exception creating server"
+ "socket: " + e);
System.exit(1);
}
// fire up the Thread's run() method
this.start();
}
In the constructor for the DateServer class, we create a new
ServerSocket, catching any IOExceptions and exiting if they occur
after printing an error message. The Thread is started with
this.start() , passing execution to the Thread's
run() method.
public void run() {
PrintStream out;
try {
while(true) {
Socket client_socket = listen_socket.accept();
try { out = new PrintStream(client_socket.getOutputStream());
}
catch (IOException e) {
try client_socket.close(); catch (IOException e2) ;
System.err.println("Exception while getting"
+ " socket streams: " + e);
return;
}
out.println(new Date());
client_socket.close(); }
//End of while(true) loop
} catch (IOException e) {
// Any IOException in big loop
System.err.println("Exception while listening"
+ " for connections: " + e);
System.exit(2);
}
}
The run() method of the DateServer does the grunt
work. The while loop blocks at the accept( )
method until a client connects. Then we get the OutputStream and wrap
it in a PrintStream while checking for IOExceptions. Notice that the
later catch could be used, but here we are sending a different error
notation. Once the PrintStream is successfully created, we simply use
the println() method to send the Date, after it has been
converted to a string automatically by println() calling
the toString() method for us. Again, we close the socket,
return to the beginning of the loop, and block again listening.
A server that would take more than a few seconds to carry out its work
should create another object, which has its own Thread to handle its
conversation with the client.
// Start the server up, listening on an optionally specified port
public static void main(String[] args) {
new DateServer();
}
}
The end of the DateServer class is a simple main() method that
calls the DateServer() constructor and gets the ball
rolling. Running this class will fail if you already have a daytime
server. If you still want to experiment with it, change the PORT
variable in both the client and the server.
Another Way
Like Perl, there are many ways to do things. Perl excels in string
handling and output formatting, things that are rather weak in
Java. Java has yet another way to handle networking. You can create
URL objects and use them to talk to servers. But that's a topic for
another day.
First published in ;login:, Volume 21, No. 6, December 1996.
|