问题描述
我正在编写一个用于发送和接收 AES 加密文件的应用程序。我有两个功能,一个用于发送:
public async Task SendFileAsync()
{
var buffer = new byte[1024];
using (Aes aesAlg = Aes.Create())
{
// tcpHandler.stream is a NetworkStream
using (ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key,aesAlg.IV))
{
using (Stream fileStream = await selectedFile.OpenStreamForReadAsync())
{
using (CryptoStream csEncrypt = new CryptoStream(tcpHandler.stream,encryptor,CryptoStreamMode.Write,true))
{
while (stream.Position < selectedFileSize)
{
int NowRead = fileStream.Read(buffer,buffer.Length); // read bytes from file
csEncrypt.Write(buffer,NowRead); // write bytes to CryptoStream (which writes to NetworkStream)
}
}
}
}
}
await tcpHandler.stream.FlushAsync()
}
还有一个用于接收:
public async Task ReceiveFileAsync()
{
var buffer = new byte[1024];
BinaryFormatter formatter = new BinaryFormatter();
int messageLength = tcpHandler.ReadMessageLength();
int totalBytesRead = 0;
using (Aes aesAlg = Aes.Create())
{
// tcpHandler.stream is a NetworkStream
using (ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key,aesAlg.IV))
{
using (var fileStream = await newFile.OpenStreamForWriteAsync())
{
using (CryptoStream csDecrypt = new CryptoStream(tcpHandler.stream,decryptor,CryptoStreamMode.Read,true))
{
while (totalBytesRead < messageLength)
{
// calculate how many bytes have to be read in this iteration
var toRead = Math.Min(buffer.Length,messageLength - totalBytesRead);
var NowRead = csDecrypt.Read(buffer,toRead); // read bytes from CryptoStream
totalBytesRead += NowRead; // sum read bytes
fileStream.Write(buffer,NowRead); // write decrypted bytes to file
}
}
}
}
}
}
问题是 ReceiveFileAsync()
在最后一个 csDecrypt.Read(buffer,toRead)
上阻塞了自己,就好像 csDecrypt
流中没有足够的数据一样。但是,当我关闭(终止进程)发送应用程序时,接收应用程序正确接收到最后一个缓冲区。
当我将 using (CryptoStream csEncrypt = new CryptoStream(tcpHandler.stream,true))
的最后一个参数更改为 false
时会发生同样的事情 - 它使 CryptoStream 在被处理时关闭基本流 (tcpHandler.stream
)。
如果我在 tcpHandler.stream.Close()
末尾执行 SendFileAsync()
,它也有帮助。
简而言之,在我关闭发送 NetworkStream (tcpHandler.stream
) 之前,我发送的最后一个缓冲区不会被接收到,无论是通过关闭/处置它还是关闭应用程序。
我尝试将 await tcpHandler.stream.FlushAsync()
添加为 SendFileAsync()
的最后一行,但没有帮助。有什么想法我应该怎么做才能解决这个问题?
编辑:使用嵌套的 using
语句更新代码。
解决方法
我测试的 .NET 5.0 异步控制台应用程序带有一个 5 字节的文本文件,其中包含一些睡眠以保持进程处于活动状态。
发件人应用要点:
-
NetworkStream.FlushAsync
或 (NetworkStream.Flush
) CryptoStream.FlushFinalBlock
NetworkStream.Close
-
TcpClient.Close
(同时关闭 NetworkStream) -
CryptoStream
参数(true -> 使 NetworkStream 保持打开状态,false -> 在 NetworkStream 上调用 Close)
leaveOpen
构造函数上的 接收器应用要点:
-
leaveOpen
构造函数上的CryptoStream
参数(这里没什么意思),
边注:
- Flush 使缓冲区中的所有数据都写入底层流。它不会关闭流。
- Close 显然会关闭流
- CryptoStream 会在流关闭时自动管理最终密码块本身,因此需要使用 Flushing 按预期执行此操作。
几乎你自己已经有了答案:
您必须关闭 NetworkStream 才能让接收者能够接收到它。有多种方法可以按照所述进行操作。
冲洗不会关闭流,因此它不会工作。
此行为不依赖于 CryptoStream
。我也尝试过直接写入 NetworkStream
而不使用加密 -> 相同的结果。
但是,您可以让 tcp 客户端保持打开状态,这意味着您仍将拥有活动连接。
所以就像你发现的和我测试的一样:
- 1)、2) 和 5)true 将不起作用(网络流将被关闭)
- 3)、4) 和 5)false 将起作用(网络流将保持打开状态)
这是我的测试代码(几乎相同但仍然决定发布):
-
客户:
class Program { static byte[] key = { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x11,0x12,0x13,0x14,0x15,0x16 }; static byte[] iv = { 0x01,0x16 }; static async Task Main(string[] args) { try { TcpClient tcpClient = new TcpClient(); tcpClient.Connect("127.0.0.1",8001); Stream stream = tcpClient.GetStream(); // NetworkStream await SendFileAsync(stream,"test.txt"); //await stream.FlushAsync(); //stream.Close(); //tcpClient.Close(); Thread.Sleep(10000); } catch (Exception e) { Console.WriteLine("Error..... " + e.Message); } } public static async Task SendFileAsync(Stream stream,string filePath) { var buffer = new byte[1024]; using (Aes aesAlg = Aes.Create()) using (ICryptoTransform encryptor = aesAlg.CreateEncryptor(key,iv))//aesAlg.Key,aesAlg.IV { using (FileStream fs = new FileStream(filePath,FileMode.Open)) using (BinaryReader br = new BinaryReader(fs)) { using (CryptoStream csEncrypt = new CryptoStream(stream,encryptor,CryptoStreamMode.Write,false)) { int bytesRead; while ((bytesRead = br.Read(buffer,buffer.Length)) > 0) { csEncrypt.Write(buffer,bytesRead); //csEncrypt.FlushFinalBlock(); break; } } } } } }
-
服务器:
class Program { static byte[] key = { 0x01,0x16 }; public static async Task Main(string[] args) { try { IPAddress ipAd = IPAddress.Parse("127.0.0.1"); TcpListener tcpServer = new TcpListener(ipAd,8001); tcpServer.Start(); var tcpClient = await tcpServer.AcceptTcpClientAsync(); await ReceiveFileAsync(tcpClient,"text.txt"); tcpServer.Stop(); } catch (Exception e) { Console.WriteLine("Error..... " + e.Message); } } public static async Task ReceiveFileAsync(TcpClient tcpClient,string filePath) { var buffer = new byte[1024]; var stream = tcpClient.GetStream(); using (Aes aesAlg = Aes.Create()) using (ICryptoTransform decryptor = aesAlg.CreateDecryptor(key,iv)) { using (FileStream fs = new FileStream(filePath,FileMode.Create)) using (BinaryWriter br = new BinaryWriter(fs)) { using (CryptoStream csDecrypt = new CryptoStream(stream,decryptor,CryptoStreamMode.Read,true)) { int bytesRead; while ((bytesRead = csDecrypt.Read(buffer,buffer.Length)) > 0) { br.Write(buffer,bytesRead); } } } } } }