支持网络I / O的异步和同步操作

时间:2014-01-15 20:30:09

标签: c# .net delegates

首先,我必须声明,我没有太多的C#背景,所以我可能会在这里找不到明显的东西。

我被限制为.net 3.5(所以没有异步/等待)。

我已经为NetworkStream做了一个包装器,它会读取"正好是N"字节(与传统的"最多N"字节相对)并且不阻止当前线程。我想以阻塞的方式做同样的事情(也就是说,阻塞直到接收到N个字节,将这些字节返回给调用者),但显然,如果没有编写完全独立的实现,则使用Read ,例如。

internal class ReadBytesContext
{
    public Action<byte[]> Callback { get; private set; }

    public byte[] Buffer { get; private set; }

    public int ReadSoFar { get; set; }

    public ReadBytesContext(
        Action<byte[]> callback, 
        byte[] buffer, 
        int readSoFar)
    {
        Callback = callback;
        Buffer = buffer;
        ReadSoFar = readSoFar;
    }
}

// Network-related exception handling omitted for brevity.
public class Connection
{
    private NetworkStream _stream;

    public Connection(NetworkStream stream)
    {
        _stream = stream;
    }

    public void ReadBytes(int numBytes, Action<byte[]> callback)
    {
        var buffer = new byte[numBytes];
        _stream.BeginRead(
            buffer, 
            0, 
            numBytes, 
            new AsyncCallback(ReadBytesMaybeDone),
            new ReadBytesContext(callback, buffer, 0));
    }

    private void ReadBytesMaybeDone(IAsyncResult ar)
    {
        int bytesRead = _stream.EndRead(ar);
        var context = (ReadBytesContext)ar.AsyncState;
        context.ReadSoFar += bytesRead;
        if (context.ReadSoFar < context.Buffer.Length)
        {
            _stream.BeginRead(
                context.Buffer, 
                context.ReadSoFar,
                context.Buffer.Length - context.ReadSoFar,
                new AsyncCallback(ReadBytesMaybeDone), 
                context);
        }
        else
        {
            context.Callback(context.Buffer);
        }
    }
}

这通常是c#中网络内容的有效方法吗?

可能不相关的问题:我如何设置一个回调函数的catch-all处理程序,它在ThreadPool上运行(代表传递给Begin *的代理,右边),那么他们中未被捕获的例外不会让应用程序崩溃吗?

2 个答案:

答案 0 :(得分:3)

不,这不是一个好方法,因为如果网络流被客户端过早关闭,可能会出现异常。

你看过Jeff Richter的AsyncEnumerator吗?我过去用过.net 3.5。在块中寻找Wintellect Powerthreading库。基本上,yield return之前的第一个代码块执行同步,但是一旦命中了yield,线程就会神奇地放回到线程池中,并且在异步操作完成之前执行不会恢复。这是老派异步/等待真的。

private IEnumerator<int> ReadBytesEnumerator(AsyncEnumerator<byte[]> ae, int numbytes)
{
   byte [] buffer = new byte[numbytes];
   int totalBytes = 0;
   while(totalBytes < numbytes)
   {
     _stream.BeginRead(buffer , totalBytes , numbytes - totalBytes , ae.End(), null);

     yield return 1;

     totalBytes +=_stream.EndRead(ae.DequeueResult());

   }
   ae.Result = buffer;
}

public IAsyncResult BeginReadBytes(int numBytes, AsyncCallback callback, object state)
{
   AsyncEnumerator<byte []> ae = new AsyncEnumerator<byte[]>();
   return ae.BeginExecute(ReadBytesEnumerator(ae, numBytes), callback, state);
}

public byte [] EndReadBytes(IAsyncResult result)
{
  return AsyncEnumerator<byte[]>.FromAsyncResult(result).EndExecute();
}

现在,只要调用EndReadBytes,就可以通过调用代码来处理任何异常。要使此方法同步,您只需调用EndReadBytes(BeginReadBytes(numBytes,null,null);或者甚至用ReadBytes方法包装该片段。

答案 1 :(得分:0)

如果要重用Connection类,特别是ReadBytes方法,可能的实现可能是,

public void ReadBytes(
    int numBytes, 
    Action<byte[]> callback, 
    bool doAsync = true)
{
   if (doAsync)
       ReadBytesAsync(numBytes, callback);
   else
       ReadBytesSync(numBytes, callback);
}

private void ReadBytesSync(int numBytes, Action<byte[]> callback)
{
    var buffer = new byte[numBytes];
    var context = new ReadBytesContext(callback, buffer, 0);
    while (context.ReadSoFar < context.Buffer.Length)
    {
        var bytesRead = _stream.Read(
            context.Buffer, 
            context.ReadSoFar, 
            context.Buffer.Length - context.ReadSoFar);
        context.ReadSoFar += bytesRead;
    }

    context.Callback(context.Buffer);
}

private void ReadBytesAsync(int numBytes, Action<byte[]> callback)
{
    // Works as your current ReadBytes, which is async
}
相关问题