Frage

Soweit ich das beurteilen kann, gibt es keine integrierte (oder Framework-Erweiterungs-) Unterstützung für ConnectAsync/AcceptAsync/SendAsync/ReceiveAsync, usw..Wie würde ich meinen eigenen Wrapper schreiben, der vom Async-Await-Mechanismus unterstützt würde.Zum Beispiel mein aktueller Code, der a behandelt ReceiveAsync sowohl inline als auch beim Rückruf (der im Feld angegeben ist) 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);
}
War es hilfreich?

Lösung

Der Trick ist zu benutzen Aufgabenerfüllungsquelle um mit diesem Szenario umzugehen.

Ich habe darüber gebloggt.Einzelheiten finden Sie unter Vorhandenen Code für Await vorbereiten.

Andere Tipps

Sie können auch eine benutzerdefinierte Wartezeit schreiben, die mir in dieser Situation besser gefällt.Dies ist eine Technik von Stephen Toub von Microsoft.Sie können hier mehr über diese Technik lesen.http://blogs.msdn.com/b/pfxteam/archive/2011/12/15/10248293.aspx

Hier ist der Brauch zu erwarten:

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

Einige Erweiterungsmethoden, die der Socket-Klasse hinzugefügt und bequem gemacht werden können:

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

Im Einsatz:

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

Für Steckdosensachen da is ein Wrapper in .NET 4.5.Wenn Sie mit .NET 4 arbeiten, würde ich empfehlen, das APM und nicht das ereignisbasierte asynchrone Muster zu verwenden.Es konvertiert zu Taskist viel einfacher.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top