Overview:
Inet checklist <-- Download and complete the InetServer checklist.
Joke checklist <-- Download and complete the JokeServer checklist.
In this assignment you will build a pair of multi-threaded servers
that accept input from multiple clients, and return appropriate output. In
addition to the basic client-server model, you will also implement a
secondary administration channel to your servers, and manually maintain
the state of all conversations within your distributed application.
Each aspect of the specifications requires you to solve a particular
client-server problem while maintaining a conversation within a
stateless protocol. The assignment is designed to give you general-purpose
exposure to writing code for a client-server environment, and working
with threads, neither of which is trivial.
Your finished server is just for fun, but with minor changes it can serve
as the basis for a real, viable, client-server application handling
thousands of client conversations simultaneously.
Note that we will not make these servers thread-safe which is beyond
the scope of this assignment.
InetServer:
-
Install Java 16.0.1 (or try
here ) if you need to. We will be running your
programs under Java 16.0.1, but later versions will mosty work well, depending
on extended features.
- You will need several command windows (shells), each running a
separate process to run your client/server programs. Command windows
are available under Windows by using [Windows Key | cmd] (or Windows Key |
PowerShell). You can use [Start]
within the original command shell to start other windows. Under Unix
(and Apple OSX which runs on top of Unix) use command shells [Terminal
Windows] of your choosing.
- Refer to the InetServer PDF
document, and the lecture, for large hints on how to get this program
working. The expectation is that you will TYPE IN THE CODE YOURSELF,
and GET IT RUNNING. In my experience, the 30-40 minutes to type in the
programs yourself, fix the bugs you have introduced, and figure out
how they run, will be paid back many times over.
- Remove ALL of my comments from InetServer.java and
InetClient.java. Add extensive comments of your own demonstrating that
you know how the server works. This is a required part of this pedagogical
assignment, demonstrating that you understand the code.
- Some sample Java Inet .class files and source
code are available to help you get started. (These work fine under
Java 1.8). Run the client/server program as follows: Download ALL the
.class files into a directory. Issue [> Java InetServer] in one of the
windows, and [> Java InetClient] in a separate window for each client
that you will be running simultaneously.
- Running the InetServer: In one command window issue: [> java
InetServer]. In another command window issue: [> java
InetClient] Then follow the prompts on the client to look up the IP
addresses of domain-named computers. Optionally, you can run the
program on two different machines, exactly the same way, but by
starting the client with the name or IP address of the server machine
as the first argument to the InetClient program.
- You may have to speak to your firewall about allowing the client/server
TCP/IP connections, depending on how it is configured.
- Note: The application code in the server—looking up the IP
addresses corresponding to domain names—is not particularly
interesting to us and was just some fun network code using existing
libraries. The client and server portion of the assignment is what we
care about. The assignment would be almost as interesting if we simply
sent two numbers from the client to the server and had the server add
them together.
- Submission files for Inet:
- InetServer.java
- InetClient.java
- InetChecklist.html
Zip these three files (two java, one html) together and submit to the InetServer D2L
link. (Use standard ZIP format only.)
- [Test of the provided TII plagiarism checker:]
- Concatenate your two Inet java source code files together and submit your
InetAll file to the InetTII link as .docx, or .html (.txt as a last
resort) NO ZIP OR PDF FILES HERE.
- Run the plagiarism checker against your InetTII link by clicking
on the D2L number representing the number of InetTII submissions you
have made, and following the links. In this case only, your code will
likely appears as being flagged for plagiarism (because all of the
code is the same as other submissions), but none of your comments should
be flagged for plagiarism.
Administration:
- Get the InetServer program fully working. You are free to use this as a basis for
your JokeServer.
- Complete the Inet Checklist. Submit your zipped Inet java files (and
checkist) to D2L. Concatenate the java source code for your Inet Client
and Server together and submit the .docx or .html file to Inet TII. Run
the plagiarism checker for practice. (Note that everything except your
comments will be flagged for plagiarism because we copied the code. This
is just practice with TII.)
- Place the completed checklist with the files you submit to D2L as
JokeChecklist.html. Update it as you make progress by changing
no to yes each time you complete one of the checklist
items. Never change a no to a yes unless
the work has been done. Saying yes you have completed a
feature of the assignment when you have not done so will be taken as
an attempt to cheat your peers, resulting in outright failure. In
cases where you really aren't sure, you can put maybe or
probably and give an explanation in
the comments section below.
- There is a high priority on our being able to download, and run,
your three java applications (JokeServer, JokeClient, JokeClientAdmin)
without any complications. So, even though this may not be the most
elegant coding style, put all your source files in one
directory, along with your checklist, the JokeLog, and so on,
and zip the files together before submitting to D2L. Make sure that your
programs will compile when we issue "javac *.java" at the command line
prompt from within this single directory. NEVER use java packages. That is,
the package statement should not appear in your source code.
- Submission files for JokeServer:
JokeServer.java
JokeClient.java
JokeClientAdmin.java
JokeLog.txt
JokeChecklist.html.
The filenames MUST be precise; we use automated scripts as part of the
grading process. Zip all these files together and submit the one .zip
file to the JokeServer D2L dropbox.
- Concatenate all your JokeServer files, but NO INET FILES, into
one plain text file (.docx, or .html, but NO ZIP FILES) for
submission to the second JokeServerTII D2L Dropbox link for plagiarism
checking. I have provided one of the plagiarism checkers I use as a
link from D2L. CHECK YOUR SUBMISSION to see if it will be flagged for
plagirism. [In D2L, click on the number link showing the number of
submissions you've made and follow this path to retrieve your
plagiarism report.] There may be some minor overlap with the work of others,
but the overall percentage should be low, and your comments should
always be unique.
Your concatenated files will not be used for compilation and you need
not be concerned about formatting, but the TEXT MUST BE THE SAME as
the JokeServer files you have submitted for grading, and the single
TII file must be readable by the plagiarism checker.
- Be sure to include the required java
comments header in each of your java files.
- Make sure that you are familiar with the assignment submission rules.
Programs that do not precisely conform to the rules will not be
graded. Please do not ask for an exception to this policy. Strict file
names apply, and are used as input to various grading scripts and
plagiarism checkers.
- Allow yourself enough time to get a basic version of the
JokeServer running. Debugging TCP/IP on your machine, having difficulties
wtih your firewall, spawning runaway processes, etc. comes
with the territory, and you should not underestimate the amount of
time this will take you during the initial phases of this project.
- You will have to figure out a way to capture the output from your
running programs so that the output, showing your working programs,
can be placed in your JokeLog.txt file. Copy
and Paste from the console is probably the easiest scheme. I do not
recommend writing to a file, because it takes a lot more work and you'll have to
address having three output streams from at least three running processes
in three files that later have to get concatenated together. Don't waste
time making the output fancy.
- Note that you have to maintain a theoretically unlimited number of
arrays, or other data structures, to keep track of client state; you
have to connect from two different kinds of clients, and so on. Each
of these steps may take some thought, so leave plenty of time to get
this assignment finsihed.
- Allow yourself enough time to format the presentation of your work
exactly as specified. This may take some thought and some
experimentation the first time you do this. For exampe, your programs must
compile and run from the command line. You should NOT assume that you
can get this done at the last minute.
- I strongly recommend that you complete this assignment
incrementally, and each time you get a partial version running,
you submit it to D2L (with the appropriate checklist!). This way, if
you run out of time you will still get substantial partial credit. If
you submit a partial version (with the matching checklist) you should
put a console message for the grader when the program starts up, and a
note in the comments section of your checklist that this is a partial
version of the assignment and you plan to resubmit later.
Suggested Development Order
- You can develop as you wish, but following is the partial,
incremental development order I recommend. After each step, clean up
the interface code as necessary to meet the console input and output
specifications. Update your checklist as you complete steps. Comment
your code during development .
- Remove the Inet application code (but leave the Inet network code); return a single joke from the
server as soon as the client connects.
- Connect from the admin client and automatically toggle the server mode
between joke and proverb on each connection. Display on the server console
what the mode is each time you toggle. If you are not clear on how to change
the server mode in one method (and thread—e.g. the Admin Worker) and read
it in another (e.g. the Joke Worker) you can
refer to this Simple Communication Java
Program or Mode Changer Method .
- Return either a single joke or a single proverb to the client depending
on the server mode.
- Start writing server console output about what has
occurred. Determine how you are going to capture the server and client
console logs so you will be able to capture this output, concatenated
it into JokeLog.txt and turn it in when your program is done. (Copy and
Paste from the console is recommended.
- Return four jokes in order: JA, JB, JC, JD, then repeat over and
over, and do the same for proverbs PA, PB, PC, PD, etc.
- At the end of each joke cycle and proverb cycle, announce on the
console "JOKE CYCLE COMPLETED" or "PROVERB CYCLE COMPLETED."
- Connect from multiple clients and verify that the previous step
works for each client, independently without interference
from other clients.
- Return interleaved jokes and proverbs, depending on server mode
at the time of any given request, without losing track of where you
are in the JA,JB,JC,JD cycle of jokes, and the PA,PB,PC,PD cycle of
proverbs for one client, verifying that there is no intereference in
the two respective cycle orders for each of jokes and proverbs. So, (without
randomization) you might have JA, PA, PB, JB, JC, JC, PC, PD, JA, JB...
- Return interleaved jokes and proverbs without losing track of
where you are in the JA,JB,JC,JD cycle of jokes, and the PA,PB,PC,PD cycle of
proverbs for multiple clients, verifying that there is no
interference from switching server mode from joke to proverb, and no
interference between any one of your multiple clients and another.
- Parse the user name in the JokeClient before entering
the request loop. Insert the user's name right after the "JA", "JB",
"PA", "PB", etc. designation, and before the joke or proverb. For
novice programmers this string processing can be hard. Do this toward
the end. It is not a particularly critical part of the
assignment. Utility code tips and questions on string manipulation in
java at the class forums is encouraged.
- Re-randomize the jokes, and proverbs for each client conversation
at the start of each four-item joke and proverb cycle. Veryify that
the randomization of jokes and proverbs for one client conversation
does not interfere with any other client conversation. Do this toward
the end. It is confusing, and not a particularly critical part of the
assignment.
- Go through your entire program to verify that ALL specifications
are met regarding file naming, comments, program logic, console input,
and output.
- Make sure your JokeClient and JokeClientAdmin programs will
accept a first argument with a different domain name or IP
address. Run your clients and server on different machines if it is
practical for you to do so. Take some care that your firewall does not
interfere with you running your programs across the network. [To find
the IP address of your machine under Windows, type "ipconfig" at a
command prompt; use "ifconfig" under unix.]
- Once your default JokeServer is fully working, proceed with the
following steps. Make SURE that when no arguments are passed to your
client java programs they run at the default locations, as given in the
specifications.
- Change your JokeServer to accept an argument (see below) indicating that the
JokeServer should run at the secondary server port number, rather than
at the default server port number. The intent is that this will only
happen after the first, default, JokeServer is already running at the
default port.
- Modify your JokeClient and JokeClientAdmin programs so that when they
are given two arguments, the first argument will point to the domain name or
IP address of the server, and the second argument, if present will be used
as IP address of a second server.
Multi-threaded Joke and Proverb Server:
JokeServer Conventions
JokeServer Development
- Start with your InetServer and InetClient programs. Rename them
to JokeServer.java and JokeClient.java. Remove the Internet
lookup application code from your client and server, but leave in the
basic client and server loops, and connection architecture.
- Modify your JokeClient so that it asks for the user's name [used
later] then enters a loop.
- Inside of the loop, when the user presses <enter> this signals a
connection to the server. When your JokeClient connects to your
JokeServer this implements a request to the server to respond with a
joke or a proverb, depending on the server's current mode.
- For each conversation with a client, complete sets of four jokes,
[JA, JB, JC, JD] and, independently, four proverbs [PA, PB, PC, PD]
are returned to the client one at a time. When all of the jokes or
proverbs in each set have been returned, then start over at the
beginning of the set. Multiple clients [theoretcially, many thousands
of clients] run simultaneously, and each client conversation is
completely independent of all other client conversations.
- Your JokeClientAdmin connects to the server and sets it in
Joke Mode [the default], or Proverb Mode for ALL
subsequent client connections within the respective conversations.
- Modify the client so it accepts the name of the user (one time!)
before entering the request loop. The server accepts the name as input
from the JokeClient, and thereafter inserts the name appropriately
into the body of all jokes and proverbs returned as part of the
conversation with that client. On the client, the user need only press
enter to make repeated requests of the server.
- Once your jokes and proverbs are being returned correctly, without
interference between client conversations, then at the beginning of
each joke or proverb cycle, randomize the jokes or proverbs before
starting the cycle again. However, the rule still stands that no joke
or proverb can be repeated as part of a client conversation until ALL
the jokes or proverbs have been seen by the client, at which point the
cyle (and randomization) is started all over again.
This radomizing is a small part of the assignment, but think about how
this might be done. Did you implement randomness efficiently? Does
your solution scale up for 1,000 jokes? 1,000,000 jokes? Why might a
set implementation, possibly with a linked data structure, be useful
here, rather than randome access to an array? Note: efficiency in your
implementation is desirable, but will not affect your JokeServer
grade. Comments in your code?
- Build a template of exactly four jokes proverbs and four proverbs
for use on your server. Put "JA " at the beginning of the first joke,
"JB " at the beginning of the second, "PC " at the beginning of the
third proverb, and so on to help with debugging, and grading [this is
required].
- Each time a client connects, select the next joke or proverb to
be sent as part of that conversation, and then insert the user's name
into the joke or proverb after the "JA " indication, and
before the body of the joke.
Asssuming the user's name is Joseph, you might start with the template
for our first joke:
JA <name-holder>: Why did the chicken cross the road? To get to the other side!
The server modifies the template for this client to produce:
JA Joseph: why did the chicken cross the road? To get to the other side!
- Proverbs are the same:
PD Joseph: The early bird gets the worm.
- Modify the server to accept a connection at port 5050 from an
administration client (and, later at 5051 for the secondary server). This
will take some thought because you will have to make an asynchronous call,
using a separate thread, to start a second server listener waiting for
administration client connections. See the file joke-threads.html for some hints, and
the file joke-pseudo.html for pseudo
code.
- Write a second client, JokeClientAdmin that connects at the
administration port and toggles the server between Joke Mode and
Proverb Mode.
State Maintenance:
- How you maintain the state of client conversations is up to you,
as long as you always break the connection after each joke, or proverb
is returned. But this will require some though before you begin to
write code. I recommend that you write out your design with pencil and
paper before beginning to write computer code.
- You have many ways that you can implement state maintenance. For
example:
- MINIMAL COOKIE ON THE CLIENT; FULL STATE ON THE SERVER: You can
maintain state by sending a unique "cookie" (e.g., a UUID would be one
way to implement this, or a large random number unlikely to have a
collision) from the client when it first connects. Pass the "cookie"
from the client each time it connects, and use it on the server to
look up the state of the unique client conversation which has been
stored there. Use this mechanism to guarantee that the client never
gets a joke, or proverb, repeated until ALL of the jokes and proverbs
in each respective four-item cycle have been sent. Then, cycle through
all the jokes or proverbs again, after re-randomizing. See the file joke-state.html for some pointers
on maintaining the state of the client.
- FULL STATE ON THE CLIENT; NOTHING ON THE SERVER: You can send the
entire state back to the client after updating by the server, and
maintain nothing at the server side. That is, read and update the
jokes-sent/proverbs-sent "checklist" (sent by the client) at the
server with the new joke or proverb marked as having been sent, and
then send the whole joke/proverb checklist back to the client. Each
time the client connects, it sends the entire state (contained in the
checklist, plus the user name) again.
- MAINTAIN PART OF THE STATE AT EACH END: You can implement
anything in between the above two schemes, such as keeping the cookie
and the joke-state on the client, and the proverb-state on the
server; the cookie and the user name on the client, but the rest of
the state on the server; etc.
- NOTHING ON THE CLIENT: It is not possible to implement the JokeServer
this way. Why not?
- MUST CONNECT TO SERVER: Even if you maintain the entire state of
the client conversation on the JokeClient, and update it there, you
cannot determine joke-mode or proverb-mode without connecting to the
server. You can, however, download your four jokes and four
proverbs when you first connect, then just ask the server what mode it
is in each time you re-connect. When the jokes or proverbs are
exhausted, you can reconnect to get them randomized and downloaded
again, or just randomize them on the client, depending on your design
and how often you envision the jokes and proverbs being updated.
Note that this allows for some network efficiency (less data going
back and forth) and can off-load the randomizing of the jokes and
proverbs, but it does mean that you have lots of duplicate data
scattered around the web. Suppose that your legal department sends a
memo that one of your jokes is now considered libelous? With this
design, you are out of luck.
- COOKIE VARIATION: Optionally you could initially send an empty
cookie/state structure from the client at startup time, and have the server
assign a value when it notices that the cookie or state structure has not
been initialized. This allows the use of a simple counter maintained by the
server to identify conversations of various clients. Thereafter, the
client just sends the number assigned as the cookie.
- Maintain state for each client such that if the server is, e.g.,
switched from Joke Mode to Proverb Mode, and then some time later back again
to Joke Mode, the constraint that no joke is repeated until all four have
been randomly returned is maintained across these transitions among server
states. (Similarly, this constraint also holds for proverbs.)
- ALL client conversations are entirely indpendent, except that the
corpus of joke templates and proverb templates is the same, and all
client coversations use the current server mode to determine whether a
joke or proverb is to be returned to the client.
- QUESTIONS: For a large-scale production system, when is it
appropriate to send just a cookie back to the client? When should the
whole state be sent to the client? Suppose that in place of jokes you
were sending 4 GByte database entries? Suppose that instead of a
static joke, you were computing, in real time, some highly
cpu-intensive output based on input data assessed at the server at the
time of the request? Which design would you use for each? How would
your solution scale?
Extending to two servers, primary and secondary:
Execution:
- Run your sever with multiple clients active at the same time. Interleave
requests from different clients. Capture eight output responses from one
client in joke mode, and eight in proverb mode, showing that the jokes and
proverbs are returned randomly, but not repeated until all four in
each set have been sent back. Annotate this in your output log.
- Repeat the process, but this time use your adminstration client to
interleave Joke Mode and Proverb Mode, showing that state is correctly
maintained across changes between modes.
- Put the output from your running sessions in a text log file and
ANNOTATE the output—highlighting the randomness of the jokes,
the interleaving of modes, etc. Do NOT change any of the output data,
but you can add some white space to make it easier to read if you
like.
- Format the presentation and submission of your work exactly as specified.
Bragging Rights:
These modifications are not required, but if you complete them, let me
know about it, and post your interesting work on the forums! Be SYRE
to print a comment about the extra features to the console screens at
startup time, and give clear prompting. Your JokeServer and clients
MUST still run in the default way. Pass an additional argument to your
programs to turn on the extra features.
- It is not required that your admin client have the capability to
shut down your server, but it would be nice. Send the token "shutdown"
from your JokeClientAdmin to shut down your server. There is a catch,
however. Your request will be processed by an AdminWorker thread. By
the time you get to your worker, your JokeServer main listening thread
will be blocked waiting, and your JokeAdminServer main listening
thread will also be blocked waiting. Neither will automatically wake
up so that they can be shut down. Even if you change the loop control
variable for each loop to false, these servers will not notice it
until you wake them up with a request. To see a way of gracefully
shutting down your admin server loop within the worker dialog and "reaching
back" to kill off the parent that called the admin worker thread see the
"HostServer" assignment. Now, how can you kill off the main Joke
listening thread?
- Make your client and server somewhat fault-tolerant by writing the
state to disk after each request at both the client side and the server
side, so that if either crashes, on restart they read the state back in from
disk before resuming operation. You will need to identify your user to the
JokeClient in this case, and the user will need to have a unique user
name, or be verified by a unique ID (email address?)
- Hard: Modify your client and server so that they use a secure channel for
JokeClientAdmin via TLS.
- Hard: See the associated extra credit project for creating an
asynchronous JokeServer Client, possibly that also connects to
multiple JokeServers.