C # Sockets: synchronous calls inside asynchronous

In MSDN, an example of using asynchronous sockets, the received data in the socket is performed by repeatedly calling the asynchronous BeginReceive from the callback handler called by BeginReceive:

private static void ReceiveCallback( IAsyncResult ar ) { //...Skipped... if (bytesRead > 0) { // There might be more data, so store the data received so far. state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead)); // Get the rest of the data. client.BeginReceive(state.buffer,0,StateObject.BufferSize,0, new AsyncCallback(ReceiveCallback), state); } else { // ...Skipped... } 

http://msdn.microsoft.com/en-us/library/bbx2eya8 (v = vs .110) .aspx

Is there a need for a second asynchronous call from a handler that is already running in a separate thread? Is it possible to just use Receive in a loop in this handler? Sort of:

 while (bytesRead) { bytesRead = client.Receive(state.buffer, 0, client.Available, SocketFlags.None); // Etc... } 
+1
source share
1 answer

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); } } 
+3
source

Source: https://habr.com/ru/post/970720/


All Articles