For this program, you'll be doing something significantly different with your card dealer server. By now, you should have all the kinks ironed out in handling the cards (shuffling and dealing in a random order). Your server will still be using TCP to communicate with clients, but you will address the issue of how to deal with multiple client connections in a different way. Instead of trying to handle all of the clients from the main program, you will use Unix's ability to create new processes.
The main server process should spawn a child process each time it gets a connection request from a client. The child process is responsible only for handling the connection to the client, while the main server process is responsible for handling new connection requests and dealing the cards. For information on how to use fork to create a child process, see the section on Multi-client Servers in the Unix Network Programming Manual. I also have some more information about fork() from Unix Network Programming (both the first and second editions) by W. Richard Stevens.
Since the child is not doing any management of the cards, it will need to communicate with the main server process (its parent process) each time it receives a request. To do this, you will use pipes. For more information on pipes, see me for a section from from Unix Network Programming, 1st Ed. by W. Richard Stevens. The main program will now have to deal with its normal server socket (the one on which it calls accept()), plus multiple pipes to communicate with its children. Notice that the pipes are represented using integer descriptors; these descriptors can be used with select(), just like file descriptors or socket handles.
As an alternative to using pipes, the program can use stream pipes, which are similar but a little more convenient. I have some info from the second edition of the Stevens book about creating stream pipes. You are free to choose either IPC (interprocess communication) mechanism for your program.
The requirements for this program are thus the following:
Since the main server process needs to deal with its normal server socket, plus all the pipes that its children use to communication with it, you might suspect that this is not really saving that process any work. This is a true observation - for this particular application, the implementation using one process and the select() call is more efficient. However, this multi-processing approach has advantages when the work that must be done for each child request is more significant. Suppose for instance that each request requires a database query plus some length computations to generate the response. The use of multiple processes means that while some of them are blocked waiting on database query responses, others can be performing computations.
There is not a lot of design work to be done here, but you should plan how you will control the child connections;will you use pipes or stream pipes? If you use pipes, which one is the parent to child pipe and which goes the other direction? Make sure you are comfortable with how fork works, and figure out how control will flow through the program after the fork to make sure that one path runs the child code and one runs the parent code. Do this before you start hacking on code.
You should continue to refine the library of socket functions - add routines to the server code that allow you to use select to manage a set of sockets on the server side. Clean up any code that might need it for both the server and client.
You should include the following: