2011-02-15 13 views
7

Por lo que puedo decir, no hay compatibilidad incorporada (o extensión de marco) para ConnectAsync/AcceptAsync/SendAsync/ReceiveAsync, etc. ¿Cómo escribiría mi propia envoltura que sería compatible con el mecanismo async-await? Por ejemplo, mi código actual que maneja un ReceiveAsyn c tanto en línea como en la devolución de llamada (que se especifica en el SocketAsyncEventArgs):Cómo crear un contenedor para una llamada asincrónica?

private void PostReceive(SocketAsyncEventArgs e) 
{  
    e.SetBuffer(ReceiveBuffer.DataBuffer, ReceiveBuffer.Count, ReceiveBuffer.Remaining);    
    e.Completed += Receive_Completed; 

      // if ReceiveAsync returns false, then completion happened inline 
    if (m_RemoteSocket.ReceiveAsync(e) == false) 
    { 
     Receive_Completed(this, e); 
    }       
} 

.

private void Receive_Completed(object sender, SocketAsyncEventArgs e) 
{ 
    e.Completed -= Receive_Completed;  

    if (e.BytesTransferred == 0 || e.SocketError != SocketError.Success) 
    { 
     if (e.BytesTransferred > 0) 
     {     
      OnDataReceived(e); 
     } 

     Disconnect(e);     
     return; 
    } 

    OnDataReceived(e); 

    // 
    // we do not push the SocketAsyncEventArgs back onto the pool, instead 
    // we reuse it in the next receive call 
    // 
    PostReceive(e); 
} 

Respuesta

2

También puede escribir una espera personalizada, que me gusta más en esta situación. Esta es una técnica de Stephen Toub de Microsoft. Puedes leer más sobre esta técnica aquí. http://blogs.msdn.com/b/pfxteam/archive/2011/12/15/10248293.aspx

Aquí es el awaitable personalizado:

public sealed class SocketAwaitable : INotifyCompletion 
{ 
    private readonly static Action SENTINEL =() => { }; 
    internal bool m_wasCompleted; 
    internal Action m_continuation; 
    internal SocketAsyncEventArgs m_eventArgs; 
    public SocketAwaitable(SocketAsyncEventArgs eventArgs) 
    { 
     if (eventArgs == null) throw new ArgumentNullException("eventArgs"); 
     m_eventArgs = eventArgs; 
     eventArgs.Completed += delegate 
     { 
      var prev = m_continuation ?? Interlocked.CompareExchange(
       ref m_continuation, SENTINEL, null); 
      if (prev != null) prev(); 
     }; 
    } 
    internal void Reset() 
    { 
     m_wasCompleted = false; 
     m_continuation = null; 
    } 
    public SocketAwaitable GetAwaiter() { return this; } 
    public bool IsCompleted { get { return m_wasCompleted; } } 
    public void OnCompleted(Action continuation) 
    { 
     if (m_continuation == SENTINEL || 
      Interlocked.CompareExchange(
       ref m_continuation, continuation, null) == SENTINEL) 
     { 
      Task.Run(continuation); 
     } 
    } 
    public void GetResult() 
    { 
     if (m_eventArgs.SocketError != SocketError.Success) 
      throw new SocketException((int)m_eventArgs.SocketError); 
    } 
} 

Algunos métodos de extensión para añadir a la clase socket y hacen que sea conveniente:

public static class SocketExtensions 
{ 
    public static SocketAwaitable ReceiveAsync(this Socket socket, 
     SocketAwaitable awaitable) 
    { 
     awaitable.Reset(); 
     if (!socket.ReceiveAsync(awaitable.m_eventArgs)) 
      awaitable.m_wasCompleted = true; 
     return awaitable; 
    } 
    public static SocketAwaitable SendAsync(this Socket socket, 
     SocketAwaitable awaitable) 
    { 
     awaitable.Reset(); 
     if (!socket.SendAsync(awaitable.m_eventArgs)) 
      awaitable.m_wasCompleted = true; 
     return awaitable; 
    } 
    // ... 
} 

En uso:

static async Task ReadAsync(Socket s) 
    { 
     // Reusable SocketAsyncEventArgs and awaitable wrapper 
     var args = new SocketAsyncEventArgs(); 
     args.SetBuffer(new byte[0x1000], 0, 0x1000); 
     var awaitable = new SocketAwaitable(args); 

     // Do processing, continually receiving from the socket 
     while (true) 
     { 
      await s.ReceiveAsync(awaitable); 
      int bytesRead = args.BytesTransferred; 
      if (bytesRead <= 0) break; 

      Console.WriteLine(bytesRead); 
     } 
    } 
0

Para cosas de socket allí es un contenedor en .NET 4.5. Si está en .NET 4, le recomendaría usar el APM y no el patrón asíncrono basado en eventos. Convierte a Task mucho más fácilmente.

Cuestiones relacionadas