The server code:
The server is implemented by the class NameServer, which has a constructor and several methods, only one of which is public. Most of the structure of the server is in the public method, run, but first we look at the instance variable declarations and the constructor. The start of the class definition is as follows:
public class NameServer
{
private HashMap nameDatabase; private ServerSocket ss; private Socket socket;
// streams for connections private InputStream is; private OutputStream os;
// writer and reader for communication
private PrintWriter toClient;
private BufferedReader fromClient;
// use a high numbered non-dedicated port
static final int PORT_NUMBER = 3000;
// protocol definitions
// sent by client:
static final String CLIENT_QUITTING = "Exit";
// sent by server
static final String USER_NOT_FOUND = "User not known";
// constructor
public NameServer ( )
{
nameDatabase = setUpNameDatabase( );
// establish a ServerSocket try
{
ss = new ServerSocket(PORT_NUMBER);
}
catch (Exception e)
{
System.out.println("Trouble with ServerSocket,
port " + PORT_NUMBER + ": " + e);
}
} // end constructor
...
They include variables that reference ServerSocket and Socket objects as well as variables that reference streams for communication with the client. In this case, since the communication is two-way, we have streams for reading input from the client as well as the streams for output that we saw in the previous server.
The constructor does two things only. It sets up the database of information that associates users' names with their email addresses. It also creates a ServerSocket object, to enable the server to listen on the specified port. As before, we have used port
3000, but this has no special significance apart from being outside the reserved range for dedicated ports.
The name database is stored in a HashMap object implements a hash table to store key-value pairs. This will allow the server to quickly look up the email address associated with a given user name. The HashMap object is created by a private helper method, setUpNameDatabase, as follows:
// set up name database and add sample data
private HashMap <String, String> setUpNameDatabase ( )
{
HashMap <String, String> db = new HashMap <String, String>( );
db.put("Gareth Williams", "[email protected]");
db.put("Robert Thomas", "[email protected]");
db.put("William Wilson", "[email protected]");
db.put("Anne Land", "[email protected]");
db.put("Dave Phillips", "[email protected]");
db.put("Kirsten Davis", "[email protected]");
return db;
}
Most of the work for this server is carried out by the run method, with the help of a number of private methods, as follows:
public void run ( )
{
try
{
// loop endlessly waiting for client connections
while (true)
{
// wait for a connection request
socket = ss.accept( );
openStreams( );
processClientRequests( );
closeStreams( );
socket.close( );
}
}
catch (IOException e)
{
System.out.println("Trouble with a connection " + e);
}
}
The basic structure of the server is a continuous loop containing code that causes the server to wait for a client to connect and then to process any requests from that client. When the client has no further requests, the connection is closed and the server returns to wait for any further clients to connect.
When a client succeeds in connecting, the accept method returns a Socket object that allows communication with the client. The helper method openStreams opens the necessary streams for input from and output to the client, and then the method processClientRequests repeatedly processes client requests. When the client sends a message indicating that it wishes to terminate the connection, the streams are closed by the helper method closeStreams and, finally, the connection to this client is closed by invoking the close method of the Socket object. The server then returns to the start of the loop to wait for another client to connect.
This structure is typical for servers, regardless of the precise service being offered. The content of the helper method processClientRequests can be adjusted to suit the particular service that is needed and the protocol for the communication.
Note also the try-catch construct, which handles any IOException objects thrown by code in the run method or any of its helper methods.
We will now look at the code for each of the remaining helper methods, to see in more detail how the server works.
The openStreams and closeStreams methods are similar to the corresponding methods. The main difference is that we need to deal with streams for text input from the client as well as the output streams we saw in the previous server example. The code for openStreams is as follows:
// set up streams for communicating with the client
private void openStreams ( ) throws IOException
{
final boolean AUTO_FLUSH = true;
is = socket.getInputStream( );
fromClient = new BufferedReader(new InputStreamReader(is));
os = socket.getOutputStream( );
toClient = new PrintWriter(os, AUTO_FLUSH);
}
All the variables used here to reference the various streams are instance variables. This is because we will need to use them in a number of other methods of this class.
The next helper method, processClientRequests, carries out the key work of the server - reading client requests and responding to them. The structure is simple and typical of servers: the server repeatedly reads a client request string using the readLine method of the BufferedReader class. In this case, the request is simply the name of a user, and the processing involves looking up this name in the hash table that stores the name database. If the name is found, then the get method of the HashMap object will return a string corresponding to the email address of the user; this string is sent as the reply to the client. If there is no matching name in the database, the get method will return a null reference and the server sends a suitable reply (in this case 'User not known') to the client. The method then waits for further requests from the client and processes them repeatedly until the special request string is read, indicating that the client is closing down. The method then terminates. The code is as follows:
private void processClientRequests ( ) throws IOException
{
String userName; // name request from client
String userEmail;
String reply; // reply sent to client
// get request from client
userName = fromClient.readLine( );
while (!(userName.equals(CLIENT_QUITTING)))
{
userEmail = (String) nameDatabase.get(userName);
if (userEmail == null)
{
reply = USER_NOT_FOUND;
}
else
{
reply = userEmail;
}
// send reply to client toClient.println(reply);
// get next request
userName = fromClient.readLine( );
}
}
Again, note that this method may generate an IOException: for example, if the connection became faulty while trying to input or output messages. Such an exception would be handled in the run method.
When all requests from a particular client have been processed, the closeStreams method closes down the streams between the server and this particular client, as follows:
private void closeStreams ( ) throws IOException
{
toClient.close( ); os.close( ); fromClient.close( ); is.close( );
}
Execution in the main loop of the run method then returns to the accept method, as in the following extract from the method code shown earlier:
...
// loop endlessly waiting for client connections while (true)
{
// wait for a connection request socket = ss.accept( );
...
Again the server waits for a client to connect and then proceeds to open streams, processes requests and eventually closes the connection for this client also. The server continues to run like this indefinitely, as this server has no way of receiving a shutdown command for the server itself. To stop this server you must close down the program, using whatever facilities the environment provides to terminate the programs.
Finally, note the approach that is used for exception handling. Several of the helper methods may generate an IOException; this is declared in the headers of these methods and the exception is handled by a single try-catch construct in the run method. An alternative approach would be to have a try-catch construct within each of the helper methods and to deal with each exception locally within the method where it occurs. This has some potential advantages if we want to take different recovery or reporting actions for the various possible locations of the exception. However, in this simple example we just want to report the exception and terminate the program, so the same action is appropriate for all occurrences of the exception.
The only other thing required is a class containing a main method, which can be used to create and run the NameServer object. A suitable class, called TestNameServer, is as follows:
public class TestNameServer
{
// create a name server to respond to client requests public static void main (String [] args)
{
NameServer server1 = new NameServer( );
server1.run( );
} // end main
}
The main method creates a NameServer object and invokes its run method. This causes the server to set up the name database and then to wait for a suitable client to connect. Next, we shall discuss how we write just such a client.
Java Assignment Help - Java Homework Help
Struggling with java programming language? Are you not finding solution for your The server code homework and assignments? Live The server code experts are working for students by solving their doubts & questions during their course studies and training program. We at Expertsmind.com offer The server code homework help, java assignment help and The server code projects help anytime from anywhere for 24x7 hours. Computer science programming assignments help making life easy for students.
Why Expertsmind for assignment help
- Higher degree holder and experienced experts network
- Punctuality and responsibility of work
- Quality solution with 100% plagiarism free answers
- Time on Delivery
- Privacy of information and details
- Excellence in solving java programming language queries in excels and word format.
- Best tutoring assistance 24x7 hours