CS360 Lab #9 -- Threaded Jtalk

  • Jim Plank
  • CS360
  • Url: http://www.cs.utk.edu/~plank/plank/classes/cs360/360/labs/lab9/lab.html

    Now we're going to turn jtalk into a threaded program. And we're going to beef the server up.

    First, look in the lab directory (/blugreen/homes/plank/cs360/labs/lab9/). There are two programs there. The first is jtalk.c. This is going to be the client for your jtalk. It requires a host and port number on the command line. It first asks for a user name, and then requests a connection to the socket at the host/port number. Once the connection is made, a separate thread is forked off. The The main thread reads lines from standard input, prepends the name to them and sends then to the server using send_string. You'll note that what send_string does is send the length of the string, and then the string itself. This makes it easy for the server to parse its input without having to look for newlines.

    The other thread (from_socket()) reads strings from the socket and prints them to standard output. It uses read_string() to do this. As you might guess, read_string() reads the length of a string and then the string itself.

    If either the socket or standard input is closed, then jtalk exits.

    Ok, now look at cat_server.c. This is a simple program that serves a socket, and then once it gets a connection, it simply shuttles all input from the connection back to the connection. You can test out jtalk by running cat_server on one machine, and then connecting to it with jtalk. Anything that you type in the jtalk window should come back with the name prepended to it. Make sure you understand everything about jtalk and cat_server before going on.

    jtalk_server

    Ok, now your job is to write jtalk_server, which is a threaded server for jtalk. Jtalk_server must use threads, and cannot use select(). Jtalk_server will take a host and port on the command line, and then it will serve a socket on that host/port. As soon as the socket is served, any number of jtalk clients may connect to it, and whatever a client types in should be printed by all the clients. If a client exits, or other clients join, everything should still work.

    Threads really make your life easier here. You should have one thread that calls accept_connection(), and whenever it gets a connection, it forks another thread that services that connection. Obviously, you'll need some data structure that is shared by all threads so that any thread may write to all of the connections.

    Note that you will need a mutex here protecting that data structure. This is because one thread may be writing to all the connections, and another thread may exit simultaneously. Think about it. Also think about what you need to do with sigpipe.

    Finally, you should have an extra thread that runs the ``jtalk console''. This thread prints a prompt to standard output, and then accepts commands on standard input. It really only accepts three commands:

    1. If standard input is closed, it should kill the server.
    2. If the command is ``TALKERS'', then it should print out a list of the people connected to the server. With each person, it should print the time that that person last talked.
    3. If the command is ``ALL'', then it should print out a list of every connection that the server has had. It should print out the name of the person connecting, when he/she started the the connection, whe he/she last talked, and if the person disconnected, when that occurred.
    There is a working executable in /blugreen/homes/plank/cs360/bin. Try it out before you start coding so that you understand exactly what is going on.

    Hints

    Obviously, you will need one thread that calls accept_connection(), one thread for the console, and another thread for each connection. I used a red-black tree keyed on the number of the connection (which is assigned whenever a new connection is made) for the current connection, and a second red-black tree for all connections, current and past.

    I never call pthread_join(), but I do call pthread_exit() when a connection goes away. Remember to close the file descriptor too.

    Use ctime() to print times.

    You will have to modify send_string() etc to deal with disconnecting correctly. However, you cannot change jtalk -- your server must work with the jtalk that I have provided.

    You may assume that if you catch SIGPIPE on a socket, that any read() calls on that socket will return instantly with a value of zero. This makes life much simpler if you structure things well.

    As usual, work in increments, and test incrementally.