必须读两次数据吗?我错过了什么?

时间:2011-09-20 01:46:51

标签: c# multithreading asynchronous

通常我可以做这样的事情来用流数据填充字节数组:

byte[] dataLength = new byte[4];

clientStream.Read(dataLength, 0, dataLength.Length);

这填补了字节数组..但是,我一直在尝试异步调用,我的代码看起来像这样:

byte[] dataLength = new byte[4];

clientStream.BeginRead(dataLength, 0, dataLength.Length, Read, clientStream);

private void Read(IAsyncResult async)
{
    NetworkStream clientStream = (NetworkStream)async.AsyncState;

    clientStream.EndRead(async);

    byte[] dataLength = new byte[4]; // ..?

    clientStream.Read(dataLength, 0, dataLength.Length); // Have to re-read in data with synchronous version?..

    int result = BitConverter.ToInt32(dataLength, 0);
}

我觉得完全是......错了。如果您只需要在回调中同步读取它,那么异步调用的重点是什么?如何在不使dataLength成为类的成员变量的情况下访问已读入的字节?显然我不想这样做,因为有多个连接,它们都有不同的值..

我觉得我在这里遗漏了一些明显的东西......

2 个答案:

答案 0 :(得分:2)

当您致电

时,您无需再读一遍
clientStream.EndRead(async);

它返回已读取的字节数,因此您需要执行以下操作:

int bytesRead = clientStream.EndRead(async);

此时,您的缓冲区已经填充了这些字节,以同步方式从流中读取只会读取更多字节。

如果你不想让你的缓冲区成为一个实例变量,你可以使用带代理的闭包:

byte[] buffer = new byte[4];
clientStream.BeginRead(buffer, 0, buffer.Length, (IAsyncResult async) =>
{
    int bytesRead = clientStream.EndRead(async);
    if (bytesRead == 4)
    {
        int result = BitConverter.ToInt32(buffer, 0);
        //..
    }
}, clientStream);

修改

更好的解决方案可能是将所有状态置于自定义类的形式,并将其传递到BeginRead()

public class StreamState
{
    public byte[] Buffer { get; set; }
    public NetworkStream Stream { get; set; }
}

clientStream.BeginRead(buffer, 0, buffer.Length, Read, new StreamState { Buffer = buffer, Stream = clientStream });


private void Read(IAsyncResult async)
{
    StreamState state = (StreamState) async.AsyncState;
    int bytesRead = state.Stream.EndRead(async);
    if (bytesRead == 4)
    {
        int result = BitConverter.ToInt32(state.Buffer, 0);
        //..
    }
}

答案 1 :(得分:0)

在MSDN上似乎没有完整的示例(我可以找到,NetworkStream.EndRead虽然有http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.endread(v=VS.90).aspx的代码。)

本教程有一个完整的例子:http://www.switchonthecode.com/tutorials/csharp-tutorial-asynchronous-stream-operations

简而言之,在您调用clientStream.EndRead之后,原始缓冲区dataLength应填充EndRead返回的字节数。

另外,我还没有对它进行过测试,但是我会在后续的4个字节中读取Read的后续调用。