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

本文关键字:数据 错过了 什么 两次 读取 | 更新日期: 2023-09-27 18:09:08

通常我可以这样做,用流数据填充字节数组:

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类的成员变量?显然我不想这样做,因为有多个连接,它们都有不同的值。

我觉得我错过了一些明显的东西…

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

当您调用

时,您不必从头再读一遍
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);
        //..
    }
}

在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返回的字节数。

同样,我还没有测试它,但我认为随后对Read的调用将读取流的下一个4字节。