Skip to main content.

Web Based Programming Tutorials

Homepage | Forum - Join the forum to discuss anything related to programming! | Programming Resources

Java Unleashed

Java Unleashed--ch35.htm Previous Page TOC Index Next Page

Chapter 35

Multiuser programming

Without doubt, Java is one of the more spectacular products to hit the computer market in recent years. Inasmuch as its initial support came from academia, most initial Java applets have been limited to decorative roles. However, now that Java’s popularity has increased, Java has recently been utilized for more practical purposes. Applets have been used to enhance the speed of search engines and to create interactive environments for such purposes as retrieving financial information. In essence, by employing the power of Java in such applets, users are now able to do much of what they would think they should be able to do on the Internet.

Nevertheless, one of the more exciting powers of Java is that it gives programmers the ability to create multiuser environments in which many users can interact and share information. In a simple multiuser environment (see Figure 35.1), several users are connected to each other through a server running on a mutually accessible host. As a result, all actions by one user can instantaneously be displayed on the screens of other users across the world—without any requirement that the users know each other beforehand.

Figure FIGURE 35.1.

A multiuser environment.

This feature enables Java programmers to accomplish feats never before possible in a Web page. A business can now set up such a system so that people who desire information could talk with a customer service representative directly on their Web page, rather than sending e-mail to a service department. Additionally, an enterprising company could allow people from around the world to participate in a real-time live auction. In general, by facilitating multiple-user environments that enable such sharing of information, Java has the power to revolutionize the Web.

WHY JAVA?
The concept of servers, sockets, and communication is nothing new, nor is the idea of linking many users together in a live-time environment. In fact, the Web, without Java, can be considered a multiuser environment inasmuch as multiple users can access the same information in the form of a simple HTML page. Furthermore, methods exist by which a user can directly affect what another user sees on a given page (such as the hit counters found on the bottom of many HTML pages).

What, then, does Java offer that is so revolutionary? It offers the capacity to perform actions while the user is viewing the same HTML page. The architecture of the Web enables users to view Web pages from around the world. However, once this page is displayed on your screen, it cannot change. In the example of the hit counters—although more users may access the given page—you will never be informed of this fact unless you reload that page. Java, however, brings life into Web pages (by means of applets), thus enabling them to perform actions such as communicating with a server. Java enables programmers to create applets as potent and effective as the applications on your own computer, and it is this capacity that enables us to create such multiuser environments.

In this chapter we discuss the elements required to create such a multiuser environment in Java. Although no entirely new topics in Java are presented, this chapter shows you how to bring several powerful aspects of Java together and highlights some of the more interesting applications of Java. Through explanation of the processes as well as sample code, this chapter enables you, the Java programmer, to code and establish your own multiuser environment. This chapter deals with subjects including the handling of the connections to the server, the graphical interface, and various animation and threading techniques necessary to make your applet Web-worthy. While each of these topics is illustrated with code, keep in mind that the essence of a multiuser environment is what you do, not how you do it. Furthermore, the code for each multiuser application is heavily dependent on the environment itself, and therefore significantly different in each. Consequently, while the code offered here can supply you with a suitable framework, it may be advisable to envision your own multiuser application. As each topic is presented, imagine how you would deal with each of the issues involved. Remember that in programming, and Java in particular, the limits of the language are the limits of your imagination. Be creative and have fun!

NOTE ON SOCKETS AND NETSCAPE
With any new technology comes new concerns and problems. Java is definitely not an exception to this rule. The chief concern of many has been the fact that Java applets can connect to servers from around the world and transmit information from the host that its owner might not want to make public. While there are several solutions to this problem, Netscape has chosen to place stringent restrictions on Java socket connections for now.

Currently, Java sockets can connect only to servers running on the host that served the applet. For example, an applet residing http://www.xyz.com/page.html can only connect back to a server running on www.xyz.com. Therefore, when developing a multiuser environment as discussed in this chapter, make sure that the server to which the applet connects is running on the same host as the HTML page in which the applet resides. (See Chapter 40, “Java Security,” for more details about applet security.)

Our Mission Should We Choose toAccept It…

In this chapter we develop a multiuser environment for a museum that is opening an exhibition of Haphazard Art. The museum believes that the most beautiful art is created by human-controlled whims, and thus has hired you to create an environment through which users from around the world can “weave” a quilt. The user should be able to access the Web page, select a color and a blank tile, and “paint” the tile. Not only should this tile change colors on the screen of the user, but it also should instantaneously become painted on the screens of all the users around the world who are working on that quilt. (The museum will then save these designs and display them at its upcoming exhibit.)

While this is a rather simplistic example of the power of multiuser environments, it is an excellent model for the explanation of the concepts involved.

Requirements of the Server

Although it is not of our direct concern, the server in this environment plays an extremely large role. Because this server can be written in virtually any language, we won’t spend much time dealing with it here. However, it is necessary that we discuss the essentials for the server in a multiuser environment.

As you can see from the Figure 35.1, the server acts as the intermediate agent between the various users. Thus, the server must be able to do the following:

As the application becomes more involved, it is necessary to add additional functionality to the server. Even in the simple example of the museum quilt, it’s necessary that the server keep track of the color of all the tiles. Furthermore, if a new client begins work on an in-progress quilt, it’s necessary for the server to inform the client of the current status of the quilt.

Nevertheless, these subjects are extremely context-dependent and deal more with computer science than Java. Therefore, we now move on to more exciting matters, such as the socket communication required to provide the interaction.

Integrating a Communication Class in Your Applet

For an effective implementation, it’s necessary to create a class that handles all the interaction with the server. This class manages such responsibilities as connecting and disconnecting from the server as well as the actual sending and receiving of data. By encapsulating this functionality in a separate class, we are able to deal with our problem in a more effective and logical manner.

A more important reason for encapsulating this functionality in a separate class, however, is the fact that in a multiuser environment the applet must do two things at the exact same time: listen for user interactions (such as a mouse click) and listen for new information from the server. The best way to do this is to create a separate class to handle the server interaction, and make that class a thread. (See Chapter 15, “Threads and Multithreading,” for details on creating threads.) This threaded class will continually run for the lifetime of the applet, updating the quilt when required to do so. With this problem out of our hands, we can allow the applet class to respond to user interactions without worrying about the socket connection.

How to Connect to a Server

Assuming that we have a suitable server running, the actual communication is not a very complicated process. In essence, the applet need only to connect to the server and read and write information to the appropriate streams. Conveniently, Sun has wrapped most of these methods and variables in the java.net.Socket class.

Here is the beginning of our Client class that handles the actual communication:

import java.net.Socket;

import java.io.*;

public class Client extends Thread

{

   private Socket soc;

   public void connect ()

   {

     String       host = “www.museum.com”;

     int          port = 2600;

     soc = new Socket(host, port);

   }

}

Note that it is necessary for you to know both the name of the host on which the server is running as well as the port on which the server can accept connections.

CHANGES IN THE JAVA LANGUAGE
Those programmers who have been involved with the Java language know that there were some changes to the Java language in going from the alpha to beta standards as well as in the transition from the beta1 to beta2 standards. While the transition from the beta1 to beta2 standards was not very dramatic, there was a very important change regarding private variables and their accessibility. Consequently, while much of the code required for a multiuser environment could be developed with the sun.NetworkClient class in the beta1 version, access restrictions to the serverInput and serverOutput streams have made this approach impossible in the beta2 version and all later versions, including the 1.0 JDK.

The best means to provide a multiuser environment using the 1.0 API is through the use of the java.net.Socket class. (You will note that the java.net.Socket class uses the methods getInputStream() and getOutputStream() to provide access to these restricted streams.) The code in this chapter has been tested with the 1.0 version of the JDK as well as the 2.0 version of Netscape, and should work with all later versions of Java and Netscape.

While in theory the previous method is sufficient to connect to a server on a known port, any experienced programmer knows that what works in theory does not always work in practice. Consequently, it is good practice to place all communication statements in a try-catch block. (Those unfamiliar with the throw and try-catch constructs can refer to Chapter 16, “Exception Handling.”) In fact, forgetting to place all statements that deal with the server in a try-catch block will produce a compile-time error with the Java compiler.

Consequently, the precious method should actually look like this:

import java.net.Socket;

import java.io.*;

public class Client extends Thread

{

   private Socket soc;

   public boolean connect ()

   {

   String          host = “www.museum.com”;

   int             port = 2600;

   try {

         soc = new Socket(host, port);

       }

   catch (Exception e)

        return(false);

   return(true);

}

Note that the method now returns a boolean variable in accordance to whether or not it was successful in connecting.

How to Communicate with the Server

Inasmuch as different situations require different constructs, Java supplies us with several options for the syntax of communication with a server. While all such approaches can work, some are more useful and flexible than others. Consequently, what follows is a rather generic method of communicating with a server, but keep in mind that there are some nice wrapper methods in the java.io.InputStream subclasses that you may wish to use to simplify the processing of the parsing involved with the communication.

Sending Information

Once we have established our connection, the output is then passed through the java.net.Socket.outputStream, which is a java.io.outputStream. Because this OutputStream is a protected variable, however, we cannot reference it as simply as one might imagine. In order to access this stream we must employ the java.net.Socket.getOutputStream() method. Thus, a simple method to send data would resemble the following:

public boolean SendInfo(int choice) {

   OutputStream out;

   try {

          out = soc.getOutputStream();

          out.write(choice);

          out.flush();

       }

   catch (Exception e)

       return(false);

   return(true);

}

Although the preceding code is sufficient to send information across a socket stream, com-munication will not be that simple in a true multiuser environment. Because we will have multiple clients in addition to the server, it is necessary to define a common protocol that allparties will speak. While this may be as simple as a specific order to a series of integers, it is nevertheless vital to the success of the environment.

A NOTE ON PROTOCOLS
While we will adopt a rather simple protocol for this applet, there are a few issues that one should keep in mind when developing more complex protocols:

  • Preface each command by a title, such as a letter or short word. For example, B123 could be the command to buy property lot 123, and S123 could be the command to sell lot 123.
  • Make all titles of the same format. This will make parsing of the input stream much easier. For example, B and S are good titles. B and Sell would cause some headaches.
  • Terminate each command with a common character. This will signify the end of information and will also give you a starting point in the case that some of the information becomes garbled. For the sake of simplicity, the newline character (\n) is usually best.
  • In the case of our quilt-making applet for the museum, each update packet will consist of three things: the x-coordinate of the recently painted tile, the y-coordinate of the recently painted tile, and the color that is to be applied (which could also be represented by an integer). Additionally, we will use the newline character as our terminating character. All of this can be accomplished with the following method:

    public boolean SendInfo(int x, int y, int hue) {
    
       OutputStream out;
    
       try {
    
              out = soc.getOutputStream();
    
              out.write(x);
    
              out.write(y);
    
              out.write(hue);
    
              out.write(‘\n’);
    
              out.flush();
    
           }
    
       catch (Exception e)
    
           return(false);
    
       return(true);
    
    }

    Reading Information

    Another factor that complicates the reading of information from the socket is the fact that we have absolutely no idea when new information will travel across the stream. When we send data, we are in complete control of the stream and thus can employ a rather simple method as we did earlier. When reading from the socket, however, we do not know when new information will arrive. Thus we need to employ a method that will run constantly in a loop. As discussed previously, the best way to do this is to place our code in the run() method of a threaded class.

    A REFRESHER ON THREADS
    Before we proceed in our development of this threaded subclass, it is important to review a key concept. As presented in Chapter 15, a class that extends the definition of the java.lang.Thread class is able to continually execute code while other portions of the applet are running. Nevertheless, this is not true of all the methods of the threaded class.

    Only the run() method has the power attributed to threads (namely the capacity to run independently of other operations). This method is automatically declared in every thread, but will have no functionality unless you override the original declaration in your code. While other methods can exist within the threaded subclass, remember that only the code contained in the run() method will have the capacity to execute concurrently with the rest of the processes in the application. Furthermore, remember that the run() method of class foo is begun by invoking foo.start() from the master class

    Also note that because we are overriding a method first declared in the java.lang.Runnable interface, we are forced to employ the original declaration: public void run(). Your code won’t compile if you choose to use another declaration, such as public boolean run(). (We deal with ways of returning data in the section on sharing information later in this chapter.)

    What results is the method shown in Listing 35.1. In reading it, pay attention to how we are able to stop the method from reading from the socket once the user is done; how the method would deal with garbled input; and how the method returns information to the main applet. (You will be quizzed later!).

    public void run() {
    
       int spot = 0;
    
       int stroke[];
    
       stroke = new int[10];                       // an array for storing the commands
    
       DataInputStream in;
    
       in = new DataInputStream(soc.getInputStream());
    
       while (running) {
    
           do {                                    // reads the information
    
                try {
    
                       stroke[spot] = in.readByte();
    
                    }
    
                catch (Exception e) 
    
                     stroke[spot] = ‘\n’;  // restarts read on error
    
              } while (  (stroke[spot] != ‘\n’) && (++spot < 9) );
    
                                      // reads until the newline flag or limit of array
    
           spot = 0;            // resets the counter
    
           quilt.sow(stroke[0],stroke[1],stroke[2]);
    
       }
    
    } 

    Okay. Time is up. Here are the answers.

    First of all, we must remember that we are designing this class to serve within a larger applet. It would be rather difficult for the Client class to decide when the “painting session” is over for it has no idea what is going on inside the applet. Thus, the applet, not the Client class, must have control of when the class will be looking for information. The best way to retain control of the run() method is to create a boolean field (running) inside the Client class itself. This variable can be set to true on connection to the server and can be set to false when the applet decides to close the connection. Thus, once the user has decided to close the connection, the run() method will exit the while loop and run through to its completion.

    Second, we must remember that while we plan on receiving three integers followed by a newline character, in real life things do not always work as we plan them. Inasmuch as we do not know what exactly the error will be, there is no ideal way of handling garbled information. The previous method uses the newline character as its guide, and will continue to read until it reaches one. If, however, there are nine characters before a newline character, it will exit the do loop regardless and call the sow() method. This means that the sow() method in our main applet must be well constructed to handle such errors.

    Third, and most important, is the means by which the class returns data to the applet. As we discussed earlier, this Client class will be a subclass of our main applet. Consequently, we will be able to call all public methods of the applet class. (In this example, the applet is referenced by the variable quilt.) While we will discuss this process in more detail later, keep in mind that the last line of code

    quilt.sow(stroke[0],stroke[1],stroke[2]);

    simply passes the appropriate values to a method in the applet that will in turn process them.

    A NOTE FOR ADVANCED READERS
    You will notice that in this case, all the information coming across the stream is read in the form of integers. Obviously, in the real world other data types such as characters, or even mixed data types such as three numbers and a letter, could be passed. While this would require slight alterations to the above code, it would not be a dramatic hack.

    While you could deal with such a situation by having separate read() statements for each data type, keep in mind the ASCII relationship between characters and numbers. Because the ASCII value of a number is 48 more than that number (for example, ‘1’ = 1 + 48), you can choose to read all the data in as integers and then translate certain values to their character equivalents, or vice versa depending on how you sent the information to the server.

    Finally, keep in mind that serverInput is a basic java.lang.Inputstream that can be manipulated in several ways using the powers of the java.lang and java.io classes and methods. (See Chapter 18, “The Language Package,” and Chapter 20, “The I/O Package,” for more details on java.lang and java.io, respectively.)

    Disconnecting

    So far, we have been able to connect to the server and communicate with it. From the point of view of the user, we have begun the application and have successfully painted our quilt. We therefore have only one essential functionality that we haven’t included in our Client class thus far: the capacity to disconnect from the server.

    While the capacity to disconnect from the server may appear trivial, it is in fact very essential to a successful multiuser environment. A server can be constructed to accept numerous clients, but due to hardware constraints, there is an inherent limit to the number of clients that a server can have connected at the same time. Consequently, if the socket connections remain open even when the clients leave, the server will eventually become saturated with lingering sockets. In general, it’s good practice to close sockets, for it benefits all aspects of the environment, ranging from the Internet-access software that the user is running to the person managing the server.

    Without further ado, here is an appropriate disconnect() method for our Client class:

    public boolean disconnect () {
    
       running = false;
    
       try {
    
              soc.close()
    
           }
    
       catch (Exception e)
    
          return(false);
    
       return(true);
    
    }

    While the code itself is nothing extraordinary, do note the use of the Boolean variable running (also used in the run() method). You will remember that the function of the running variable was to serve as a flag that allowed the client to listen to the socket stream. Inasmuch as we do not want the client to be listening to the socket stream after the user has decided to disconnect, we set the variable to be false in this method. Also note that, the running = false statement comes before the soc.close() statement. This is because regardless of the success of the disconnect statement, we no longer wish to listen to the stream.

    The Graphical Interface

    Now that we have developed the framework of our Client subclass, let’s switch gears for a moment to develop the applet class that will act as the heart of our application. While not extremely intricate, the applet must be able to respond to user input and present the user with a friendly and attractive display. Fortunately, as a Web-based programming language, Java is very well suited for this. Each applet that you create inherits various methods that enable it to respond to virtually anything that the user can do. Furthermore, the java.awt package provides us with some excellent classes for creating good-looking interactive features such as buttons and text areas.

    Responding to Input

    The most important aspect of a graphical interface is its capacity to monitor the user’s responses to the system. As you have seen in Chapter 22, “The Windowing Package,” Java supplies us with an excellent system for doing this in the form of the java.awt.Event class. Additionally, the java.awt.Component class, of which java.applet.Applet is a subclass, provides us with many methods that we may use to catch and process events. These methods will seize control of the applet under appropriate conditions, thereby enabling the applet to react to specific user events.

    For our museum quilt applet, the user must be able to change the current color, select a tile, and quit the application. If we decide that the user will select the tile through a mouse click and change colors and use the keyboard to quit, we would require two interactive methods from the java.awt.Component class: keyDown(Event, int) and mouseDown(Event, int, int).

    In this setup, the keyboard has two purposes: to enable the user to change colors and to enable him or her to quit. If we can choose to present the user with a pallet of numbered colors (0 to 9, for example), from which he or she can select, our keyDown() method could be as simple as

    public boolean keyDown(Event evt, int key) {
    
       if ( (key >= ‘0’) && (key <= ‘9’) ) {            // if a valid key
    
             current_color = key - 48;            // converts ASCII key to numeric
    
             return(true);                                    // equivalent
    
       }
    
       else if ( key = ‘Q’) {
    
               leave();         // a method that will handle cleanup
    
               return(true);
    
       }
    
       return(false);
    
    }

    where current_color is a field that keeps track of the currently selected color.

    If we declare museum to be an instance of the Client socket class (that we have almost completed), the mouseDown() method could be accomplished with the following code:

    public boolean mouseDown(Event evt, int x, int y) {
    
       int x_cord, y_cord;
    
       if ( (x >= xoff) && (x =< xoff + xsize) && (y >= yoff) && (y =< yoff + ysize) {
    
                                      // checks to see if the click was within the grid
    
            x_cord = (x - xoff)/scale;  // determines the x and y coordinates
    
            y_cord = (y- yoff)/scale;  //  of the click
    
            museum.sendInfo(x_cord, y_cord, current_color);  // makes use of the
    
                                                    // sendInfo method to send the data
    
            return(true);                                        // the click was valid
    
          }
    
       return(false);                   // the click was outside the bounds of the grid
    
    }

    Note that in the previous method it is necessary to have already declared the x and y offsets, the size of each tile (scale), and the size of the grid itself as global fields of the class.

    Displaying Information

    In Chapter 23, “The Applet Package and Graphics,” you were given an overview of how to create a background and fill it in with whatever you would like. In a dynamic graphical interface, however, there will be various properties that will change and thus must be tracked in some manner. Our quilt application may consist of nothing more than a single colored background on which tiles of different colors are drawn. Consequently, the only information that we are concerned with is the color (if any) that has been assigned to the individual tiles.

    A simple way of doing this is to create an array of java.awt.Color (hues[ ]) and assign each color to be used a given value (hues[1] = Color.blue, for example). Thus, we can store the design in simple array of integers: tiles[ ][ ], with each integer element representing a color. If we do so, our paint() method can be accomplished by the following code:

    public void paint(Graphics g) {
    
       for (int i = 0; i < size; i++) {
    
           for(int j = 0; j < size; j++) {
    
              g.setColor(hues[ (tiles[i][j]) ]);
    
              g.fillRect( (i*scale+xoff) , (j*scale+yoff), scale,scale);
    
           }
    
       }
    
    }

    Note that in the preceding example, g.fillRect( (i*scale+xoff) , (j*scale+yoff), scale,scale); will paint squares with length and width equal to a final field, scale, that are located on the grid with respect to the initial x and y offsets.

    How to Thread the Client Class

    Thus far, we have been successful in creating the interface that the user will find on our Web page, as well as some of the methods necessary to facilitate the user’s interaction with the applet. We have also assembled a Client subclass that will serve as our means of communication.Our next step is to integrate the Client class within the applet class to produce a cohesiveapplication.

    Nevertheless, it is important to keep in mind that we are not using this subclass merely as a means of keeping the information separate. The main reason for creating a separate class is that by making it a thread as well, we are able to perform two tasks at the exact same time.

    NOTE
    Although it may seem as if we are able to perform two tasks at the exact same time, this is not entirely true on a standard single-processor computer. What Java (and other time-sharing applications such as Windows) does is portion “time-slices” to each thread, allowing each to run for a brief moment before switching control over to the other. Because this process is automatically done by the Java virtual machine and because the time for which a given thread is not running is so small, we can think of the two threads as running at the same time.

    Although we almost forget about its existence, we must retain some control over the Client class. We do not want it checking for new commands before the socket connection has been opened or after it has been closed. Additionally, we want to be able to control such socket events as opening and closing in a manner that will isolate them from the main applet, but will also impact the status of our run() method. Conveniently for us, we developed the Client class in such a manner: keeping the connect() and disconnect() methods separate entities, both of which have control over the run() method (by means of the boolean variable running).

    Consequently, within the applet class, the code necessary to control communications is quite simple:

    public class Project extends Applet {
    
       public Client museum;
    
              .
    
              .
    
              .
    
       public void init() {
    
          museum = new Client();
    
                  .
    
                  .
    
          museum.connect();
    
          museum.start();
    
       }
    
                             .
    
                             .
    
       public void leave() {
    
          museum.disconnect();
    
       }
    
    }

    First of all, note that this is the first time that we have dealt with the applet itself. Thus far its only important property is that it is named Project.

    Also note the use of the constructor Client(). Like any other class, the Client class requires not only a declaration, but also a new statement to actually create and allocate memory for the class. We will deal with this constructor again in the next section.

    Finally, note that in the previous example we established the connection by means of the museum.connect() statement located the init() method. Most likely (and in the case of the museum applet), we will want to establish the socket stream as soon as the applet starts up. Consequently, the most logical place for the museum.connect() statement is in the init() method. Nevertheless, you may place this code anywhere you may like in your applet. (You may, for example, have a “Connect to Server” button somewhere in your applet.)

    WARNING
    While giving the user the ability to initiate connection may be a good idea, be careful that you not do allow the same user to connect the server more than once without first disconnecting. This can lead to all sorts of headaches and improper results.

    How to Share Information

    We have created the framework of both the Project (applet) and Client classes and have begun the process of intertwining them by creating an instance of the Client class in the Project class. However, there is more to the interaction between the two classes than what we have thus far.

    Remember that the purpose of the Client subclass is to provide information to the applet. Nevertheless, we are required to employ the run() method of the threaded Client class, which does not allow us to return information through a simple return() statement. Also, in most cases (as well as the museum application), we must return several pieces of information (the x and y coordinates of the tile being painted as well as its color).

    However, with a little construction, we can easily solve this problem. First, we must enable the Client class to refer to the Project class. Thus, we must make a few alterations to the Client class. The changes will allow the Client class to accept the Project applet class in its constructor method and make use of it during its lifetime.

    public class Client extends Thread
    
    {
    
       private Project quilt;
    
       public Client (Project proj) {
    
          quilt = proj;
    
       }
    
    ...
    
    }
    NOTE
    Note that in this setup, the parent class is required to pass itself as an argument to the constructor method. Therefore the appropriate syntax would resemble the following:
    
    
    museum = new Client(this);

    What exactly does the previous code accomplish? First, it establishes a constructor method for the Client class. (Remember that as in C++, constructor methods must have the same name as the class itself.) While this constructor may serve other purposes, its most important function is to accept a reference to a Project class as one of its arguments.

    Furthermore, we have created a public variable, quilt, of type Project. By doing so, each method of the Client class is now able to reference all public methods and variables in the Project class. For example, if we had the following definitions in the Project class

    public int tiles_remaining;
    
    public void sow(int x, int y, int hue) {
    
    ...
    
    }

    the Client subclass could reference quilt.tiles_remaining, and quilt.sow().

    The “Translating” Method

    Okay, time for an assessment of where we stand. We are now able to

    As of now, whenever the user clicks on a square, the “request to paint” is immediately sent to the server by the sendInfo() method. Nevertheless, we have not yet developed a true method of translating a command after it comes back across the server stream.

    We have, however, laid the foundation for such a process. By enabling the Client subclass to refer to all public variables and methods of the Project applet class, we have given ourselves access to all of the necessary information. In fact, we have many options for updating the data in the applet once it has been parsed from the server stream. Nevertheless, some approaches are much better than others.

    The most secure and flexible approach is to create a “translating” method in the applet class, such as the sow() method in the Project class—mentioned several times in this chapter. This method will serve as the bridge between the client and applet classes. Why is this approach the best? Why can’t we create some public variables in the Project class that can be changed by the Client class? There are several reasons.

    Why Use a Translating Method?

    The first such reason is that it makes life a great deal easier for the programmer. By employing a “translating” method, we are able to maintain the encapsulation enjoyed thus far in our application. The sow() method will be contained entirely within the Project class and thus will have all the necessary resources, such as private variables that would be hidden from the Client subclass. Furthermore, when we are actually coding the application, we will be able to focus on each class independently. When we code the Client class, we can effectively forget how the sow() method will work, and when we code the Project class, we can trust that the appropriate information will be sent to it.

    The second reason for having the Client class rely on a foreign method is flexability. If we decided to revamp this application, thereby changing the manner in which we stored the data, we would have no need to drastically change the Client class. In fact, in the worst case, the only changes necessary would be to increase the amount of data being read from the stream and the number of parameters passed to the sow() method.

    The third reason for employing a translating method is that by linking the two classes by a method rather than direct access, we are able to prevent the corruption and intermingling of data that can occur when two processes attempt to access the same data at the same time. For example, if we made tiles[ ][ ], the array that contained the design in the applet class to be public, we could change the colors with a statement such as this:

    quilt.tiles[(stroke[0])][(stroke[1])] = stroke[2];

    However, what would happen if at the exact moment that the paint() method was accessing tiles[1][2], the Client class were changing its value? What if more data had to be stored (the color, the design, the author’s name, and so on)? Would the paint() method get the old data, the new data, a mixture, or none? As you can see, this could be a catastrophic problem. However, if we use a separate “translating” method in our applet class, this problem could easily be solved through use of the synchronized modifier. If our applet grew to the point that this problem presented itself, we could make the paint() method synchronized and place any necessary statements in a synchronized block. As a result, these portions of our code would never be able to run at the exact same time, thereby solving our problem.

    How to Create a Translating Method

    Now that we have seen why the sow() method is necessary, let us discuss its actual code. As we discussed earlier, each command in the museum application will consist of three integers: the x coordinate, the y coordinate, and the new color. Also remember that in our discussion of the Client class, we noted that the sow() method must be able to handle corrupted data. Consequently, the sow method could look something like this:

    public void sow(int x, int y, int hue) {
    
       if ( (x>=0) && (x <= size) && ( y >= 0) && (y <= size) && (hue >= 0) && (hue <= Â9) ) { // 9 colors
    
            tiles[x][y] = hue;
    
            repaint();
    
       }
    
    }

    While the first statement is self explanatory, the second statement deserves some comment, if not a complete explanation. The first statement performs the actual task of changing the color on the tile. However, the user will not see anything unless the repaint() method is called. Thus, by setting the array and calling repaint(), the sow() method updates the quilt in the mind of the computer as well as on the screen itself.

    In a brief moment, we will also harness the sow() method to perform another vital functionfor us.

    Finalizing the Client Class

    Now that we have developed the entire functionality of the Client subclass, we will tie ittogether once and for all and put it aside. First look our final product over, as shown in List-ing 35.2.

    import java.net.Socket;
    
    import java.io.*;
    
    public class Client extends Thread
    
    {
    
       private Socket soc;
    
       private Boolean running;
    
       private Project quilt;
    
       public Client (Project proj) {
    
         quilt = proj;
    
       }
    
       public void connect () {
    
         String          host;
    
         int             port = 2600;
    
         host = “www.museum.com”;
    
         try {
    
               soc = new  Socket(host, port);
    
             }
    
         catch (Exception e)
    
               return(false);
    
       }
    
       public boolean SendInfo(int x, int y, int hue) {
    
         OutputStream out;
    
         try {
    
                out = soc.getOutputStream();
    
                out.write(x);
    
                out.write(y);
    
                out.write(hue);
    
                out.write(‘\n’);
    
                out.flush();
    
             }
    
         catch (Exception e)
    
                return(false);
    
         return(true);
    
       }
    
       public void run() {
    
         int spot;
    
         int stroke[];
    
         stroke = new int[10];            // an array for storing the commands
    
         spot = 0;
    
         DataInputStream in;
    
         in = new DataInputStream(soc.getInputStream());
    
         while (running) {
    
               do {                                    // reads the information
    
                    stroke[spot] = in.readByte();
    
                  } while (  (stroke[spot] != ‘\n’) && (++spot < 9) );
    
                                            // until the newline flag or limit of array
    
         spot = 0;            // resets counter
    
         quilt.sow(stroke[0],stroke[1],stroke[2]);
    
       }
    
       public boolean disconnect () {
    
         running = false;
    
         try {
    
               soc.close()
    
             }
    
         catch (Exception e)
    
                return(false);
    
         return(true);
    
       }
    
    }

    While none of the methods are new, there is a very important piece of the class that has been completely installed for the first time—namely the boolean field running. As a field, it is accessible to all methods within the Client subclass. Nevertheless, by making it private, we have assured that if it is changed, its change will be associated with an appropriate change in the status of the socket connection—a connection or disconnection, for example.

    While the Client class may require application-dependent changes, the previous version is rather sufficient to satisfy most requirements. However, as of yet we have dealt rather little with the applet itself. This is primarily because each multiuser applet will inherently be very different. Nevertheless, there are a few topics that should be discussed if you wish to develop a quality multiuser applet.

    Advanced Topics

    What we have developed thus far in the chapter is entirely sufficient to satisfy the demands of the museum. Nevertheless, there are several other topics regarding efficiency, architecture, and appearance that we have not dealt with yet. In the next few sections we will discuss such issues as well as those regarding the development of an effective server on the other end.

    Animation and Dynamic Changes to the Screen

    While a programmer may appreciate the intricacies of a multiuser environment, users are attracted to those applications that look nice and enable them to do interesting things. While a multiuser environment is certainly one of the latter, we must still keep in mind the first criteria of making our applet look nice and run smoothly.

    Although this chapter’s purpose is not to deal with such issues as the graphical layout of applets, the communication structures developed in this chapter do have a direct impact on the “smoothness” of the changes that will occur on the user’s screen. In the case of the museum applet developed earlier in this chapter, every time that a user clicks on a tile, his or her request to paint must be sent to the server and then returned to the client. Once this information is finally returned to the client, the applet must then repaint each and every tile. While this process occurs in a matter of seconds—for the user who has to wait three seconds to see his change reflected, as well as the user who can actually observe each tile being repainted—this process may be a few seconds too slow.

    A SHOT OF JAVA JARGON
    As we have learned, the method that actually handles the creation of graphics in a Java applet is named paint(). While various actions such as drawing lines, changing the background color, and displaying messages may be performed in this method, this functionality is nevertheless commonly referred to as the “painting” of the applet.

    This slow and choppy appearance, commonly referred to as “flickering,” can nevertheless be easily remedied. While this requires a few changes to our applet, the main idea behind it is noticing that we do not need to repaint those items that have not changed. While this may seem rather elementary, when developing an applet, it would seem a great deal easier to simply repaint the entire screen. (In fact, if you take a look at some of the Java applets on the Web, you will notice that many applets exhibit the problem of flickering.)

    In the case of our museum applet, you will see that we need only to paint the tile that has been changed. Because we know the coordinates and size of each tile, this should not be much of a problem at all. It would seem as if all that we had to do was modify the paint() method to paint only one tile. Thus when the sow() method calls repaint, only the most recently changed tile is changed.

    Nevertheless, there are two problems with such a simplistic approach. First of all, the paint() method is not only called by explicit calls to repaint(). It is also called whenever the applet “appears,” such as when it first starts up or after the browser has been hidden behind another application. Thus, if we change the paint() method to paint only one tile, we may be left with only one tile on our screen, rather than the full quilt.

    Another problem is that we cannot pass information to the paint() method inasmuch as (like the run() method of threads) we are overriding an already created method.

    Don’t worry. These problems can be handled easily. Peruse the following additions to our code in Listing 35.3 and see if you can determine how the two problems were solved.

    public class Project extends Applet {
    
       private boolean FullPaint;
    
       private java.awt.Point Changed;
    
       public void init() {
    
             ...
    
         FullPaint = true;
    
         Changed = new Point();
    
       }
    
       public void paint(Graphics g) {
    
         if (FullPaint) {
    
             for (int i = 0; i < size; i++)
    
                 for(int j = 0; j < size; j++) {
    
                      g.setColor(hues[ (tiles[i][j]) ]);
    
                      g.fillRect( (i*scale+xoff) , (j*scale+yoff), scale,scale);
    
                 }
    
         }
    
         else
    
           g.fillRect( (Changed.x*scale+xoff) , (Changed.y*scale+yoff), scale,scale);
    
         FullPaint = true;
    
       }
    
       public void sow(int x, int y, int hue) {
    
         if ( (x>=0) && (x <= size) && ( y >= 0) && (y <= size) && (hue >= 0) && (hue Â<= 9) ) { // 9 colors
    
              tiles[x][y] = hue;
    
              Changed.x = x;
    
              Changed.y = y;
    
              FullPaint = false;
    
              repaint();
    
         }
    
       }
    
    }

    Not too bad, right? Let’s see how the problems were solved.

    Most notably, we have added two new variables, one for each problem. The first variable is a Boolean named FullPaint. As you can probably guess, its function is to inform the paint() method as to whether it should paint all the tiles over or just the newest one. Note that the key to this variable is that we want it to be true as much as possible so that we prevent the “one tile quilts” discussed earlier. In this setup, the FullPaint variable is set to false only one line before the call to repaint() and is reset to true at the end of the paint() method.

    CAUTION
    Remember to reset the FullPaint to true at the end of your paint() statement! If you don’t you’ll defeat the purpose of the variable—and ruin the appearance of your applet.

    How did we deal with the second problem of being unable to inform the paint() method of what tile was changed? You will notice the use of the private Point Changed. Inasmuch as the field is accessible to all methods, by setting the x and y values of this variable in the sow() method, we are able to indirectly pass this information along to the paint() method.

    A LOOK INSIDE THE EVOLUTION OF THE LANGUAGE
    In the sample code, we employed Changed, a variable of type Point, which can be found in the java.awt package. While this is a useful class, it has not always been part of Java. When the alpha releases came out, authors (especially those who were making graphical interfaces) were forced to develop their own Point-type classes. While this was not much of a problem, Sun chose to respond to this need by including the Point class in the beta and later API libraries.

    Ensuring that the Sockets Close Properly

    Although we have developed a sufficient disconnect() method, we must remember that things do not always work as well as they should. In fact, the disconnect() method in the socket class has had some problems in Java further complicated by the fact that we are running our applet through a browser. As we discussed earlier, not closing sockets can become a serious problem in a multiuser environment. Thus, it is a good practice to develop into your protocol a “close” command. Although it should resemble the other commands in your protocol, it need not be anything elaborate. In the museum example, if 123 were the command to paint tile (1,2) color 3, sending -123 could be the defined close command.

    Are You Still There?

    Another related issue is that some users may not tell the applet that they are leaving, and thus the disconnect() method will not have a chance to execute. For example, the user may go to another HTML page or his or her computer might be struck with a sudden power outage. In either case, it is thus advisable to have some method by which the server can ensure that all its clients are still active.

    A simple way of doing this is to develop another command into your protocol, the semantics of which are irrelevant. Regardless of the syntax, the server should send out some kind of “Are you there?” command periodically. In terms of the applet itself, you should develop the translating method in such a manner that when such a command is received it will respond with an appropriate answer.

    Consequently, if this functionality is built into the applet, the server will know that any client that does not respond within a reasonable amount of time (20 seconds, for example) is no longer active and can be closed.

    Requests Versus Commands

    In this chapter, we have continually referred to the information passed from each client to the server as a “request.” Furthermore, you will note that in the system developed in this chapter, although a user may click a square, his or her screen is not updated until the request has been echoed back from the server. These two facts may seem a bit abnormal, for it may seem more natural to have the applet update itself and then inform the server of what was done. Nevertheless, as you will soon see, these two procedures are very necessary for the exact same reason.

    In the example of the museum applet, users are only able to paint blank (unpainted) tiles. Thus, imagine for a moment what would happen if user A decided to paint tile (1,2) black, and a moment later user B decided to paint tile (1,2) yellow. User A’s command would be received first, and thus B’s command would be received second. (It is impossible for the server to receive two commands at the same time on the same socket. Subsequent commands are normally placed in a queue.) If the applets were designed to paint themselves before the commands were echoed, user A and user B would have different images of the same quilt at the same time. Disaster!

    Consequently, in a multiuser environment where the status would be really important, or even a game, it is essential that you develop your environment in such a manner that the status of whatever is being displayed on the screen, such as the position of the players, is updated only by what is returned from the server. As a result, the server should be developed in such a manner that any invalid requests (such as a request to paint an already painted tile) are simply “sucked up” by the server and are not echoed to any clients. Thus, if two users attempt to paint the same tile, only the request of the first user would be honored, and the second request would simply be swallowed up by the server. Even better, you could enable your server to send user- specific error messages that would cause some form of error message on the user’s screen.

    Limiting and Keeping Track of Users

    Several situations exist (such as error messages) wherein the server would like to speak with just one user. Furthermore, your situation might require that only five users may be in the environment at a given moment. These are real-world problems, but ones that are easily solved.

    At the heart of these solutions is the idea of having more than one server for your multiuser environment. This may involve one central server that has the power to spawn other servers (on other ports), or a central server acting as a”gatekeeper,” accepting clients only if there are openings.

    Either case requires the development of a richer protocol, but also provides you with opportunities for greater power over your environment. For example, you can now establish several two-player games that will be managed by a central server. By designing the applet to connect on a given port (1626, for example) and request entrance to a game, the server on that port would act as a manager by beginning a new game on another port (1627, for example) and informing the applet of the new port number. Thus, the applet would disconnect from port 1626, connect to port 1627, and wait for the central server to send another user to the same port. Once the central server has sent two players to port 1627, it would start a new server on the next available port (1628, for example) and could continue indefinitely.

    Also, in the case of the central server/children server system as well as the gatekeeper system, the server may assign each client an identification number upon connection. This will not only allow the server to keep track of who is sending the information, but also enable it to send user- specific messages (for example, “Player One—You can’t do that”). Both of these tasks can be facilitated by appending a simple identification letter or number to each command in the protocol. (B112 could mean, for example, that player 1 just “b”ought plot 12.)

    Summary

    Although we have dealt with many topics in this chapter, keep in mind that the power of Java is its abstract capability to facilitate a multiuser environment, not its specific lexical constructs. Nevertheless, there are a few issues relating to the Java language that one should keep in mind when developing a multiuser environment:


    Previous Page TOC Index Next Page