8.1 Updates:
  1. 2023-10-07 Minor clarification on No Secondary Server message.

Multi-process, Multi-threaded Joke Server and Client

Copyright (c) 2023 By Dr. Clark Elliott with all rights reserved.

Overview:

Joke-checklist <-- Download and complete the JokeServer checklist. Submit to D2L along with your Java source code.

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 multi-processing 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. Although we will only be testing with from 1 to 5 clients at a time, your program should be designed to run thousands of worker threads at once.

Note that we will not make these servers thread-safe, which is beyond the scope of this assignment. However, you are free to do so if you have the expertise and wish to do so.

Administration:

  • Supplemental links:

  • Submission files for JokeServer ( precise names are rquired ):
    Joke-checklist.html
    JokeServer.java.txt ← Copied from JokeServer.java. Contains the code for three separately
    running processes: JokeServer, JokeClient, and JokeClientAdmin. Also included will be your JokeLog.txt and
    your Postings files as Java comments at the bottom You will likely have to copy your file from the command
    line. (On Windows, use copy. On Mac/unix, use cp).
    
    Executable files generated: JokeServer.class, JokeClient.class and  JokeClientAdmin.class
    

  • Additonally, you will create a JokeLog.txt file, then insert it as Java comments at the bottom of your JokeServer.java (and thus JokeServer.java.txt) file.

  • Complete the ColorServer first. You are free to use that code as a basis to start this assignment. Note: you may not copy any other code, including InetServer code, or you will be guilty of plagiarism. WRITE YOUR OWN CODE.

  • Update your Joke-checklist as you make progress on the JokeServer assignment.

  • There is a high priority on our being able to download your JokeServer.java.txt file, compile JokeServer.java from it, and run your three java applications (JokeServer, JokeClient, JokeClientAdmin) without any complications.

  • Make sure that your program will compile when we issue "javac *.java" (twice) at the command line prompt from within the directory where the java code resides. For this class, for grading purposes, NEVER use java packages. That is, the package statement should not appear anywhere in your source code.

  • Submit to D2L well before the deadline.

  • Be sure to include the required java header file as comments at the top of of your java file.

  • 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.

  • JokeLog.txt (Will later be inserted at the bottom of JokeServer.java as Java comments.) Capture the console output from your running programs so that the output—showing your working processes in each of the three terminal windows—can be placed in your JokeLog.txt file. Manual Copy and Paste from the console is probably the easiest scheme, and exactly what I suggest. 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—one for each conversation—for a theoretically unlimited number of Joke Clients; you have to connect from two different kinds of clients (Jokeclient and JokeClientAdmin); and so on. Each of these steps may take some thought, so leave plenty of time to get this assignment finished.

  • Allow yourself enough time to prepare the D2L submisison of your work exactly as specified. This may take some thought and some experimentation the first time you do this. For example, your programs must compile and run from the command line. You should NOT assume that you can get this done at the last minute. And, you must get a TII report.

  • I strongly recommend that you complete this assignment incrementally: Make small additions to the code at a time, while always maintaining a running program.

  • Read this whole assignment page before you get started so you know where all the hints, links and explanations are.

Suggested Development Order:

  1. 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. You can use the checklist as a guide, and should ypdate your checklist as you complete steps. Comment your code during development .

  2. Starting with your running ColorServer code: (a) rename the client and server classes to JokeServer and JokeClient, (b) remove the ColorServer application code that selects and sends colors (but leave the serializing and network code in place, leave in the basic client and server loops, and the network connection and serialization architecture. Finally, (c) modify the code to return a single joke from the your server as soon as the client connects.

  3. Copy your JokeClient class code to a JokeClientAdmin class. Change the port at which it is connecting to the server. Modify your JokeServer to run a second thread in a server loop listening for JokeClientAdmin connections (see example code below). Connect from the admin client and automatically toggle the server mode between joke and proverb on each connection. Display on both the server console and the admin client 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 Mode Changer Method as one way you might want to do this.

  4. You can see how to generate three executable, named .class files fron one Java source file from this three executables from one Java file code.

  5. Return either a single joke or a single proverb to the JokeClient depending on the server mode.

  6. At this point you should be presenting console output in each of your three terminal windows, with regular announcements about what your three process are doing. Later, using Copy and Paste, you will copy this output to your JokeLog.txt file.

  7. 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.

  8. At the end of each joke cycle and proverb cycle, announce on the console "JOKE CYCLE COMPLETED" or "PROVERB CYCLE COMPLETED."

  9. Connect from multiple Joke Clients and verify that the previous step works for each client, independently without interference from other clients. THIS IS IMPORTANT. You will need to have a separate data structure saving each conversation—one for each Joke Client. In theory you should be able to support 10,000 independent conversations with your code.

  10. 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, JD, PC, PD, JA, JB, PA...

  11. 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.

  12. 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 on the class D2L forums is encouraged. Example: "JA Joseph: Why did the chicken cross the road? To get to the other side!"
  13. Re-randomize the jokes, and proverbs for each client conversation at the start of each four-item joke and proverb cycle. (The J and P labels stay with the jokes.) Veryify that the randomization of jokes and proverbs for one client conversation still does not interfere with any other client conversation. Because randomizing makes development confusing, do this toward the end.
  14. Go through your entire program to verify that ALL specifications are met regarding file naming, comments, program logic, console input, and output.

  15. Make sure your JokeClient and JokeClientAdmin processes 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/Mac.]

  16. 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.

  17. 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.

  18. 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.

Joke-postings:

You are required to make at least two scholarly postings in the D2L discussion forums JokeServer thread. A scholarly post can be any of, e.g., a discussion thread you start on design aspects of the JokeServer or clients and servers in general, bragging rights for some interesting extra feature you plan on implementing, a well-formed question about how to implement or design the JokeServer, bigger-picture ideas about scale-up design considerations, or data-protection considerations. You can include annotated URLs as part of your discussion. Favorite topics might have to do with discussions about maintaining conversation state. Or, you can reply to any of the above that others have posted. Answers to coding or design questions are always a big hit. Postings that initiate or take part in discussions are always more highly prized. Submit your two (or more) postings with your Java code—at the bottom of JokeServer.java as Java comments.

Multi-threaded Joke and Proverb Server:

JokeServer Conventions

  • Communication between client and server must be via serialized Java objects, as was done in the Color Server. You are not allowed to use InetServer code.
  • Use "localhost" or "127.0.0.1" as the default IP address/domain name location for your JokeServer. Use 4545 as the default port for your JokeServer, and 4546 for the secondary port.

  • The JokeServer and JokeClients must run (in perpetual joke mode) without ever starting the JokeClientAdmin process. It is required that we be able to start and stop the JokeClientAdmin at any time without affecting the running of the system. When we stop either of the running state-maintaining processes (either JokeServer and any JokeClient) the system becomes undefined, which means you don't have to worry about it for this assignment. If you gracefully shut down your system that is nice, but you can also simply let it blow up.

  • We must be able to start your clients and servers in any order .

  • If your server or client has odd behavior, print a note about this on the console at startup, and discuss it in the notes at the bottom of your checklist.

  • Use strictly clean, inoffensive jokes and proverbs please. Be respectful to the grader and your professor.

  • Use the following conventions for command-line arguments to your JokeServer, JokeClient, and JokeClientAdmin processes. Note that some of you may not complete the final step of JokeServer (not a big deal...) in which case you will not have a secondary server, and will not need to deal with any command-line argumnets for the secondary server.

    1.  > java JokeServer [secondary]
      Runs at port 4545 by default, and at port 4546 if "secondary" is indicated (no square braces typed!).
    2.  > java JokeClient <IPaddr> <IPaddr> 
      
      If a first argument is present, the client will connect to the server at that IP address or domain name, otherwise it will use the default of "localhost" or "127.0.0.1"

      If the second IP address or domain is indicated the client allows the user to switch between primary and secondary servers, possibly at different IP addresses.

       > java JokeClient localhost localhost
      
      ...would connect to the local host (127.0.0.1) at ports 4545 for the primary server, and 4546 for the secondary server.

       > java JokeClient localhost 140.192.1.9
      
      ...would connect to port 4545 on the local host (127.0.0.1) for the primary server, and port 4546 on 140.192.1.9 for the secondary server.

    3.  Java JokeClientAdmin <IPaddr> <IPaddr>  
      If a first argument is present, the client will connect to the server at that IP address or domain name, at port 5050, otherwise it will use the default of "localhost" (127.0.0.1) at port 5050.

      If the second IP address or domain is indicated the client allows the user to switch between primary and secondary servers, possibly at different IP addresses. The administration port for the secondary server is 5051.

  • It is required that your JokeServer[s], JokeClientAdmin, and JokeClient[s] can be started in any order.

  • The JokeServer starts in Joke Mode by default. The JokeServer and JokeClient[s] will run without the JokeClientAmin being started.

  • If any JokeClient process is stopped, or the JokeServer is stopped, the system becomes undefined (any behavior is fine, including the whole system blowing up).

  • The JokeClientAdmin process can be started and stopped at will without otherwise affecting the client[s] and server[s].

  • All communications from the secondary server are preceeded by the five characters "<S2> " ←the last character is a single space.

  • Use the following JokeClient and JokeClientAdmin conventions:

    1. At startup, print the IP address and port number of the server being used on the client console. If you are using a secondary server, then print both IP addresses and ports being used:
       Server one: localhost, port 4545 Server two: some.other.ip.net, port 4546 
    2. When using a secondary server, client console or client admin console input of "s" means toggle from one server to the other: switch from primary to secondary, or secondary to primary. Print the IP address and port number of the current server whenever a toggle has occurred. When you have not written code for a secondary server, input of "s" means print the message, "No secondary server being used" on the console.
      Now communicating with: localhost, port 4545
      Now communicating with: localhost, port 5050  [<-- for the Admin client]
      
    3. Use "quit" to exit from the client.

    4. For both types of clients, simply pressing the <Enter> key will take us to the next state (get a joke or proverb / toggle the server mode). So, to see your system run we can simply keep pressing the <Enter> key on any of the client consoles.

  • For the JokeClient use the following additional conventions:

    1. Get the user's name before entering the server query loop.

    2. <Enter> means connect to the current server and request a response. If you are having difficulty with empty console input, then allow us to enter any string other than "quit" to get a response, including a single character such as "x". But you MUST tell us this is what you are looking for.

  • For the JokeClientAdmin use the following additional conventions:

    1. The JokeClientAdmin connects to the primary JokeServer by default whenever it is started.

    2. <Enter> means connect to the current server and tell it to toggle between JokeServer modes: Joke Mode switches to Proverb Mode, and Proverb Mode switches to Joke Mode. Print the current mode on the console.

    3. The JokeClientAdmin connects to both the primary server at port 5050 and the secondary server at port 5051.

  • Jokes have the following format:
     JA <username>: <joke body> 
    ...where "JA" can be either JA, JB, JC, or JD. Similarly, proverbs have the same format, except with PA, PB, PC, PD. This will require that you use the Java string processing libraries to splice together such strings.

  • Comments are an important part of your grade. Comment your pedagogical code as you develop. Never plagiarize your comments!

  • You will need to run the JokeServer, JokeClient, and JokeAdminClient in different command windows as you did with the ColorServer.

  • Joke label randomizing: The labels (e.g., JA, JB) stay with the joke or proverb. In other words, when the jokes get randomized, the lables do too.

  • Joke / proverb interleaving: Suppose you are in Joke Mode and have seen two jokes. Then you swith to Proverb Mode. At this point, the joke state goes into "suspeneded animation"—it is frozen in time. When you later return to Joke Mode, you pick up exactly where you left off (e.g., the third joke in the sequence) without any concern whatsoever about what happened in Proverb Mode. And, the same is true for proverb state.

JokeServer Detailed Specifications:

  • Make sure your JokeClient asks for the user's name [to be used later] before it enters the request loop. We should never have to enter the user name more than once per process.

  • Inside of the loop, when the user presses <enter> this signals a connection to the server. When your JokeClient connects to your JokeServer this initiates an algorithmic process such that, ultimately, the next joke or proverb is displayed on the console of the JokeClient. Whether it is a joke or a proverb depends on the current mode of the server.

  • Build a template of exactly four jokes and four proverbs for use in your client/server system. 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]. The letters always stay with the same joke or proverb through the randomizing step.

  • 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 members of the jokes set or of the proverbs set have been returned, then print a console message that the joke or proverb cycle is complete, and 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 toggles it to Proverb Mode , or Joke Mode for ALL subsequent client connections within the respective conversations.

  • The username must appear (as specified above) as part of each joke or proverb JokeClient console message. Additionally, the username must appear on the server console as well, each time a client request is made (e.g., "New client request from user Joseph").

  • 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 in that cycle have been seen by the client, at which point the cycle (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 random access to an array? Note: efficiency in your implementation is desirable, but will not affect your JokeServer grade. Comments in your code?

  • 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 thread call in your server, initiating a separate thread, to start a second server listener waiting for JokeAdminClient 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 (because we are writing explicitly for a connectionless protocol). But this will require some thought before you begin development. I recommend that you write out your design with pencil and paper before beginning to write computer code.

    The general problem to be solved is that having established a client/server conversation (i.e., a history of your previous interactions), then broken the TCP/IP connection between client and server, how do you retrieve that same conversation next time you connect? (Remember that there may be 2,000 simultaneous conversations going on.)

  • You have many ways that you can implement state maintenance. For example:

    1. MINIMAL COOKIE ON THE CLIENT; FULL STATE ON THE SERVER: You can maintain the state of the conversation 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 (e.g., in an array of conversation-state objects). 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.

    2. 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 all on 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.

      Or, because the only thing you actually need from the server, is what mode it is in, you can simply send the username to the server (to display on the server console), and get back what mode the server is in, then do EVERYthing else on the client.

    3. 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.

    4. NOTHING ON THE CLIENT: It is not possible to implement the JokeServer this way. Why not?

    5. See joke-state.html for some additional pointers on maintaining the state of the client.

Some further notes on state maintenance:

  • There are other design elements to consider: For example, suppose you want to store everything, including the jokes and proverbs on each of your 50,000 clients. What happens if you hear from your legal department that one of your jokes is now considered libelous and must be replaced? With this model you have 50,000 jokes to retrieve and replace, including on the 5,000 clients that will not be connected anytime soon, right? Ouch!

  • 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, as noted above, 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.

  • 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 (such as an incremental array index) 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.

  • FOR THE TWO-SERVER MODEL: When extending our client / server system to use multiple servers (see below), we have additional design concerns. Each client will now have two conversations going—one with each server. If you use the state-maintenance model of storing everything on the server, and only a UUID/cookie on the client, then you only need to pass the same UUID to either server, and you'll get put back in the right conversation from each respective server. If, by contrast, you store everything on the client, you'll have to maintain two conversation-objects on your client, and send the right one to the right server each time.
  • 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:

  • Once you have completed the basic single-server model, continue as follows. This portion of the JokeServer assignment is only worth ten percent, so it is not that big of a deal if you don't complete it.

  • All of the above functionality remains the same. If no arguments are passed to the server, or the clients, everything MUST continue to work exactly as above, which is the default mode of operation.

  • Your secondary server—using exactly the same code—will run in its own process in a separate command shell but communicating via different ports.

  • Modify your JokeServer to accept an argument, the token "secondary":
     
    > java JokeServer secondary
    
    When your JokeServer code detects this argument, it starts the server listening for JokeClient connections at port 4546. (See the ColorServer for an example of accepting command line arguments.) Print on the console that this is the secondary server. JokeClientAdmin connections are at port 5050 for the primary admin server, and 5051 on the secondary admin server.

  • All communications from the secondary server to the JokeClient are preceded by the four characters (plus a trailing space):
    <S2>
    
    So, for example, a proverb would look like this:
     <S2> PD Joseph: The early bird gets
    the worm.  

  • The two servers are completely independent from one another, and all client conversations between a server and a client are independent of conversations between that client and the other server.

  • Modify your JokeClient and JokeClientAdmin classes so that when they are run as independent processes and 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.

  • Hint: If you store the entire state of the conversation on the server and pass a "reminder token" (cookie) to the server to go find your state when you connect, you only have to pass the same token to the second server, and everything else is done. If you store the entire state on the client, you only have to keep two track of two conversation states on your client, and send the right one to the right server when you connect. Other mechnisms will be about as easy, so (don't tell! but...) it is not that hard to add the second server.

Execution and JokeLog.txt:

  • 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 JokeOutput.txt log file.

  • 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. NEVER alter any of the output data, but you can add some white space and some annotation headings to make it easier for us 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 us know about it in the checklist comments section, and post your interesting work on the forums! Be SURE 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.

  • Any cool thing you feel like doing, and telling us about.

  • It is not required that your JokeClientAdmin 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[s]. There is a catch, however. Your request will be processed by an AdminWorker thread. By the time you get to your JokeWorker, your JokeServer main listening thread will be blocked waiting, and your JokeAdminServer main listening thread will also likely be blocked waiting. Neither will automatically wake up so that they can be shut down. Even if you change a loop-control variable for each loop to false, these servers will not notice it until you wake them up with another request. To see a way of gracefully shutting down your admin server loop with a single connection, 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: If it is available see the associated extra credit project for creating an asynchronous JokeServer Client, possibly that also connects to multiple JokeServers.