如何解决使用int NetworkStream.ReadSpan <Bytes>接收完整的网络流
正如标题所述,我正在尝试将新(C#8.0)对象(Span)用于我的网络项目。在我以前的实现中,我了解到必须强制确保NetworkStream在尝试使用其内容之前已收到完整的缓冲区,否则,取决于连接,另一端接收的数据可能并不完整。
while (true)
{
while (!stream.DataAvailable)
Thread.Sleep(10);
int received = 0;
byte[] response = new byte[consumerBufferSize];
//Loop that forces the stream to read all incoming data before using it
while (received < consumerBufferSize)
received += stream.Read(response,received,consumerBufferSize - received);
string[] message = ObjectWrapper.ConvertByteArrayToObject<string>(response);
consumerAction(this,message);
}
但是,引入了另一种读取网络流数据的方法(Read(Span))。并且假设stackalloc将有助于提高性能,我正在尝试迁移旧的实现以适应该方法。这是现在的样子:
while (true)
{
while (!stream.DataAvailable)
Thread.Sleep(10);
Span<byte> response = stackalloc byte[consumerBufferSize];
stream.Read(response);
string[] message = ObjectWrapper.ConvertByteArrayToObject<string>(response).Split('|');
consumerAction(this,message);
}
但是现在我如何确定缓冲区已被完全读取,因为它没有提供我正在使用的方法?
编辑:
//Former methodd
int Read (byte[] buffer,int offset,int size);
//The one I am looking for
int Read (Span<byte> buffer,int size);
解决方法
我不确定我是否理解您的要求。使用Span<byte>
时,您在第一个代码示例中依赖的所有相同功能仍然存在。
Read(Span<byte>)
重载仍然返回读取的字节数。而且由于Span<byte>
本身并不是缓冲区,而是缓冲区的一个窗口,因此您可以更新Span<byte>
值以指示新的起点来读取其他数据。读取旧字节数并能够为下一次读取指定偏移量是您在旧示例中重复该功能所需要的。当然,您目前没有保存原始缓冲区引用的任何代码;您也需要添加它。
我希望这样的工作正常:
while (true)
{
while (!stream.DataAvailable)
Thread.Sleep(10);
byte* response = stackalloc byte[consumerBufferSize];
while (received < consumerBufferSize)
{
Span<byte> span = new Span<byte>(response,received,consumerBufferSize - received);
received += stream.Read(span);
}
// process response here...
}
请注意,由于unsafe
的工作方式,因此这需要stackalloc
代码。您只能通过使用Span<T>
并每次分配新的块来避免这种情况。当然,这最终将耗尽您的所有内存。
由于在您的实现中您显然是将一个线程专用于此无限循环,所以我看不出stackalloc
有何帮助。您不妨在堆中分配一个长寿命的缓冲区数组并使用它。
换句话说,我真的没有看到这比仅使用常规托管数组的原始Read(byte[],int,int)
重载有什么好处。但是上面就是您使代码工作的方式。
此外:您应该了解异步API的工作方式。由于您已经在使用NetworkStream
,因此async
/ await
模式是很自然的选择。而且,无论使用哪种API,循环检查DataAvailable
都是胡扯。不要那样做Read()
方法已经是阻塞方法;您无需等待数据显示在单独的循环中,因为Read()
方法只有在有一些方法时才会返回。
我只是添加了一点额外的信息。
你所说的功能有以下说明
public override int Read (Span<byte> buffer);
(来源:https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.networkstream.read?view=net-5.0)
返回的 int 是从 NetworkStream 读取的字节量。现在,如果我们查看 Span 函数,我们会发现 Slice 具有以下描述
public Span<T> Slice (int start);
它返回我们 Span 的一部分,您可以使用它在不使用不安全代码的情况下将 stackalloc 的特定部分发送到您的 NetworkStream。
重用你的代码,你可以使用这样的东西
while (true)
{
while (!stream.DataAvailable)
Thread.Sleep(10);
int received = 0;
Span<byte> response = stackalloc byte[consumerBufferSize];
//Loop that forces the stream to read all incoming data before using it
while (received < consumerBufferSize)
received += stream.Read(response.Slice(received));
string[] message = ObjectWrapper.ConvertByteArrayToObject<string>(response).Split('|');
consumerAction(this,message);
}
简单来说,我们“创建”了一个新的 Span,它是初始 Span 的一部分,指向我们的 stackalloc 与 Slice,“start”参数允许我们选择从哪里开始这部分。然后将该部分传递给 read 函数,该函数将在我们“开始”切片的任何地方开始写入我们的缓冲区。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。