There’s a rather disappointing article on .Net sockets in this month’s MSDN magazine.
Updated 5th May 2023 to fix broken links
The title of the piece is “Get Closer to the Wire with High-Performance Sockets in .NET” which nicely over-hypes the content. How, exactly, am I getting closer to the wire by placing a managed layer between my code and the wire? The summary box is a little more honest; describing the article as covering “basic socket programming” and “factors in creating socket-based servers” which it does…
We start with a basic intro to sockets, moves swiftly to a thread per connection server which is swiftly put to bed due to the lack of scalability of such a design. That’s all well and good but the reasons given for the lack of scalability are the number of threads created and destroyed and the amount of memory taken up by each thread. No mention of the horrendous thrashing that you’d experience with 1000 threads all servicing clients. I’d have preferred to have seen a mention of this as otherwise the inexperienced might think that they can simply throw more memory at the problem.
Next we’re on to using
select which is, rightly, dismissed as being inefficient and prone to starving connections unless you’re careful…
Finally we move on to a simple asynchronous IO server which is described as removing the number of connections limit of the thread per connection server and beating the performance of the
select based server. Which, I guess, is true. But then comes the bombshell. It’s still not that scalable… The best the author can get is 4 times the number of connections than the thread per connection server (and he doesn’t specifically say that this is with the async server rather than the
select server, but I hope it is…).
So because we “Get Closer to the Wire with High-Performance Sockets in .NET” we can handle, at best, 4000 incoming connections… I’m sorry but I do hope this is a limitation of the author or his hardware rather than the .Net runtime because I just ran up one of my simple C++ servers and established 30775 connections to it; and I’d file that in the “without trying” cabinet as there are several tricks that you can use to reduce the amount of non-paged pool that you use per active connection and, since it was a simple test, I wasn’t bothering with any of them. Of course this isn’t a fair comparison as I don’t have a clue how the author tested his server, and I have no way of knowing how much memory he had in the box (the amount of memory affects the amount of non-paged pool, and therefore the limit he was bumping into, my box has 1GB), but for ball park figures they’re worrying. The C++ echo server and simple connection opener that I used in the test can be downloaded from here; for best results run them on different machines.
Moving on the article discusses new features in .Net 2’s socket classes; basically full support for
AcceptEx. But, unfortunately, only a cursory mention of the potential denial of service attack that can come from accepting and receiving when a malicious client doesn’t bother to send any data. I’m assuming you can access the accepted time on a socket via socket options so that you can protected against this. .Net 2’s sockets also include support for
DisconnectEx so that you can reuse your sockets. The author notes that queuing multiple accepts eats into the maximum number of connections that his server can achieve, but doesn’t say if it’s a one to one trade off. Interestingly both features have been supported in the underlying WinSock 2.0 layer since before .Net 1.0 so it’s hardly that revolutionary to include support for them in .Net 2.0; they’re just making the wrapper a bit more complete.
Finally the author covers the new tracing features in .Net 2’s sockets; and you know what I think of tracing ;) Still, this kind of thing, in a framework library, could be useful.
The article ends by saying that “there are multiple approaches to improving the performance of socket applications. This article only scratched the surface of the full socket API.” Well, yes… There are multiple approaches and, it would seem, that staying with C++ is the one to use if you want to handle a reasonable number of clients. One of the things that constantly bugs me about the .Net hype machine is that I don’t think I’ve ever seen a .Net-head say something like “in this particular situation, if the performace of .Net isn’t good enough for you, you might consider the added complexity of using C++”… I’m sure there are plenty of places where there is no reason to use C++ to obtain better performance but, in this case at least, it seems there might be…
I really wish this article had been written by someone who really knows their stuff, like Ian Griffiths. In my opinion this blog posting contains much better coverage on Async sockets in .Net, though it’s not so hypey and it doesn’t mention .Net 2.0 because it’s pretty old. I’m always sceptical of articles that talk about performance without giving some figures to back up the claims and without providing enough details so that you can repeat the tests. And finally, I wish that the download could be built by the currently released version of Visual Studio rather than the latest beta. The code doesn’t actually seem to use the .Net 2.0 features so why is it built in VS2005betaX?
All in all a disappointing article from many perspectives.