CSocket Considered Harmful

by Warren Young

If you’re an MFC user, you’ve probably thought about using the library’s CAsyncSocket class or one of its derivatives. Unfortunately, the entire hierarchy has problems that you should know about before you start writing code.

CAsyncSocket

CAsyncSocket is a bare-bones asynchronous sockets wrapper. There are now two examples in the FAQ that show straight-to-the-API and CAsyncSocket-based clients.

If you download and study the examples, you can see that the CAsyncSocket version isn’t much smaller, and it has a couple of problems. First, CAsyncSocket does synchronous DNS lookups, so a slow DNS server can cause a program to block for a very long time. Second, you can’t derive a class from both CAsyncSocket and CWnd, because they have a base class conflict that the compiler can’t resolve. This means you can’t set up network timeouts, among other things. This is a design mistake: CAsyncSocket should have been derived from CWnd, because it receives window messages from Winsock. (That would let you have Window timers, too.) Instead, CAsyncSocket creates a separate window to handle these messages.

CSocket

With CSocket the trend goes from mediocre to bad.

CSocket is a subclass of CAsyncSocket that "fakes" blocking I/O using asynchronous sockets by running a small message pump every time it gets a WSAEWOULDBLOCK error. This is technically cute, but it can cause reentrancy side effects. While CSocket is "blocking", it’s pumping window messages, which allows a message to trigger another call to the blocking CSocket object. The full list of similar and related problems is left as an exercise for the reader.

CSocket has outright bugs, too: CSocket tends to crash or raise assertions when used in nontrivial situations. Who ever heard of a network app worth using that didn’t get pushed beyond its original design specs?

The most damning criticism of CSocket, though, is that true blocking sockets are easy to come by: Winsock blocking sockets are dead easy to program. Why would you choose fake and buggy blocking sockets when you can get bug-free blocking sockets that are less complex to boot?

CCESocket

CAsyncSocket and CSocket don’t work on Windows CE because it only supports Berkeley-style blocking and nonblocking sockets. Microsoft didn’t want to give up asynchronous notification, so they committed designicide once again: there’s now a subclass of CSocket (!) called CCESocket that emulates asynchronous notifications using a pair of worker threads and blocking sockets.

Conclusion

Let’s summarize the story: First we had asynchronous sockets. Then we added a layer on top to make those asynchronous sockets look synchronous. Then we added a layer on top of that that uses synchronous sockets — not the base class’s fake synchronicity but this time real Winsock blocking sockets — to mimic asynchronous sockets. This last layer must override almost all the base class functionality! Clue to Redmond: it’s time to lay off the large lattés.

CSocket breaks the “is a” rule of good object oriented design. With this hierarchy, Microsoft is saying, “a blocking socket is an asynchronous socket, and a sorta-asynchronous thread-based socket is a blocking socket, but not necessarily the reverse.” I feel like I’m watching The Three Stooges...

The only class in the hierarchy that’s at all usable is CAsyncSocket, but usable isn’t the same thing as useful. If my needs are simple, it’s nearly as easy to write straight Winsock API code. If I have tough networking problems to solve, I use an intelligent class library that does more than simply wrapping Winsock. Such wrappers offer features like buffering and dissection of TCP streams into logical packets. (You can find several on the Libraries page.) Given these resources, why would I ever want to use CAsyncSocket?

The cause of this design mess is obvious to any student of Microsoft design history: they started with a usable idea, and then repeatedly added layers to it without considering whether the new layer actually fits conceptually within the existing framework. Refactoring is a foreign concept to Microsoft. At best they’ll deprecate an old interface, but even that’s usually a toothless threat. How many deprecated and never-documented Win16 APIs are in Win2000?

Bad design is a sign of messy thinking. That’s not a helpful trait for someone writing foundation libraries.


<< WsControl() Revealed
Passing Sockets Between Processes >>
Updated Mon Sep 22 2014 20:57 MDT   Go to my home page