调整缓冲区长度以从 NetworkStream 读取小数据

Posted

技术标签:

【中文标题】调整缓冲区长度以从 NetworkStream 读取小数据【英文标题】:Tuning buffer length for reading small data from NetworkStream 【发布时间】:2011-11-25 13:52:08 【问题描述】:

如何在从 TcpClient/NetworkStrem 读取小数据时微调 bufferSize? 如果 bufferSize 大如 1024、4096,则 Read/BeginRead 块。 如果我将 bufferSize 设置为 16, 32,它不会阻塞。

将 bufferSize 设置为 1 是否保证不会有任何块?性能影响会很严重吗?

看起来将 ReadTimeout 设置为 1000、2000 之类的值 对阻塞没有影响。有没有其他方法可以阻止 短? (NoDelay = true 不起作用)

public static IObservable<byte[]> AsyncReadChunk(this Stream stream, int bufferSize)

    var buffer = new byte[bufferSize];

    return Observable.FromAsyncPattern<byte[], int, int, int>(stream.BeginRead, stream.EndRead)(buffer, 0, bufferSize)
        .Select(cbRead =>
                    
                        var dataChunk = new byte[cbRead];

                        Buffer.BlockCopy(buffer, 0, dataChunk, 0, cbRead);

                        return dataChunk;
                    );


public static IObservable<byte[]> AsyncRead(this NetworkStream stream, int bufferSize)

    return Observable.Defer(() => stream.DataAvailable ? AsyncReadChunk(stream, bufferSize) : Observable.Return(new byte[0]))
        .Repeat()
        .TakeWhile((dataChunk, index) => dataChunk.Length > 0);

【问题讨论】:

您如何读取数据?文档指出,当没有数据可用时,NetworkStream.Read 不会阻塞但返回值 0。 我用上面的代码读取了数据。当 NetworkStream.DataAvailable 为真时,我循环并读取 bufferSize 块中的数据。 你能展示从套接字读取所涉及的完整代码吗? 我反复调用 AsyncRead。没有线程。睡眠。 @Jan:真的吗?我会认为(像所有其他类型的流一样) NetworkStream.Read 将返回零以指示流的结束,并阻塞(或在异步情况下不调用 EndRead)直到数据可用。我有很多 代码依赖于这种行为。你能分享你的来源吗? 【参考方案1】:

看起来像设置 将 ReadTimeout 设置为 1000、2000 等值对阻塞没有影响。

Msdn 说,使用异步BeginRead 方法时,ReadTimeout 无效。

将 bufferSize 设置为 1 是否保证不会有任何块?

不,当然不是。当没有通过连接发送一个字节时,无论缓冲区大小如何,Read 调用都会阻塞。

性能影响会很差吗?

我猜你在这里谈论的是 1 字节缓冲区。 这取决于您收到的数据量和频率以及您在EndRead 上执行的代码。处理具有高带宽的流时,影响可能很大。您必须在接收数据时尝试观察您的 cpu。

我不太确定你想要分别实现什么,你对阻塞的关注是什么。

当您使用 1024 字节的缓冲区启动 Receive(或 Networkstream.Read)并在套接字上收到 10 个字节时,Read 调用将在短暂延迟后返回该 10 个字节,但不会阻塞,直到整个缓冲区已满。

还有其他方法可以使阻塞变短吗?

是什么意思。正如我所说,即使缓冲区很大,在接收少量数据时读取也不会无休止地阻塞。

(NoDelay = true 不起作用)

这是一个 completely different 的故事,但在您的发件人方面将其设置为 true 可能会很有趣(如果您也可以控制它)。

当设置为 false(默认值)时,它会将发送的小数据块合并为更大的数据块,以减少一个 tcp 数据包(40 字节标头)的开销。

编辑

我的意思是 NetworkStream.BeginRead 如果没有数据就立即返回。

使用stream.DataAvailable 怎么样?没有数据时应该返回false。

此外,当使用异步模式时,调用会阻塞直到有事可做,这难道不是预期的行为吗?否则,您将在繁忙的循环中获得主动轮询。

当缓冲区很大时,它有时会等待 60 秒才能返回(当它没有完全填充或填充到一定量时)

嗯,不敢相信。您通过通道发送什么样的数据?每分钟 1 个字节?每秒 1000 字节?

【讨论】:

>>我不太确定你想分别实现什么,你对阻塞的关注是什么。 我的意思是 NetworkStream.BeginRead 如果没有数据就立即返回。当缓冲区很大时,它有时会等待 60 秒才能返回(当它没有完全填充或填充到一定量时)。

以上是关于调整缓冲区长度以从 NetworkStream 读取小数据的主要内容,如果未能解决你的问题,请参考以下文章

C# TCP编程 NetworkStream 的大小?

怎么计算环形缓冲区长度

调整拥塞控制的性能

c++缓冲池循环队列实现

如何停止从 NetworkStream 读取?

为啥处理 NetworkStream 会断开客户端