The main purpose of APM pattern was to avoid blocking the calling thread while waiting for the result of an asynchronous operation, and thus increase the scalability of server applications.
If in your AsyncCallback you continue to call Receive in a loop synchronously, you will still block the IOCP stream on which the initial BeginReceive . This might be fine for the client user interface, where you might not like the head of ThreadPool , but it is certainly not a good idea for a server application where a blocked thread might otherwise serve other incoming client requests.
Note that with C # 5.0 / .NET 4.5 onwards, APM is deprecated. You can use async/await and a new Task-based Asynchronous Pattern (TAP) template, which greatly simplifies the development of asynchronous code, for example:
async Task<string> ReadAllAsync() { var sb = new StringBuffer(); using (var tcp = new TcpClient()) { await tcp.ConnectAsync(IPAddress.Parse("localhost"), 8080).ConfigureAwait(false); var buffer = new byte[1024]; using (var stream = tcp.GetStream()) { var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); if (0 == bytesRead) break; sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead)); } } return sb.ToString(); }
If for some reason you don't want to use NetworkStream.ReadAsync , you can wrap the APM-style socket APIs as TAP with Task.FromAsync :
public static class SocketsExt { static public Task ReceiveDataAsync( this TcpClient tcpClient, byte[] buffer) { return Task.Factory.FromAsync( (asyncCallback, state) => tcpClient.Client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, asyncCallback, state), (asyncResult) => tcpClient.Client.EndReceive(asyncResult), null); } static public async Task<Int32> ReceiveInt32Async( this TcpClient tcpClient) { var data = new byte[sizeof(Int32)]; await tcpClient.ReceiveDataAsync(data).ConfigureAwait(false); return BitConverter.ToInt32(data, 0); } }