This week, you will continue work on the program that you started in the previous lab. In that lab, you created a Graphical User Interface. This week, you make that interface functional. The program will be a "client" for a network chat room. It will allow users to connect to a chat room "server" and exchange messages with other people who are connected to the same server. Although the lab lets you do some network programming, the main point is really a lesson in thinking asynchronously, that is, dealing with the fact that events can happen at any time, and you have to write methods that can respond to them whenever they occur.
Note that you are not going to write the server. During the lab period on Thursday, a server will be running on the computer with name eck.hws.edu, and you can connect to that server during the lab. When you are working on the program later, you will have to run your own server. The server program is available in the file ChatRoomServer.jar, which you can find in the directory /classes/f09/cs124/files_for_lab_12. (You can look at the source code, although you shouldn't expect to understand it.) To run this server program yourself from the command line, go to a directory that contains the program and enter the command:
java -jar ChatRoomServer.jar
The program will run until you hit Control-C (or close the terminal window where it is running). As it runs, it will print out status information about users who connect to the server. When you want to use your client to connect to a server that is running on the same computer, you can use localhost as the name of the server. (Connecting to a server will be discussed below.)
To start the lab, you can try out a complete version of the client program, which is in the file ChatRoomClientComplete.jar, in the directory /classes/f09/cs124/files_for_lab_12. You can run this program by double-clicking its icon or by using the following command on the command line:
java -jar ChatRoomClientComplete.jar
When you run this program, it asks you for the name of the computer where the server is running and for a "screen name" that you will use in the chat room. It will then open a ChatRoom window and try to connect to the specified server.
After trying out the sample solution, create a new folder or project named lab12. Copy the file ChatRoom.java, which you worked on last week, from lab11 into lab12. Also add the files SimpleNet.java and SimpleNetObserver.java from /classes/f09/cs124/files_for_lab_12 to your lab12 folder.
This lab is due at the next lab period, in two weeks.
Networks allow computers to communicate with other computers. Network communication usually follows the client/server model. One computer must act as a server. The server "listens" for connection requests from other computers. A client computer sends a connection request to the server, and the server accepts the request. Once the request has been requested, the client and server are connected, and there is a communications channel between them. The channel is two-way; each computer can send messages to the other. The connection lasts until one of the two computers closes the connection.
In order to send a connection request, the client must know the location of the server. The location can be specified by a host name such as math.hws.edu or www.nytimes.com. Furthermore, since it is possible for one computer to have connections to several computers at the same time, there has to be a way to distinguish one connection from another. This is done with port numbers, which are simply integers in the range from 1 to 65535. A server is a program listens on some specific port number. A client must send a request to a specific host name and specific port number.
Network communication can get very complicated, partly because of the asynchronous nature of network communication. That is, messages can arrive from the other side of the connection at any time. Another problem is that network operations can block: It can take some time to open a connection or to send a message, and a program might have to wait for some unpredictable time for a message to arrive.
In this lab, you will use a class named SimpleNet that I wrote to provide a simplified interface to the network. With this class, you work with the network in the same way that you work with asynchronous events. That is, you write event-handling methods that will be called when an event occurs. When a message arrives from the other side of the connection, that is an event. When a connection has been opened, that is an event.
When using SimpleNet, the methods for handling network events are defined by a Java interface named SimpleNetObserver. In order to work with SimpleNet, you will have to implement this interface.
A SimpleNet object represents one endpoint of a (potential) connection between two computers. SimpleNet only supports simple text messages. Each message is a single line of text. SimpleNet can be used in server mode, but you will only use it in client mode, to connect to a server that is running somewhere else.
In order to do anything with the connection, a SimpleNet objects requires an "observer" that implements the SimpleNetObserver interface. This observer is responsible for handling events generated by the connection.
In your chat room program, the ChatRoom class itself can implement the SimpleNetObserver interface. The definition of the class must declare that it does so. To do this, change the first line of the definition of class ChatRoom to read
public class ChatRoom extends JPanel implements ActionListener, SimpleNetObserver {
Furthermore, the class must provide definitions for all the methods that are specified in the SimpleNetObserver interface. Here are the methods that must be defined:
/** * Called by SimpleNet when a message is received from the * other side of the connection. The data parameter contains * the message that was received. */ public void connectionDataReceived(SimpleNet connection, String data) { } /** * Called by SimpleNet when a connection has been established * (after you have requested SimpleNet to make a connection). */ public void connectionOpened(SimpleNet connection) { } /** * Called when the connection has been closed because you * requested it to be closed (by calling SimpleNet's close() * method. */ public void connectionClosed(SimpleNet connection) { } /** * Called when the connection has been closed normally by * the other side of the connection. */ public void connectionClosedByPeer(SimpleNet connection) { } /** * Called when the connection has been closed because an * error occurred, either when attempting to open the * connection or while transmitting data or because some * other network problem occurred. */ public void connectionClosedWithError(SimpleNet connection, String errorMessage) { }
You can copy these declarations into your ChatRoom class. You will have to provide definitions for these methods. Note that each method has a parameter of type SimpleNet. This parameter is useful in a program that uses more than one connection -- it tells which connection produced the event. Since your program will only use one connection, you can ignore this parameter.
To use SimpleNet, you need to create a SimpleNet object. To keep track of the connection, you will need an instance variable of type SimpleNet. Let's say that you name the variable connection. To work with this variable, you only need to know about three methods and a constructor from the SimpleNet class.
To create the SimpleNet and to make the connection to the ChatRoomServer that is running on eck.hws.edu, you can add the following code to the ChatRoom constructor:
connection = new SimpleNet(this); connection.connect("eck.hws.edu", 40001);
Later, you will probably want to modify this so that the user can enter the host name, instead of assuming that the server is on eck.hws.edu.
Now comes the actual work of the lab: You have to make everything work together to implement a working client for the chat room application.
Your program will have to respond to two general kinds of events. Events from the user are handled in the actionPerformed() method. It's in this method that you will send the messages that the user wants to send and will close the connection when the user wants to quit, for example. The second kind of event comes from the connection. These are handled in the SimpleNetObserver methods such as connectionDataReceived(). You will have to figure it all out!
To communicate effectively with the ChatRoomServer, you need to know the protocol that it uses for communication. A "protocol" for communication specified the details of what messages can be sent, when they can be sent, and what they mean.
The protocol requires that the very first line that you send to the server has to be of the form "~user NAME", where name is the screen name that will be used to identify the user of the chat room. The NAME must be a single word, with no spaces. You can send this line after opening the connection in the ChatRoom constructor:
connection.send("~user " + name);
where you can use any name that you like. Later, you probably want to modify this so that the user of your program can select a screen name.
To get things working quickly so you can test your connection, you can simply program connectionDataReceived() to append any message that it receives to the transcript. And in actionPerformed(), when the user wants to send a message, you can simply call connection.send(message) to send it to the server. However, to properly implement your client, you have to take the full protocol into account. In fact, most messages between the server and the client have to include more than just the text that is displayed to the user. The message should also include control information that tells the server or client what to do with the message.
Here is the full list of messages that your client can send to the server:
And here is the list of messages that the server can send to your client:
When you write your full version of connectionDataReceived(), you will need to inspect the message that you have received to see what kind of message it is. Note that you will have to break the message into two parts, the part that comes before the first space and the part that comes after the first space. Here is some code that will do this, where data is the full message that was received from the server:
String first, second; // The two parts of the message. int pos; // The index of the first space in the message. pos = data.indexOf(' '); first = data.substring(0,pos); // Chars from 0 to pos-1. second = data.substring(pos+1); // Chars from pos+1 to end.
To finish the program, you need to know a few more things. First, and most simply, the command for clearing the transcript is transcript.setText(""). But to make a good user interface, you'll need to use at least one more class: JOptionPane.
The poorly-named class JOptionPane contains a variety of static methods for displaying dialog boxes on the screen and, in most cases, getting a response from the user. The simplest method is
JOptionPane.showMessageDialog(parent,message);
which displays a message to the user in a dialog box and waits until the user clicks an "OK" button in the dialog box. You can use this method when the connection has been closed, to tell the user what is going on, before exiting from the program. The parent is a component that acts as the parent or owner of the dialog box. (The window that contains this component is blocked while the dialog box is on the screen.) You can use the special variable this for the parent. The message can be more than one line long; use the '\n' character in the message as a line break.
JOptionPane also has a method, showInputDialog, that lets you ask the user a question and get back the user's response. The response is the return value of the method. The general format is
response = JOptionPane.showInputDialog(parent,question);
The user can cancel the dialog box, in which case the return value is null. If the user clicks the "OK" button in the dialog box, the return value is a non-null string (but it might be an empty string).
You can use this method in the ChatRoom constructor to ask the user for the host name of the computer where the server is running and for the name that the user wishes to use as a screen name. Since the window has not yet been opened when the constructor is running, you should use null as the parent in this case. If you want to be careful, you should check whether the user has canceled the dialog; in that case you might end the program or ask the question again.
Please be sure to test your program and to check it carefully to make sure that you have implemented all the commands and covered all the different types of messages.