CSCI 136
Fundamentals of Computer Science II
Spring 2016

Montana Tech
Computer Science & Software Engineering



Shared Line Drawing

You will create a client and server program that allows multiple people to draw on a shared canvas. In the process you will learn how to develop a network program using socket communication. This includes creating a multi-threaded server that can handle concurrent requests from multiple clients.

Getting started. You will developing a few simple classes as well as the main program for the socket client. You will also create a multi-threaded socket server program that keeps track of the lines drawn by all connected clients. The clients will see (after a short time delay anyway) all the lines drawn by all other clients. Any client can also clear all the lines.

There is no stub code for this assignment. We have given you the API for the two simple classes. The client and server programs you can base off some of the socket examples given in class.

Line. It doesn't get much simpler than this class. A Line object represents a two-dimensional line with a given color. It knows things like its end points and its color. It can do things like return a string representation of itself and draw itself. Your Line data type must implement the following API:
public class Line
----------------------------------------------------------------------------------------
       Line(double x0, double y0, double x1, double y1, int r, int g, int b)  // Create line (x0,y0)-(x1,y1) of given RGB color, r/g/b in [0, 255]
String toString()       // String representation of the line, format "x0 y0 x1 y1 r g b"
       void draw()      // Draw the line using StdDraw
Lines. The Lines class main job is to store a collection of Line objects. This class is also important as it controls access to the shared data being assessed by the server's worker threads. Hint: think carefully about what methods need to be synchronized in the API. In your multi-threaded server, a single Lines object should be used by all the worker threads. Here is the API:
public class Lines
-----------------------------------------------------------------------------------------
  void add(Line line) // Add a new line to the collection 
String toString()     // Return a single line containing the data for all the lines
   int size()         // Find out how many lines are currently in the collection
  void clear()        // Remove all lines from the collection
The toString() method in Lines should create a single line of output by appending the output of calling toString() on all the Line objects in the collection (separated by spaces). This makes it handy for producing the ending part of the server's response to the GET command.

Communication protocol. Before discussing the other classes you will implement, it is important to understand the communication protocol used between the client server programs. The client and server communicate via a simple text-based protocol. The protocol uses a request-response style of communication, similar to protocols such as HTTP. The client always initiates a communication by sending a single line of text to the server. Normally the line is only a command, but in the case of the ADD command, additional parameters specifying the location and color of a line follow the command word. After receiving a command from the client, the server does the appropriate action and sends a single line of text in response. The server always respond with a single line of text, often just simply saying "OK".

The following tables details each command:

CommandClient parametersServer response
GETNone A single line of text containing fields separated by spaces. The first field is an integer giving the number of current Line objects in the Lines collection. The second field is an integer giving the number of currently connected clients. Following this are 0 or more 7-tuples giving the details of the lines in the collection. Each 7-tuple consists of 4 floating-point values for the endpoints of the line (x0 y0 x1 y1). After this come 3 integer values specifying the RGB color of the line. The RGB color is specified by three integers in [0, 255].

For example, the response for a canvas with 2 lines and 3 connected clients:
2 3 0.65 0.23 0.88 0.5 200 134 89 0.1 0.23 0.33 0.428 12 29 109
CLEARNone
OK
ADD A 7-tuple of the form: "x0 y0 x1 y1 r g b" where (x0, y0) and (x1, y1) are the endpoints of the line and (r, g, b) are integer values in [0, 255] specifying the line's color.

For example, to add a green line from the lower-left corner to the upper-right corner:
ADD 0.0 0.0 1.0 1.0 0 0 255
OK
QUITNone
OK

LineClient. This is the program that does the actual drawing. The client periodically polls the server (approximately every 100ms) using a GET command to obtain the current set of lines and the number of clients currently connected. The number of lines in the shared canvas should appear as text in the upper-left corner of your window. The current number of clients connected should appear as text just below the count of lines.

The client program takes 0, 1, 2, or 5 command line arguments as follows: If some other number of command line arguments is given (e.g. 3, 4, 6+), print out the following help message to standard output and exit:
LineClient [server name] [server port] [<red, 0-255> <green, 0-255> <blue, 0-255>]
All lines drawn by the same client are always this same color for the lifetime of the client. The start position of a line is specified by the user pressing the mouse button. The end position of that line is specified when the user releases the mouse button. After button release, an ADD command is sent to the server in order to add the new line to the collection. As shown in the video, you don't need to draw the line until the button is released. New lines drawn by other clients should continue to appear even while a client is busy drawing a line (i.e. the user is holding down the mouse button).

The client program supports two commands via the keyboard. The 'q' key causes the client to execute the QUIT command. This should cause an orderly closing of the input and output streams as well as the socket. Note that the drawing window in StdDraw remains open even after your client's main() method finishes. Note also that the lines drawn by a client remain in the shared collection even if the client quits. The 'c' key causes the client to execute a CLEAR command. This removes all lines (including from other clients) from the shared set stored on the server.

LineServer. This program has only a main() method and takes a single command line argument, the integer port number to listen on. This program's job is to spawn a worker thread to handle an incoming client and then carrying on listening for new clients. It is similar to Keith's Magic8ServerMulti.java example.

LineServerWorker. This is the server-side workhorse. The worker needs to have access to the Lines object so it can add new lines or clear all the lines if a client requests it. The class also keeps track of the total number of clients. The worker waits for a single line of text from the client and always responds with a single line.

When a client connects, the worker should print to standard output "HELLO:" followed by the client's Socket object (using the objects build-in toString method). Then print another line "CONNECTIONS:" followed by the total clients connected once the new client joins.

If the worker receives a QUIT command, it responds with "OK" and then does an orderly closing of the read/write streams and the socket. When the client disconnects, print to standard output "GOODBYE:" followed by the client's Socket object. Then print another line "CONNECTIONS:" followed by the total clients connected once the new client joins.

The client may be naughty and shutdown without sending a QUIT command. Try this and see what happens to your worker thread. Handle this circumstance so that your worker prints out "HANGUP:" followed by the socket information, updates the client counter, and then displays the number of connections after the impolite client is gone.

Here is an example of our server's output in which two clients connected to the server, then both disconnected normally. A third client connected and then disconnected abruptly.
% java LineServer 4242
HELLO: Socket[addr=/127.0.0.1,port=51536,localport=4242]
CONNECTIONS: 1
HELLO: Socket[addr=/127.0.0.1,port=51537,localport=4242]
CONNECTIONS: 2
GOODBYE: Socket[addr=/127.0.0.1,port=51537,localport=4242]
CONNECTIONS: 1
GOODBYE: Socket[addr=/127.0.0.1,port=51536,localport=4242]
CONNECTIONS: 0
HELLO: Socket[addr=/127.0.0.1,port=51544,localport=4242]
CONNECTIONS: 1
HANGUP: Socket[addr=/127.0.0.1,port=51544,localport=4242]
CONNECTIONS: 0


Grading
Grade ItemPoints PossiblePoints Earned
Program Compiles
1
Program Runs
1
javadoc Comments on All Classes and Methods
4
Class: Line
4
Class: Lines
4
Class: LineClient
5
Class: LineServer
5
Class: LineServerWorker
6
Total
30

Why does the server need to send "OK" back for some of the commands? It isn't strictly necessary, but this way all the commands have a client request followed by a server response. This simplifies the logic of the client and server.

What is an easy way to parse the data from a GET response? We recommend you use the Scanner class. You can use it just like with a file, but you pass the constructor a String instead of a File object.

How should the server keep track of the number of connected clients? This is a perfect job for a static instance variable in the worker class. If you declare an instance variable static, all instances of that class share the same underlying variable. But remember that anytime multiple threads access the same shared variable, you have to consider problems this concurrency might cause.

What type of exception is caused if the client terminates without issuing a QUIT command? Normally this causes an exception of type SocketException. But we've also seen other exceptions such as NullPointerException. So this is a case you might consider catching an exception of type Exception. This will cover any type of exception but be aware that it could also hide any other bugs in the enclosed code!

How can I make sure there is only one instance of the Lines class shared among all my workers? If you declare an instance variable as static, only a single variable is created and shared between all objects of the data type. An equally valid approach is to have your LineServer instantiate a single Lines object and then pass a reference to this object to all workers. But remember that anytime multiple threads access the same shared variable, you have to consider problems this concurrency might cause.


Extra credit. Add additional functionality to the shared drawing program. Some ideas:
  • Allow drawing of different shapes such as lines, circles, points, etc.
  • Allow selection of a color from an onscreen palette of colors.
  • Make it into some sort of game.

Submission. Submit your programs Line.java, Lines.java, LineClient.java, LineServer.java and LineServerWorker.java, using Moodle. Be sure each submitted source file has the required header with your name, username, and a description of the program. For extra-credit, please submit a single zip file containing all your programs/graphics/sounds via the special Assignment extra-credit drop box.

Page last updated: December 28, 2016