Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/url/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 必须读入数据两次吗?我错过了什么?_C#_Multithreading_Asynchronous - Fatal编程技术网

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

C# 必须读入数据两次吗?我错过了什么?,c#,multithreading,asynchronous,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, clientStre

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

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()


MSDN上似乎没有完整的示例(我可以找到,NetworkStream.EndRead有一些代码)

本教程提供了一个完整的示例:

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


另外,我还没有对它进行测试,但是我想后续调用
Read
将读取流的下4个字节。

谢谢您的回答。然而,“如果你不想让你的缓冲区成为一个实例变量”是什么意思?如果我一次有多个连接,实例变量难道不起作用吗?你说得好像这是可能的?还有,有没有什么办法不关闭它?如果能够将闭包代码放在一个单独的函数中就好了。另外,如果使用这样的闭包,为什么还要传入clientStream作为对象数据的最后一个参数,并将其强制转换回NetworkStream?既然clientStream在范围内,为什么不直接访问它呢?我注意到在你做了这个
(NetworkStream)async.AsyncState
之后,你甚至从未使用过这个变量,那么为什么还要传递它呢?@John Smith:是的,这是一个经过疏忽编辑的答案,并添加了一个解决方案,该解决方案应该更好(自定义状态对象)BrokenGlass:感谢更新。但是仍然想知道,当他们必须从回调中访问字节时,总是这样吗?似乎是一件很平常的事。。这真的是最好的方法吗?
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);
        //..
    }
}