Lecture 17

Network Programming in Java

What is the value of unshared information? Not much. The thought that I just had, but
didn't relate to anyone, may die as soon as I forget it. The communication, exchange of
information, is a fundamental factor that made us who we are today. It is by interchange
of ideas that we grow and develop. 
In the early days of computing, when CPU power was scarce, the concept of distributed computing was born.  Not necessarily to share the information, just to speed up computation.
Ever since, computer science was infected and fascinated with issues in distributed computing. How is information transferred? What is the protocol? Is it safe/secure? What
is the capacity of transformation channel? What is the rate of transformation of data?
What are the functions of the participants on different ends? These questions shaped
the field of distributed computing to be a sophisticated and inextricable part of information
technology. Two core ways to pass the information around come to mind - client/server
and peer-to-peer. 

 

As many of you know, the idea behind client/server programming is to have lightweight 
programs sitting on user computers (usually PCs) communicate with powerful servers.
Client programs, when they startup, connect themselves to servers, authenticate and ask for 
some profile-type data. The role of server is store large amounts of information, 
allow the share of this information when appropriate, perform significant computations.
These days you would find several multi tier client/server architectures. The financial
industry is probably still dominated by 3-tier setups.

Another distributed model is peer-to-peer. Unlike client/server, this model assumes that 
participating computers have equal power and capacity. This model, is probably close
to idea of propagating information through self-organizing networks - the once we find
so many in nature. Basically, peer-to-peer network is organized in a graph. There are
no strict rules on how computers should be hookup, except that any computer
should be connected to at least two other computers. 

Peer-to-peer model is natural and powerful. There is no centralized controller. When a node 
needs to obtain certain information which it doesn't have it asks its peers, initiating 
the recursive traversal of the graph. In the stabilized version of such network,
by stabilized here we mean the one that has been used for a while, information is
stored evenly among participating machines.

Regardless of how you decide to organize the network, next question that you need
to address is how your machines will communicate, what is the protocol. 
We live in the world were there are to many different protocols and platforms;
we are now consciously trying to standardize on just a few. However, when we speak 
about network protocols we need to carefully distinguish layers of communication
that we mean. Let us use simplified 4-leyer representation of network communication,
taken from Java Network programming by Elliotte Harold.

The internet layer, IP, defines how bytes should be organized into packets and the addressing scheme
by which machines on the network find each other. The packets of data which are send across are
called datagrams. Datagrams have headers which describe the size its contents, its destination
and origin.

The Transport layer deals with redelivering the data in case if some of it gets lost. It is also 
concerned with order in which packets are received. As most of you know, two primary
protocols here are TCP and UDP.


Now, in the application layer, you may choose to communicate in any way you like.
For example, web browsers and web servers communicate via HTTP. Many financial
applications have their own proprietary message-based communication formats.
Often we see objects being streamed (serialized) over the network or methods 
being invoked on remote objects. 

Java comes with wide variety of facilities for distributed computing which range from
elementary socket support to Enterprise Java Beans. Today we will study Sockets 
by writing simple chat program. We start by considering most primitive
means of networking in Java - Sockets. In Java, you can think of a Socket as a
two way data tunnel. This tunnel has input and output streams

Socket is instantiated with network address and port. You send data by simply writing
to sockets output stream and receive data by reading from sockets input stream.
The class java.netSocket encapsulates the functionality of client socket. However, in 
order for you to connect to remote host you need to have ServerSocket on the other end.
The server socket, binds to the port on host where program is running and blocks
until someone tries to connect to it. As soon as this happens, it wakes up, negotiates
new port where connection will take place, create new socket to represent and support
this communication and then blocks again.

The API's of ServerSocket and Socket are quite simple. People who had to
implement sockets in C would definitely appreciate them.

We now move on to concrete example - chat program. 

 

What does the typical chat program involve?
Well, clients connect to a some defined chat server that allows them send messages
to each other. The server essentially acts like a proxy, it forwards a message only
if client for which it destined is logged in. Typical chat program allows user
to type in message, while receiving messages from other users. This means that
I/O are running in the separate thread. Writing the chat program using plain sockets,
even in Java, is not trivial. You will see that there are several interesting issues and
techniques involved.

Let us first address the communication protocol issues. What is exchanged between
client and server? We definitely know that it should be more than just plain messages.
This is where Java serialization together with command design pattern come handy.
Here is the idea. We construct small command objects which get serialized and shipped
between client and server. Once command arrives to its destination it executes 
which ever code it needs to execute. This is flexible and powerful, but requires that 
both client and server have all command classes. Here is command hierarchy:

Commands travel between client and server. Whenever command arrives 
on its destination it gets executed with the target object. For example, here is 
the code for login command:

Or here is the message command:

So client and server exchange commands which simply execute API methods when
they arrive. 

Next problem that we want to address is the asynchronous nature of I/O in chat programs.
Whenever you are communicating with your friend via some chat, you don't want to
have to wait to type in your sentence while program is receiving response from your friend.
You want the I/O to be asynchronous - so we have to run them in separate threads.
Here is the idea. We will create class RemoteDestination, which will be used both
by server and client, which will encapsulate this functionality.

Here is public interface of RemoteDestination

As we mentioned previously, every remote destination will contain sender and
receiver which will be running in separate thread. Here is the code:

And here is the rest of remote destination, so that you can see how these are being used



Okay, now we are all set to write ChatClient and ChatServer. All that our chat client
should do is just prompt user to login and then let him/her enter messages.



Finally, we need to do ChatServer. The job of the server is to listen on a certain port
and wait for connections and messages from clients. Once connection is attempted,
Server creates an instance of RemoteDestination to represent and manage this connection.
When Login message from this connection arrives server associates given user with
this remote destination. Now this user and others can start exchanging messages.

We conclude with class diagram and link to zip file containing the code for this chat.