views:

635

answers:

2

Lets say I have a server program that can accept connections from 10 (or more) different clients. The clients send data at random which is received by the server, but it is certain that at least one client will be sending data every update. The server cannot wait for information to arrive because it has other processing to do. Aside from using asynchronous sockets, I see two options:

  1. Make all sockets non-blocking. In a loop, call recv on each socket and allow it to fail with WSAEWOULDBLOCK if there is no data available and if I happen to get some data, then keep it.

  2. Leave the sockets as blocking. Add all sockets to a fd_set and call select(). If the return value is non-zero (which it will be most of the time), loop through all the sockets to find the appropriate number of readable sockets with FD_ISSET() and only call recv on the readable sockets.

The first option will create a lot more calls to the recv function. The second method is a bigger pain from a programming perspective because of all the FD_SET and FD_ISSET looping.

Which method (or another method) is preferred? Is avoiding the overhead on letting recv fail on a non-blocking socket worth the hassle of calling select()?

I think I understand both methods and I have tried both with success, but I don't know if one way is considered better or optimal. Only knowledgeable replies please!

+1  A: 

I would recommend using overlapped IO instead. You can then kick off a WSARecv(), and provide a callback function to be invoked when the operation completes. What's more, since it'll only be invoked when your program is in an alertable wait state, you don't need to worry about locks like you would in a threaded application (assuming you run them on your main thread).

Note, however, that you do need to enter such an alertable wait state frequently. If this is your UI thread, make sure to use MsgWaitForMultipleObjectsEx() in your message loop, with the MWMO_ALERTABLE flag. This will give your callbacks a chance to run. On non-UI threads, call on a regular basis any of the wait functions that put you into an alertable wait state.

Note also that modal dialogs generally will not enter an alertable wait state, as they have their own message loop which doesn't call MsgWaitForMultipleObjectsEx(). If you need to process network IO when showing a dialog box, do all of your network IO on a dedicated thread, which does enter an alertable wait state regularly.

If, for whatever reason, you can't use overlapped IO - definitely use blocking select(). Using non-blocking recv() like that in an infinite loop is an inexcusable waste of CPU time. However, do put the sockets in non-blocking mode - as otherwise, if one byte arrives and you try to read two, you might end up blocking unexpectedly.

You might also want to consider using a library to abstract away the finicky details. For example, libevent or boost::asio.

bdonlan
Thanks. For this implementation, I do not want to use Overlapped IO, but thanks for the suggestion. I will look into that. I understand your suggestion to use non-blocking mode AND select(). My main question was should I call recv for no reason, or go through the motions of calling select(). Thanks for the reply!
JPhi1618
updated it a bit to be clearer - just looping through with `recv()`'s going to peg the CPU at 100%, which is never a nice thing.
bdonlan
A: 

the IO should be either completely blocking with one thread per connection and in this case the event loop is essentially an OS scheduler or the IO should be completely non-blocking, and in this case select/waitformultipleobjects-based event loop will be in your application

All intermediate variants are not very maintainable and error prone

Completely non blocking approach scales much better when the amount of concurrent connections grows and does not have a thread context switch overhead, so it is a preferrable where the number of concurrent connections is not fixed. This approach has higher implementation complexity compared to completely blocking one.

For a completely non-blocking IO the core of the applicaiton is a select/waitformultipleobjects-based event loop, all sockets are in non-blocking mode, all reads/writes are generally done from within event loop thread (for top performance writes can be first attempted directly from the thread requesting the write)

bobah