我試圖使用Richter的AsyncEnumerator
類實現APM模式。目標是實現從Socket
派生的ExtendedSocket
類,並提供Begin/EndReceiveFixed
和Begin/EndSendFixed
方法以異步方式發送或接收固定數量的字節。使用AsyncEnumerator實現APM模式時的異常處理
的代碼看起來像這樣(我省略了發送部分,因爲它基本上是相同接收):
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 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());
}
}
}
這工作在我的應用完全沒有問題,但我不知道該如何處理異常在DoReceiveFixed
。我想實現在調用EndReceiveFixed
時拋出異常(重新拋出異常)的默認APM行爲。
不幸的是,我無法訪問DoReceiveFixed
中的AsyncResult
對象,因此我不能撥打SetAsCompleted
,但AsyncResult
對象除外。
我目前的解決方法是使用AsyncEnumerator<Exception>
,而不是AsyncEnumerator
這樣的:
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;
}
}
這似乎是工作,但我真的不喜歡這種解決方案。有一個更好的方法嗎?也許有一種方法可以從DoFixedReceive
內部訪問AsyncResult
對象?