I have an application that makes a couple hundred TCP connections at the same time, and receives a constant stream of data from them.
private void startReceive()
{
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.Completed += receiveCompleted;
e.SetBuffer(new byte[1024], 0, 1024);
if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); }
}
void receiveCompleted(object sender, SocketAsyncEventArgs e)
{
ProcessData(e);
if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); }
}
My attempts led to something like this:
private async void StartReceive()
{
byte[] Buff = new byte[1024];
int recv = 0;
while (Socket.Connected)
{
recv = await NetworkStream.ReadAsync(Buff, 0, 1024);
ProcessData(Buff,recv);
}
}
The issue I had was the method calling StartReceive() would block, and not get to the accompanying StartSend() method called afterStartReceive(). Creating a new task forStartReceive()would just end up with 300-ish threads, and it seems to do so just by callingStartReceive()` anyways.
What would be the correct method of implementing the new async and await keywords on my existing code while using a NetworkStream so it is using the thread pool that Socket.SendAsync() and Socket.ReceiveAsync() are using to avoid having to have hundreds of threads/tasks?
Is there any performance advantage of using networkstream in this manner over i/o completion ports with beginreceive?
You’re changing two things at once here: the asynchronous style (
SocketAsyncEventArgstoTask/async) and the level of abstraction (SockettoNetworkStream).Since you’re already comfortable with
Socket, I recommend just changing the asynchronous style, and continue using theSocketclass directly.The Async CTP doesn’t give
Socketanyasync-compatible methods (which is weird; I assume they were left out by mistake and will be added in .NET 4.5).It’s not that hard to create your own
ReceiveAsyncTaskextension method (and similar wrappers for other operations) if you use my AsyncEx library:Once you do that, your
StartReceivecan be written as such:Now, to address many minor points:
awaitdoesn’t spawn a new thread. I wrote up an async/await intro on my blog, as have many others.async/awaitallows concurrency, but that doesn’t necessarily imply multithreading.async/awaitis not a brand new form of asynchronous processing; it’s just an easier way to express asynchronous processing. It still uses IOCPs underneath.async/awaithas slightly lower performance than the lower-level methods; its appeal is the ease of writing and composing asynchronous methods.async/await. Stephen Toub on the Parallel Team wrote up some example socket-specific awaitables that can help with that issue. (I recommend using the straightforward pattern first, and only using the performance-enhanced approach if you find it necessary; still, it’s good to know it’s out there if you do end up needing it).Taskunless you really need them to returnvoid.Taskis awaitable, so your method is composable (and more easily testable);voidis more like “fire and forget”.ConfigureAwait(false)to tell the rest of theasyncmethod to execute on a thread pool thread. I use this in my example above so thatProcessDatais executed in a thread pool thread, just like it was when usingSocketAsyncEventArgs.Socket.Connectedis useless. You need to send data to detect if the connection is still valid.