Typically I maintain a list of structures where each represents a session. For each pass through the "select loop" I set the readfd corresponding to each connected session and call select(). When select returns, I loop across each session structure to see if the select indicated data ready and read the data if it's there. Similar to what you are doing.
A potential issue is that the data received may not be complete, or it may have multiple complete "things." If, for instance, your application is expecting zero terminated strings as a "thing" the data read may contain multiple things with one partial thing at the end. You will have to write code that saves the partial thing and joins it with the first part of the next data read from that session. Not difficult, just tedious.
Checking to see if a write() will block is nearly the same as checking for data ready. Set the fd list for each session you want to write to and then invoke select(). Test the write fd list for each fd, and any that is set can be written to without blocking.
Similarly to piecing back together partial buffers when reading, if you don't want the write to block, you'll need to save the data to be sent in a queue, and manage sending it at some future time when the session isn't blocking. Again, not difficult, but tedious to implement.
Getting your hands dirty with this level of socket programming is good experience, but if you're looking for something that might handle this layer of the communications for you, check out 0MQ (Zero-MQ):
The Intelligent Transport Layer - zeromq