2011-02-15 85 views
7

從我可以告訴,沒有內置(或框架擴展)支持ConnectAsync/AcceptAsync/SendAsync/ReceiveAsync,等等。我將如何編寫自己的包裝,將支持異步等待機制。例如,我的當前代碼,其處理一個ReceiveAsyn C均直列和回調(這是在SocketAsyncEventArgs指定):如何爲異步等待調用創建包裝?

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); 
} 

回答

2

你也可以寫一個自定義的等待,在這種情況下我更喜歡。這是微軟公司的Stephen Toub所採用的技術。你可以在這裏閱讀更多關於這種技巧。 http://blogs.msdn.com/b/pfxteam/archive/2011/12/15/10248293.aspx

這裏是習俗awaitable:

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); 
    } 
} 

一些擴展方法添加到Socket類,並使其方便:

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; 
    } 
    // ... 
} 

在使用中:

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

對於套接字的東西 .NET 4.5中的包裝。如果你在.NET 4上,我建議使用APM而不是基於事件的異步模式。它更容易轉換爲Task