The echo server from the previous project is very simple, and yet you may have noticed that synchronization issues can creep in if your logic is faulty. For example, it is possible for a client and server to be deadlocked, each waiting on the other to write something. Race conditions are also common, since a client may disconnect before the server is done echoing, etc.
For this next project, you'll need to handle more advanced synchronization. You may use any language and any synchronization mechanisms you want (mutexes, semaphores, monitors, messages, etc).
Implement a TCP/IP broadcast hub. Your hub should accept any number of connections from clients. Some clients will connect and simply continuously read until the socket is closed. These clients are called "subscribers". Other clients will connect and write messages on the socket. These clients are called "publishers". It's also possible that a client will both read and write (in any order). We'll call those "chatters".
Whenever a client writes to the server, the hub should broadcast the message to all connected clients. This includes the client that sent the message.
While seemingly simple logic, the synchronization issues involved in this project are rather complex and are typical of real-world programs. You will need to use threads to handle multiple clients at the same time. Each such thread is called a "request thread". You will need to synchronize access to common resources among these threads. For example, you could have a shared list of sockets, one for each currently connected client. Then you'd need to synchronize access to this list.
The fact that reads and writes can block will cause some problems you will need to solve. The problems stem from the fact that the server cannot tell beforehand whether a newly connected client is a publisher, subscriber, or chatter. If the server starts reading from a subscriber, the request thread will block forever, since subscribers never write anything. Likewise, if the server tries to write to a publisher, the request thread will block forever, since publishers never read anything. Even worse, chatters may read and write in any random order, so race conditions will abound.
Probably the best way to solve this problem is to use the "select" function. It will let you test whether a client socket is ready for reading or writing before you actually call read or write. This allows you to prevent blocking. Another way is to use non-blocking sockets, which can be configured using the "ioctrl" function (this is not recommended). Another way is to spawn two separate threads for each socket: one which only reads and one which only writes. This way the reading thread can block while the writing thread runs, and vice versa, without deadlocking. It is up to you to come up with an approach best suited to your language and platform.
When you get things working, your hub can be used in several interesting ways. The most obvious application is an old-fashioned chat room. It wouldn't be hard to implement IRC on top of this. The same logic is also used in PubSubHubbub, CloudRSS, Usenet, BBSes, DDS, or any other publish-subscribe protocol.
As usual, you should write a Makefile to build and test your project. You will need to launch at least two "chatter" clients to test your server's ability to handle multiple connections without deadlocking. Something like this should work:
A chatter should work something like this: