lu.se

Datavetenskap

Lunds Tekniska Högskola

Denna sida på svenska This page in English

A Chat System: The Server Side

Objectives

The main objective of this exercise is to build a chat system enabling a group of participants to discuss in real time. The exercise is divided into two sessions. The objectives of the first session are to:

  • Understand connected communications
  • Create a TCP server
  • Create a multithreaded TCP server
  • Understand threads and thread coordination
  • Write a program creating and running multiple coordinated threads to read and dispatch messages (consumer/producers)

Summary of the Work

Each group will have to:

  • Build a chat system that will enable users to connect to a server and to discuss in real time with all the connected participants;
  • Write a TCP server program that will receive the messages;
  • Create server threads that will handle the messages;
  • The complete implementation will span two sessions.

Reading Assignments

Read the following chapters from the book Java Network Programming by Elliotte Rusty Harold:

  • Chapter 4 on Java input/output streams.
  • Chapter 5, pp. 105-123, on threads.
  • Chapter 9, pp. 275-306, and chapter 10, pp. 325-341, on TCP programming.

Read the description of the Thread class of the Java API.

Programming: TCP

Creating Connected Communications Using TCP

In this exercise, you will implement a connected server using the TCP protocol (EchoTCP1.java). The server will just echo a message until the user decides to disconnect.

  1. Define a protocol for the echo service.
  2. Use the ServerSocket class of the java.net package to create a connected service and bind it to the port number 30000 of your computer.
  3. Write a loop that accepts clients. Use the accept() method to receive a connection from a client and assign it to a Socket object.
  4. Identify the client using the getInetAddress() method of the Socket class and display it on the server screen.
  5. Use the getInputStream() and getOutputStream() methods of the Socket class to obtain the socket streams.
  6. Write a loop to read the client requests:
    • Read the client message using the read() method of the InputStream class and display it on the server screen.
    • Use the write() method of the OutputStream class to send a response to the client. To get the bytes from a String object, you can use the getBytes() method.
    • When the connection is finished, close it.
  7. To connect to the server, use telnet with the syntax:
    $ telnet machine port
    Telnet will then pass all the characters to the server.
  8. Does the server accept more than one client at a time? Why?

Creating a Concurrent Server

Your first TCP server could not handle more than one client at a time. To process parallel requests, each client must be paired with one thread of execution on the server.

In this exercise, you will improve the TCP server and write a new program (EchoServerTCP2.java) so that it can handle concurrent communications.

  1. In the server code, determine which part of the program answers the clients. How to run it in parallel?
  2. Encapsulate this part of code in a separate class derived from Thread.
  3. Modify the remaining code so that each time there is new connection, the server starts a new thread.
  4. Start the server and two or three clients. Run netstat -a. Observe the result and explain it.
  5. Try to launch a second server. Observe the result and explain it.

Programming: Thread Coordination

With the previous server, a client could not send messages to all the other participants (all the other clients). You will add this feature to your server and build a communication device between participants using threads and a shared structure. However, a major problem with threads is to coordinate them so that do not interfere with each other. The goal of this part is to coordinate threads to read and dispatch messages.

In this session, you will review coordination mechanisms step by step. You will integrate the resulting methods in the server in the second laboratory session.

Creating Threads

In this exercise, you will create and run concurrent threads.

  1. Write a thread class that writes its name on the screen five times. Derive your class from the Thread class of the Java API and overload the run() method.
  2. Write a program that creates ten threads with a name and launches them.
  3. Insert a sleep() method in the loop of each thread with a random timing. Use the Math.random() method and observe the writing sequence.

Concurrent Access

Instead of writing their name on the screen, the threads will write it in a mailbox. Another thread will read the names and print them on the screen. The mailbox will be a Java object consisting of a data store to hold the message and access methods to write the message and withdraw it.

  1. Create the mailbox class that threads will use to deposit a message and remove it. It consists of an instance variable to store a message with access methods to assign a value to the string and to read and clear it. Write a class with a String instance variable and two access methods. The first one will assign a value to the variable. The second one will erase and return the variable.
  2. Create a thread class so that it deposits five times its name in the mailbox (writes its name in the string variable). Each thread will sleep a random time between each turn. You will pass the mailbox object to a thread as an argument of its constructor.
  3. Create a thread class that clears the mailbox (reads and empties the string variable) and prints the content on the screen. The thread must run – loop – infinitely. This thread will also sleep a random time between each turn.
  4. Write a main program that creates the mailbox, ten writer threads, one reader thread, and launches the threads.
  5. Observe the results and comment on them briefly.

Synchronizing Threads

In this exercise, you will coordinate threads so that no message is lost.

  1. Write new conditions in the mailbox access methods so that you are sure the threads will deposit and clear all the messages. Use the wait() and notifyAll() methods.
  2. Observe the results.

Improving the Mailbox (Optional)

In this exercise, you will improve the mailbox so that it can contain one or more messages.

  1. Replace the string instance variable with a buffer of five strings. You may implement the buffer as a Vector.
  2. Modify the access methods so that they can manage the new mailbox.
  3. What are the benefits of this new mailbox?

Improving Throughput (Optional)

Adjusting priorities may help optimize throughput. You will modify them in the program with the simple mailbox (one-place mailbox) and report for a given priority of the reading thread, the maximum number of writing threads your machine can handle.

  1. Add a counter to the writers to count the number of times the writer threads execute the wait() method.
  2. Why is it preferable to have less wait()s and how to avoid them?
  3. What should be the reader thread's priority to minimize this counter.
  4. Determine for different priorities of the reading thread the maximum load in terms of number of writing threads on your machine. You will remove the pause (the call to sleep()) between each turn. As a hint, the reading thread should be able to clear all the messages in less than 5 seconds. You can try first Thread.MIN_PRIORITY for the reading thread and Thread.NORM_PRIORITY for the writing threads.