我经常遇到这样的问题:我有一个充满数据的流,希望将其全部写入另一个流。
所有的代码示例都使用字节数组形式的缓冲区。
是否有更优雅的方法?
如果没有,请问缓冲区的理想大小是多少。哪些因素构成了这个值?
在 .NET 4.0 中,我们终于得到了一个 Stream.CopyTo
方法!太棒了!
关于理想的缓冲区大小:
"使用Read方法时,使用与流的内部缓冲区大小相同的缓冲区更有效,其中内部缓冲区设置为所需的块大小,并始终读取少于块大小。如果在构建流时未指定内部缓冲区的大小,则其默认大小为4千字节(4096字节)."
任何流读取过程都将使用Read(char buffer[], int index, count)方法,这是该引用所指的方法。
http://msdn.microsoft.com/en-us/library/9kstw824.aspx (在“备注”下)。
我不确定在.NET中是否可以直接将一个流传输到另一个流中,但以下是一种使用中间字节缓冲区实现的方法。缓冲区的大小是任意的,最有效的大小取决于传输的数据量。
static void CopyStream(Stream input, Stream output){
byte[] buffer = new byte[0x1000];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
output.Write(buffer, 0, read);
}
BufferedStream.CopyTo(Stream)
CopyTo
和 CopyToAsync
方法来完成此工作。这里提供一个示例,展示了一个在端口30303
上监听外部连接并将其与本地端口8085
进行连接的TCP服务器(使用 .NET 5 编写)。大多数流应该都可以使用相同的方式处理,只需注意它们是双向还是单向的即可。using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task Main(string[] args)
{
var externalConnectionListener = new TcpListener(IPAddress.Any, 30303);
externalConnectionListener.Start();
while (true)
{
var externalConnection = await externalConnectionListener.AcceptTcpClientAsync().ConfigureAwait(false);
_ = Task.Factory.StartNew(async () =>
{
using NetworkStream externalConnectionStream = externalConnection.GetStream();
using TcpClient internalConnection = new TcpClient("127.0.0.1", 8085);
using NetworkStream internalConnectionStream = internalConnection.GetStream();
await Task.WhenAny(
externalConnectionStream.CopyToAsync(internalConnectionStream),
internalConnectionStream.CopyToAsync(externalConnectionStream)).ConfigureAwait(false);
});
}
}
}
}