Generic client-server classes
Develop your own applet-to-server protocols by subclassing from a simple client-server package
By Rich Kadel, JavaWorld.com, 09/01/96
The Java API provides a nice set of libraries for initiating and communicating over TCP/IP sockets. I know a number of Unix
programmers (myself included) that had, at one time or another, resorted to writing C or C++ wrappers over the existing BSD
sockets API. The standard Unix API provides a very low-level interface, and requires a significant amount of error checking
and handling. It is also very architecture-specific. Java, with its
java.net package, has removed all of the portability issues, and most of the tedium of network I/O.
However, there is room for improvement. To develop a Java-based server application that listens for client connections and
spawns off threads to handle them, a number of steps must be taken. In the following paragraphs, we will go over each of these
steps, and will touch on several useful techniques including the use of Sockets, Threads, and basic Java data I/O. The result
will be a package that can be used to develop almost any client-server protocol by subclassing the classes in the package.
These classes can then be integrated into an applet, or a standalone application.
The architecture
A typical client-server architecture consists of a server application running on one machine, and one or more clients -- often,
but not always, operating on different machines across a network. The server is modeled as a perpetual process that waits
for clients to connect (request a service), and then processes the client requests. Java threads are ideal to the implementation
of a server application, because a new thread can be started for each connecting client. This prevents one client request
from holding up others if and when it enters a wait state, or some time-consuming task.
The example - RemoteFileInputStream
For the purposes of this article, we are going to be developing a client-server protocol to allow a client to retrieve a file
from the server machine. We will use a familiar API on the client side by mimicking the
java.io.FileInputStream class in creating the new class RemoteFileInputStream. The constructor will take the hostname of the server, the TCP/IP port
number of the service, and the filename to retrieve. The user can then use the methods defined in
java.io.InputStream to read the data from the file.
(Note: Java actually provides a very simple way to read a file from the server, as long as the file is in under the HTTP document
root. Ordinarily you would want to use the openStream() method in java.net.URL. The RemoteFileInputStream example still provides a good example of client-server communication, and it can be molded to
support just about any other protocol.)
The API
Now that we have a good idea about what we are trying to model, we need an API. On the server side, it should start out as
simple as:
new RemoteFileInputServer( portnum );
This could be the only line in the static main() function of a standalone Java application. As we will see later, we will create a subclass of a new class called NetPortToClient,
which is a subclass of java.lang.Thread, to perform the server-side portion of the protocol.