- Grab a studio sheet and open your group workspace as usual.
- Use your notes from class that describe the architecture of the multiuser
- Run BLockingQueue as a Java application. Why do you see
what you see given the code in the class at this point?
- Work on the BlockingQueue class so that it properly uses
wait and notifyAll to accomplish its goals as
documented in its comments.
Do not spin, but loop with a wait inside.
Recall the pattern
for wait and notify where some predicate p must be true before
a thread can proceed:
- The thread that needs p to be true executes while (!p) o.wait();
and it must have a lock on o.
- Any thread that can cause p to become true executes
o.notifyAll(); and it must have a lock on o.
- It is handy to have o be the object that must be manipulated
in a thread-safe manner, so that only one thread at a time modifies o.
- Run the BlockingQueue as a Java Application and make sure
that all 20 integers (0...19) are enqueued and dequeued without problem.
You should see the program pause initially, then enqueue 10 objects quickly,
then as each object is dequeued a new object should get enqueued.
Have the professor or TA check your work here.
- ConsoleHandler: take a look at this class and see how
it intends to interact with the GameController class. The
ConsoleHandler can call methods on the GameController
locally, on the same computer that is running the GameController.
In a real game, the ConsoleHandler could be used to see how the
game is going, change parameters about the game's execution, shutdown the
game, etc. Because the ConsoleHandler is under control of the
game operator, it can usually do more things with less checking than a
code to ConsoleHndler so that if the game operator enters
die die die, the shutDown
method of the game object is called. You can't test this yet, but
try to modify ConsoleHandler anyway to incorporate this feature.
If this kind of message were accepted by
a PlayerHandler, then any player could shut down the game.
- You are about to complete the Server class, which
will wait for clients to connect who wish to play the game.
Its service is offered at some port (3989 in this studio). When a
connection is made, the Server will start a new PlayerHandler
to interface with the new player.
In the Server's run() method, write code that:
- Establishes a ServerSocket at the specified port.
- Continually (infinite loop) accepts a connection from the
ServerSocket, constructs a new PlayerHandler for
the resulting Socket and the game, and
.start()s the PlayerHandler.
Why is it important to .start() the new PlayerHandler instead
of .run()ning it?
- PlayerHandler: this class is responsible for handling
the actions of the player. It is this class that along with the client code
must determine the protocol of how the client and server interact.
Complete the code as directed by the comments in the code.
If you have a DataInputStream object, you can call readUTF()
on that object to read a Java String.
- Look at how the code handles any exceptions. You can always throw
an Error if you want the code to stop.
- Be sure you understand just how the try...catch...finally
code works, especially if you had trouble on the midterm.
- At this point you should be able to run GameController as
a Java application and see the server working with the simulated
clients and with the ConsoleHandler.
The ConsoleHandler pops up a box into which you can type messages
that are passed along to the game.
- GameController: complete the showPlayers method
so that it displays a list of the current players,
as you saw in class today.
Do not do the work directly! Instead, use addRunnable to queue
up the work for the server to perform.
- Sometimes the server will want to send a message to the client. In a game, for example,
maybe a client's move was unacceptable, or perhaps an entire board will be
transmitted to each client.
The GameController has a method tellClient
that is incomplete. Finish this so that it calls tellClient in
PlayerHandler. The code currently in PlayerHandler currently
just prints out a message on the game console.
If you have time,
- Modify the PlayerHandler tellClient(String s) so that it sends
message across the socket to the client.
- Modify the SimClient so it can accept such messages
in a separate thread. Start that thread from the SimClient's
run() method. This allows the client to send and receive messages
You must agree on the protocol for sending and receiving such messages.
Question: Do you need to worry about race conditions as the server sends messages
back to the client? Why or why not?
The client now should get messages everytime a player joins. Have it display
them client-side on the console.
You can force a new player to join server-side by
typing spawn into the ConsoleHandler
on the server side.
- The main of GameController currently starts the
server in one thread, and causes some clients to request sockets locally in other
threads using localhost.
If you have time,
you should next try running this studio in a distributed fashion using
your studio partner or another studio team:
- One of you will function as server, running the GameServerOnly class.
The GameServerOnly class will tell you its hostname when it runs. You have
to tell the other team that name so they can connect to you. The server must
be running when the clients try to connect.
- One (or more) of you will function as a client, running the ClientOnly class.
The client must know the hostname of the server, which you will get from the
server team. Address the FIXMEs in ClientOnly using
the server hostname and your client's team name.