2011-02-15 83 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