http://www.developer.com/design/article.php/3604491/Objects-and-ClientServer-Connections.htm
This series, The Object-Oriented Thought Process, is intended for someone just learning an object-oriented language and who wants to understand the basic concepts before jumping into the code, or someone who wants to understand the infrastructure behind an object-oriented language he or she is already using. These concepts are part of the foundation that any programmer will need to make the paradigm shift from procedural programming to object-oriented programming.
Click here to start at the beginning of the series. In keeping with the code examples used in the previous articles, Java will be the language used to implement the concepts in code. One of the reasons that I like to use Java is because you can download the Java compiler for personal use at the Sun Microsystems Web site http://java.sun.com/. You can download the standard edition, J2SE 5.0, at http://java.sun.com/j2se/1.5.0/download.jsp to compile and execute these applications. I often reference the Java J2SE 5.0 API documentation and I recommend that you explore the Java API further. Code listings are provided for all examples in this article as well as figures and output (when appropriate). See the first article in this series for detailed descriptions for compiling and running all the code examples. In the previous column, we covered the basic Java technology required to construct a simple client/server example. The example illustrated how an object is constructed on one machine and then marshaled across a network to another machine. The process involves serializing the object so that it can be transferred across a wire and then reconstructed on the other side. In this article, we will further explore the process of moving an object across a client-server connection. Specifically, we will investigate what happens when the unexpected occurs. We will first review the client/server application that we began in the last column, and then we will throw in some wrinkles. First, lets review the code from the last column. The examples in this article extend this code, so it is important that you reach this baseline by compiling and running the simple client server example (if you have not read the previous column or would like to review it, please visit the following URL: http://www.developer.com/db/article.php/3597071). There are 3 files in this example, Employee.java, Server.java and Client.java. The basic concept is this: The complete code for the Employee class is shown in Listing 1. Listing 1: The Employee Object The complete code for the Client class is shown in Listing 2. Listing 2: The Client The complete code for the Server class is shown in Listing 3. Listing 3: The Server I compiled the code using a DOS Shell. On certain machines it is called a DOS Shell and on others it is called a Command Prompt. They and equivalent and I will show examples of both. Eventually we will need two of these DOS Shells, one to run the Server and one for the Client. You can open a DOS Shell in the Programs->Accessories option.
Type the following code at the command prompt to compile all three of the files.
Figure 1 shows the screen shot of how this is accomplished.
Figure 1. Compiling the Code In one of the DOS Shells, type in the following line at the command prompt:
Figure 2 shows what happens in the DOS Shell.
Figure 2 - Starting the Server If everything is working properly, the "Server Waiting" message is displayed. At this point, we can start the Client.
In a separate DOS Shell, start the Client with the following line.
The result is shown in Figure 3.
Figure 3 - Starting the Client If all is well, you will see that the employeeNumber and the employeeName both were changed. You can put some specific identification in the print statements to provide further assurance.
With the circuit complete, the Server should exit cleanly, as shown in Figure 4.
Figure 4 - Completing the System What we have created is a very simple client/server application using the Java programming language. At this point, our server accepts a single transaction from the client and then simply terminates. While this is fine for demonstration purposes, it is not very useful. One of the more interesting exercises that I use in the classroom has multiple clients connecting to the same server. For this to occur, the server obviously must not terminate after serving a single transaction.
At a very basic level, there is a simple code enhancement that will allow the Server to handle multiple clients. In fact, the server can also handle multiple transactions from the same client. For example, by adding a simple while loop, we can provide this behavior. Listing 4 contains this enhancement.
Listing 4: Resetting the Server The two lines in bold represent the code required to implement the loop. Note that several lines of the code must reside within the loop.
The basic concept here is that once the Server handles a single client transaction, it closes the input/output stream and then loops back to wait for the next client transaction. Each time the following line is encountered, the Server simply waits.
If no client is sending transactions, the Server blocks and waits to be notified.
The Sun documentation states the following regarding the ServerSocket accept method: To test the use of multiple clients, simply create more than one client. Make sure that each client uses the IP address of the server and you are ready to test. Figure 5 shows how my system looks when I am running the Server with 2 different clients.
Figure 5 - Multiple Clients One very useful piece of information that the Server can collect is some sort of client ID. We can actually embed this in the Employee object itself. This ID can take the form of a simple ID number or we can inspect any attribute on the object. In this example, rather than use the employeeNumber attribute, let's use the employeeName attribute. This will make it easier for us to read and identify the client more easily.
We have already created an Employee object with the employeeName name of Joe, so let's create one with the employeeName of Mary. We can use the following line of code in the 2nd client application.
On the server side, we can add a line of code to print out the name of the specific Employee object.
Figure 6 shows what happens when we use two clients. The first Client has the name of Joe and the second the name of Mary. You can see the specific information printed in the client Command Prompt. The Server Command Prompt is more interesting. Note that the Server handles the message from both Joe and Mary and then prints their name. This illustrates that the Server can indeed handle multiple clients. It is important to realize how nicely all of this is packaged into the objects and how the objects are marshaled across the network.
Figure 6 - Identifying Multiple Clients At this point, rather than using the loopback IP address ("127.0.0.1"), it is interesting to find another computer on the network and connect to the server from a different machine. There will be no change to the client code except for updating the IP address. To find the specific IP address of the machine, you can use the ipconfig command at the Command Prompt of the Server.
This command will provide the appropriate IP address for the server. Simply change the loopback string, "127.0.0.1", to the IP address of the Server.
Getting this client/server system to work requires that both sides use the same classes. This may seem obvious, but it is easy for the two sides to become out of sync. This has to do with one of the strengths of Java. As is often the case, a specific strength can lead to some potentially significant problems if you are not careful.
For example, let's make a change to the Client. Let's say that we want to add a last name to the Employee object as seen in Listing 5.
Listing 5: The Employee with lastName added. Note the lines in bold. These lines add the functionality of the lastName. However, this obviously changes the Employee class. For example, if the Client compiles on one machine with this change and the Server compiles on another with the old version of Employee, a problem will arise (see Figure 7).
Figure 7 - Incompatible Employee Class The Server reports the following exception:
The Employee class the Server uses is different than the one that the Client uses - and this is not allowed. The fact that this can even happen is because Java dynamically loads all classes - there is no linked executable. This is a tricky configuration management issue. If you update a class you must make sure that all distributions of that class get updated.
In this example, the Employee class was updated on the Client, but not the Server. Since the Server was expecting a specific Employee class, an exception was generated when it received a different one. The interesting thing to look at is the fact that a class is assigned a serialVersionUID. When these two serialVersionUIDs do no match, a problem is identified. This is as much a security issue as it is a programming issue.
The Sun documentation states the following regarding the serialVersionUID: In this article, we expanded upon the basic concepts involved with creating a simple Client/Server model. The code is a complete and functional model.
As we have seen, moving an object from one place to another is often a tricky proposition. In languages such as Java and the .Net languages, while the ability to load objects dynamically is a major strength, we have to deal with the problem of keeping the class versions in sync.
While the example of this article is complete and useful, it is quite basic. There are many more fascinating topics to explore regarding client/server applications.
Matt Weisfeld is a faculty member at Cuyahoga Community College (Tri-C) in Cleveland, Ohio. Matt is a member of the Information Technology department, teaching programming languages such as C++, Java, and C# .NET as well as various web technologies. Prior to joining Tri-C, Matt spent 20 years in the information technology industry gaining experience in software development, project management, business development, corporate training, and part-time teaching. Matt holds an MS in computer science and an MBA in project management. Besides The Object-Oriented Thought Process
Objects and Client/Server Connections
May 8, 2006
Running the Client/Server Example
import java.io.*;
import java.util.*;
public class Employee implements Serializable {
private int employeeNumber;
private String employeeName;
Employee(int num, String name) {
employeeNumber = num;
employeeName= name;
}
public int getEmployeeNumber() {
return employeeNumber ;
}
public void setEmployeeNumber(int num) {
employeeNumber = num;
}
public String getEmployeeName() {
return employeeName ;
}
public void setEmployeeName(String name) {
employeeName = name;
}
}
import java.io.*;
import java.net.*;
public class Client {
public static void main(String[] arg) {
try {
Employee joe = new Employee(150, "Joe");
System.out.println("employeeNumber= " + joe
.getEmployeeNumber());
System.out.println("employeeName= " + joe
.getEmployeeName());
Socket socketConnection = new Socket("127.0.0.1", 11111);
ObjectOutputStream clientOutputStream = new
ObjectOutputStream(socketConnection
.getOutputStream());
ObjectInputStream clientInputStream = new
ObjectInputStream(socketConnection.getInputStream());
clientOutputStream.writeObject(joe);
joe= (Employee)clientInputStream.readObject();
System.out.println("employeeNumber= " + joe
.getEmployeeNumber());
System.out.println("employeeName= " + joe
.getEmployeeName());
clientOutputStream.close();
clientInputStream.close();
} catch (Exception e) {System.out.println(e); }
}
}
import java.io.*;
import java.net.*;
public class Server {
public static void main(String[] arg) {
Employee employee = null;
try {
ServerSocket socketConnection = new ServerSocket(11111);
System.out.println("Server Waiting");
Socket pipe = socketConnection.accept();
ObjectInputStream serverInputStream = new
ObjectInputStream(pipe.getInputStream());
ObjectOutputStream serverOutputStream = new
ObjectOutputStream(pipe.getOutputStream());
employee = (Employee )serverInputStream.readObject();
employee .setEmployeeNumber(256);
employee .setEmployeeName("John");
serverOutputStream.writeObject(employee);
serverInputStream.close();
serverOutputStream.close();
} catch(Exception e) {System.out.println(e);
}
}
}
Compiling and Running the System
Compiling the Code
"C:Program FilesJavajdk1.5.0_06binjavac" Employee.java
"C:Program FilesJavajdk1.5.0_06binjavac" Client.java
"C:Program FilesJavajdk1.5.0_06binjavac" Server.java

Click here for a larger image.
Starting the Server
"C:Program FilesJavajdk1.5.0_06binjava" Server

Click here for a larger image.
Starting the Client
"C:Program FilesJavajdk1.5.0_06binjava" Client


Resetting the Server
Handling Multiple Clients
import java.io.*;
import java.net.*;
public class Server {
public static void main(String[] arg) {
Employee employee = null;
try {
ServerSocket socketConnection = new ServerSocket(11111);
while (true) {
System.out.println("Server Waiting");
Socket pipe = socketConnection.accept();
ObjectInputStream serverInputStream = new
ObjectInputStream(pipe.getInputStream());
ObjectOutputStream serverOutputStream = new
ObjectOutputStream(pipe.getOutputStream());
employee = (Employee )serverInputStream.readObject();
employee .setEmployeeNumber(256);
employee .setEmployeeName("John");
serverOutputStream.writeObject(employee);
serverInputStream.close();
serverOutputStream.close();
} // end of the while loop
} catch(Exception e) {System.out.println(e);
}
}
}
Note: In a later article we will explore some of the performance issues that we can address in a client/server environment.
Socket pipe = socketConnection.accept();
Listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made.
A new Socket s is created and, if there is a security manager, the security manager's checkAccept method is called with s.getInetAddress().getHostAddress() and s.getPort() as its arguments to ensure the operation is allowed. This could result in a SecurityException.

Identifying the Client
Employee mary = new Employee(250, "Mary");
employee = (Employee )serverInputStream.readObject();
System.out.println("Serving " + employee.getEmployeeName());

Click here for a larger image.
Using another Machine on the Network
C:column22cs> ipconfig
Keeping things in Sync
import java.io.*;
import java.util.*;
public class Employee implements Serializable {
private String employeeLastName;
private int employeeNumber;
private String employeeName;
Employee(int num, String name) {
employeeNumber = num;
employeeName= name;
}
public int getEmployeeNumber() {
return employeeNumber ;
}
public void setEmployeeNumber(int num) {
employeeNumber = num;
}
public String getEmployeeName() {
return employeeName ;
}
public void setEmployeeName(String name) {
employeeName = name;
}
public String setEmployeeLastName() {
return employeeName ;
}
public void getEmployeeLastName(String name) {
employeeName = name;
}
}

java.io.InvalidClassException: Employee; local class incompatible: stream classd
esc serialVersionUID = -2555159439224478262, local class serialVersionUID = 1247
525241219380744
The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an InvalidClassException.
Conclusion
References
About the Author
, which is now in it's second edition, Matt has published two other computer books, and more than a dozen articles in magazines and journals such as Dr. Dobb's Journal, The C/C++ Users Journal, Software Development Magazine, Java Report, and the international journal Project Management. Matt has presented at conferences throughout the United States and Canada.