Sending Objects Across the Network Using Sockets

   

You saw earlier how easy it is to create a client Socket and a ServerSocket and then send data between the two using sockets. What about sending objects between the two using sockets? Can it be done? Fortunately, it can, and it's almost as easy as sending primitive data. There is one thing that hasn't been discussed yet and that's an application rotocol.

An application protocol is the understood messaging structure that goes on between the client and server sides. From the server side standpoint, the client can send all the data that it wants, but unless the server knows what to do with it ahead of time, the act is pretty much meaningless. There has to be an agreed upon structure in the communication. This is not referring to the underlying communication protocol, but a syntax protocol between the client and server. For example, suppose you have an application that allows two clients to connect to a server and play a game of Tic Tac Toe against each other. Someone needs to go first, both sides need to know which one is going to go first, and then each side has to understand when it's their turn . The server and client need to agree on the message that each one will send to convey certain pieces of information. Consider when a client sends a move to the server. The server needs to be able to determine which move the client made or the entire game will not function correctly and no one will want to play it. That's the level of communication that the server and client sides need.

Take a look at how you might communicate for the game Tic Tac Toe. The entire game isn't built in this example, instead you'll see a small section of the game: sending a move to the server. First, look at the information that needs to be sent to the server to indicate the client's move. Following is the board that will be used. The numbers along the side and top indicate the row and column, respectively.

Row/Column

_0 __1__2_

_1_______

2

Here's the information that the client will send to the server to indicate the client's next move:

 clientId: int  Just an id representing this client row: int  Which row this move is selecting column: - Which column this move is selecting 

You can look at the board differently and come up with a different application protocol. Look at this board design to see the difference:

_0 __1__2_

_3__4__5_

6 7 8

This difference in the two boards indicates a difference in application design and also in the application protocol that was mentioned earlier. There are many different ways an application protocol can be designed. You must use good object-oriented analysis and design in selecting which one is best suited for the particular application.

For the example here, the second of the two boards is used. You might have a class that represents the board, TicTacToeBoard. Listing 23.4 shows what the class might look like.

Listing 23.4 TicTacToeBoard.java
 public class TicTacToeBoard {   private String[] board = new String[9];   public TicTacToeBoard()   {     super();   }   public boolean isWinning()   {     // The algorithm here to see if any combination is a winner   }   public TicTacToeMoveResult addNewMove(TicTacToeMove nextMove)   {     // validate the move for the client and return a result   } } 

This is a simple representation of what the board class might actually need to be, but it's a good start. You can use the following information to indicate the client's next move:

 clientId: int  Just an id representing this client index: int  The index into the String array 

You need to design an object that represents the client's next move. You might be wondering why an object is needed when the information about the next move is only a few pieces of data. In some cases, that might be the correct thinking, but you must also consider whether there might be any other pieces of information sent later. This is where an understanding of the application and analysis and design techniques comes into play. For now, let's assume that you have decided to use an object to represent the move. Listing 23.5 shows what the object might look like.

Listing 23.5 TicTacToeMove.java
 public class TicTacToeMove implements java.io.Serializable {   String playerId = null;   int index;   public TicTacToeMove(String id, int moveIndex)   {     super();     playerId = id;     index = moveIndex;   }   public String getPlayerId()   {     return playerId;   }   public int getIndex()   {     return index;   } } 

Objects that are going to be marshaled (sent) across the network must implement java.io. Serializable interface. Refer to chapter 22, "Object Serialization," for a complete description of Object Serialization.

So now that you have identified the object that the client will send to indicate its move, what happens if the client sends an invalid move? Suppose the opponent is already positioned in the square that the client requested in the move. The server, which is also acting as the umpire, must inform the client to select a different move. The server could return a String telling the client that it's invalid. But where would you keep the list of valid Strings that can be returned for a particular situation? The problem with this approach is that you end up with a bunch of so-called magic values all over the code that really don't have any meaning to the problem you are trying to solve. This is why an application protocol is very important. You must decide ahead of time what messages and information the client and the server will send back and forth in all the possible scenarios.

Ideally, what you need every TicTacToeMove object that is sent to the server to return a TicTacToeMoveResult object. Listing 23.6 shows what the board class might look like.

Listing 23.6 TicTacToeBoard.java
 public class TicTacToeMoveResult {   TicTacToeBoard board = null;   boolean validMove;   boolean winningMove;   // Default Constructor   public TicTacToeMoveResult()   {     super();   }   public TicTacToeBoard getBoard()   {     return board;   }   public boolean wasValidMove()   {     return validMove;   }   public boolean wasWinningMove()   {     return winningMove;   } } 

Notice that an instance called TicTacToeBoard is referenced in the TicTacToeMoveResult class. Every time a client sends a move, the client would be expecting an instance of this class to see what the latest board looks like. That establishes a protocol. The protocol is to make a move, get a move result object back. This way the client can figure out if your move was valid and a winning move. Obviously the opponent would also have to get some type of notice of the client's move. This object could be modified slightly to accommodate both clients.

Obviously, plenty of details have been omitted about building a complete game here, but the idea is still valid. You can't have a client and server start sending data back and forth and expect each side to know exactly what to do with it or even really what sort of data to expect. This is where the protocol is absolutely necessary. Spending the time up front during design will pay off big during the construction phase.

Sending Objects Using Sockets

In chapter 22, you learned about object serialization. In that chapter, you saw how you could serialize objects to streams and then de-serialize the objects later. What if you wanted to send complete objects over the network, rather than just primitive data or strings? This can be done using the Socket classes already covered and the ObjectInputStream and ObjectOutputStream classes as shown in the following example. The following example which is contained in Listing 23.7 and 23.8 is similar to the echo server that was shown in the previous example; except this echo server takes an object that both the client and the server are familiar with. The client sends an EchoObject (see Listing 23.9) and the server prints out some information from that object.

Note

The object that is being sent across the network must implement the java.io.Serializable interface, as discussed in Chapter 22.


Listing 23.7 Source Code for EchoObjectServer.java
 import java.net.*; import java.io.*; public class EchoObjectServer {   public static final int PORT = 7;   // Default Constructor   public EchoObjectServer()   {     super();   }   public void startEcho()   {     Socket clientSocket = null;     try     {        // Create a Server Socket on the        ServerSocket server = new ServerSocket(PORT);        // Wait for a single client to connect     System.out.println("Waiting on a client to connect");      clientSocket = server.accept();     System.out.println("Client requested a connection to the echo object server");     ObjectInputStream input =         new ObjectInputStream(clientSocket.getInputStream());     PrintWriter output =         new PrintWriter(clientSocket.getOutputStream(), true);     output.println("Echo Object Server received an echo object");     Object obj = input.readObject();     if (obj instanceof EchoObject)     {       EchoObject echoObj = (EchoObject)obj;       StringBuffer buf = new StringBuffer("Client Name: ");       buf.append(echoObj.getClientName());       buf.append(" sent msg: ");       buf.append(echoObj.getMsg());       System.out.println(buf.toString());      }      else      {       System.out.println("Unknown data sent");      }      System.out.println("echo server exiting");      clientSocket.close();    }    catch(ClassNotFoundException ex)    {      System.out.println("Unknown object type sent");      System.exit(-1);    }    catch(IOException ex)    {      System.err.println("Failed I/O: " + ex);      System.exit(-1);    } }    public static void main(String[] args)    {      EchoObjectServer echoServer = new EchoObjectServer();      echoServer.startEcho();    }  } 
Listing 23.8 Source Code for EchoObjectClient.java
 import java.net.*; import java.io.*; public class EchoObjectClient {   public static final String HOST = "localhost";   public static final int PORT = 7;   // Default Constructor   public EchoObjectClient()   {     super();   }   public void testEcho()   {     try     {       // Get connected to an echo server somewhere, they are usually on       // port 7 of a well known server       Socket socket = new Socket(HOST, PORT);       // Create a reader and stream for reading the data from the echo server       BufferedReader input =            new BufferedReader(new InputStreamReader(socket.getInputStream()));       // Create a writer and stream for writing to the echo server       ObjectOutputStream output =         new ObjectOutputStream(socket.getOutputStream());       // Send some text to the echo server       EchoObject obj = new EchoObject("CLIENT 1", "Hello there server");       output.writeObject(obj);       // Get the message from the echo server       System.out.println(input.readLine() + " from the echo object server");       // Close the socket       socket.close();     }     catch(UnknownHostException ex)     {       System.err.println("Unknown host: " + ex);       System.exit(1);     }     catch(IOException excpt)     {       System.err.println("Failed I/O: " + excpt);       System.exit(1);     }   }   public static void main(String[] args)   {     EchoObjectClient example = new EchoObjectClient();     example.testEcho();   } } 
Listing 23.9 Source Code for EchoObject.java
 public class EchoObject implements java.io.Serializable {   private String clientName = null;   private String msg = null;   // Default Constructor   public EchoObject(String client, String data)   {     super();     clientName = client;     msg = data;   }   // Public Accessor for Client Name   public String getClientName()   {     return clientName;   }   // Public Accessor for Msg   public String getMsg()   {     return msg;   } } 

Listing 23.10 shows the output from the EchoObjectServer program that received an EchoObject from the client.

Listing 23.10 Output from EchoObjectServer
 C:\jdk1.3se_book\classes>java EchoObjectServer Waiting on a client to connect Client requested a connection to the echo object server Echo Object Server received an echo object Client Name: CLIENT 1 sent msg: Hello there server echo server exiting C:\jdk1.3se_book\classes> 
   


Special Edition Using Java 2 Standard Edition
Special Edition Using Java 2, Standard Edition (Special Edition Using...)
ISBN: 0789724685
EAN: 2147483647
Year: 1999
Pages: 353

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net