Devin,
When I first started with the TCP/IP stack I tried making the data structures for the sockets look like their Unix counterparts. I quickly abandoned that and file descriptors because of the overhead.
The code to read a socket looks almost exactly like a send or receive call in Unix, but they are always non-blocking. Even in the case where data is available the caller is responsible for calling send/recv until they send all of the data they put in a buffer or receive enough data to move on. (Even on a Unix system it is possible for send or recv to process only part of your payload, thus forcing you to loop until everything you asked to send or receive is done.)
The keyboard input is non-blocking as well.
In general, the main loop of one of my mTCP programs is structured like this:
while ( !done ) {
Call the TCP library to see if any data has arrived at the Ethernet card
Check for open sockets that might have closed
Check open socket connections for received data and process it
Check the keyboard
}
The call to the TCP library is another polling operation. It checks for newly queued packets from the Ethernet packet driver, and then pushes them through the TCP stack. This makes incoming data available at the specific receive buffer for each socket before the user does a recv call to read it.
I thought about hooking the timer interrupt to do that processing automatically in the background, but it makes things slower because I would have to add more locks to ensure that two pieces of code weren't touching the same data structure at the same time.
In this scheme the programmer gets to control how aggressively the poll the adapter for data, but the programmer also is responsible for not forgetting to check for new data.
The telnet client is pretty simple. The telnet server I did for the telnet BBS earlier this year has to multiplex up to 10 different sockets, and maintain the state of 10 different client sessions. The polling overhead can get quite large there. I've thought about experimenting with a 'select' call that blocks until something actually happens as opposed to constantly polling all open file descriptors, but that is the only application with that problem so I've not implemented it yet.
Mike