开发者

Exception handling when implementing the APM pattern with AsyncEnumerator

I'm trying to implement the APM pattern using Richter's AsyncEnumerator class. The goal is to implement an ExtendedSocket class which is derived from Socket and offers Begin/EndReceiveFixed and Begin/EndSendFixed methods to send or receive a fixed amount of bytes asynchronously.

The code looks like this (I omitted the sending part since it is basically the same as for receiving):

class ExtendedSocket : Socket
{

    public Exten开发者_开发知识库dedSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
        : base(addressFamily, socketType, protocolType)
    {

    }

    public IAsyncResult BeginReceiveFixed(byte[] buffer, SocketFlags socketFlags, AsyncCallback callback, Object state)
    {
        AsyncEnumerator ae = new AsyncEnumerator();
        return ae.BeginExecute(DoReceiveFixed(ae, buffer, socketFlags), callback, state);
    }

    public void EndReceiveFixed(IAsyncResult asyncResult)
    {
        AsyncResult ar = asyncResult as AsyncResult;
        (ar.InitiatingObject as AsyncEnumerator).EndExecute(ar);
    }

    private IEnumerator<Int32> DoReceiveFixed(AsyncEnumerator ae, byte[] buffer, SocketFlags socketFlags)
    {
        int totalReceivedBytes = 0;
        while (totalReceivedBytes < buffer.Length)
        {
            BeginReceive(buffer, totalReceivedBytes, buffer.Length - totalReceivedBytes, socketFlags, ae.End(), null);
            yield return 1;
            totalReceivedBytes += EndReceive(ae.DequeueAsyncResult());
        }
    }
}

This works perfectly fine in my application but I don't know how to handle exceptions in DoReceiveFixed. I'd like to implement the default APM behaviour where exceptions are (re)thrown when EndReceiveFixed is called.

Unfortunately I don't have access to the AsyncResult object inside DoReceiveFixed, so I can't call SetAsCompleted with an exception on the AsyncResult object.

My current workaround is to use AsyncEnumerator<Exception> instead of AsyncEnumerator like this:

class ExtendedSocket : Socket
{

    public ExtendedSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
        : base(addressFamily, socketType, protocolType)
    {

    }

    public IAsyncResult BeginReceiveFixed(byte[] buffer, SocketFlags socketFlags, AsyncCallback callback, Object state)
    {
        AsyncEnumerator<Exception> ae = new AsyncEnumerator<Exception>();
        return ae.BeginExecute(DoReceiveFixed(ae, buffer, socketFlags), callback, state);
    }

    public void EndReceiveFixed(IAsyncResult asyncResult)
    {
        AsyncResult ar = asyncResult as AsyncResult;
        AsyncEnumerator<Exception> ae = ar.InitiatingObject as AsyncEnumerator<Exception>;
        ae.EndExecute(ar);
        if (ae.Result != null)
        {
            throw ae.Result;
        }
    }

    private IEnumerator<Int32> DoReceiveFixed(AsyncEnumerator<Exception> ae, byte[] buffer, SocketFlags socketFlags)
    {
        int totalReceivedBytes = 0;
        Exception catchedException = null;
        while (totalReceivedBytes < buffer.Length)
        {
            try
            {
                BeginReceive(buffer, totalReceivedBytes, buffer.Length - totalReceivedBytes, socketFlags, ae.End(), null);
            }
            catch (Exception ex)
            {
                catchedException = ex;
                break;
            }
            yield return 1;
            try
            {
                totalReceivedBytes += EndReceive(ae.DequeueAsyncResult());
            }
            catch (Exception ex)
            {
                catchedException = ex;
                break;
            }
        }
        ae.Result = catchedException;
    }
}

This seems to work but I don't really like this solution. Is there a better way to do this? Maybe there is a way to get access to the AsyncResult object from inside DoFixedReceive?


With the help of Jeffrey Richter I solved my problem (see here):

There is no need to catch all exceptions in an iterator and rethrow them manually. AsyncEnumerator does this for us.

But be careful with your debugger settings. I needed to uncheck the 'Enable Just My Code' setting on the general debugging page. Otherwise, if an exception occurs inside the iterator, the debugger breaks with an unhandled exception message before AsyncEnumerator has the chance to catch the exception.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜