【问题标题】:NetworkStream can read data only after the sending NetworkStream is closed or disposedNetworkStream 只能在发送的 NetworkStream 关​​闭或处置后才能读取数据
【发布时间】:2021-06-25 17:35:46
【问题描述】:

我正在编写一个用于发送和接收 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, 0, buffer.Length); // read bytes from file
                        csEncrypt.Write(buffer, 0, 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, 0, toRead); // read bytes from CryptoStream
                        totalBytesRead += nowRead; // sum read bytes
                        fileStream.Write(buffer, 0, nowRead); // write decrypted bytes to file
                    }
                }
            }
        }
    }
}

问题是ReceiveFileAsync() 在最后一个csDecrypt.Read(buffer, 0, toRead) 上阻塞了自己,就好像csDecrypt 流中没有足够的数据一样。但是,当我关闭(终止进程)发送应用程序时,接收应用程序正确接收最后一个缓冲区。

当我将using (CryptoStream csEncrypt = new CryptoStream(tcpHandler.stream, encryptor, CryptoStreamMode.Write, true)) 的最后一个参数更改为false 时,也会发生同样的事情 - 它使 CryptoStream 在处理基本流 (tcpHandler.stream) 时关闭它。

如果我在SendFileAsync() 末尾添加tcpHandler.stream.Close(),它也会有所帮助。

简而言之,在我关闭发送 NetworkStream (tcpHandler.stream) 之前,我发送的最后一个缓冲区不会被接收,无论是通过关闭/处置它还是关闭应用程序。

我尝试将await tcpHandler.stream.FlushAsync() 添加为SendFileAsync() 的最后一行,但没有帮助。有什么想法我应该怎么做才能解决这个问题?

编辑:使用嵌套的 using 语句更新代码。

【问题讨论】:

  • 您使用的块是错误的。它们需要嵌套(不是串行的)。 using 块中的对象位于块外。对象文件流在流被写入后被释放。
  • @jdweng 我嵌套了using 块,但同样的事情发生了。
  • 发布更新代码。
  • @jdweng 贴出代码
  • 在收到所有数据之前,您无法解密文件。加密数据以块为单位,您无法解密部分块。 while 循环必须在尝试解密之前读取整个消息。

标签: c# stream cryptography aes networkstream


【解决方案1】:

我正在测试 .NET 5.0 异步控制台应用程序,其中包含一个 5 字节的文本文件,其中一些处于休眠状态以保持进程处于活动状态。

Sender 应用关键点:

  1. NetworkStream.FlushAsync 或 (NetworkStream.Flush)
  2. CryptoStream.FlushFinalBlock
  3. NetworkStream.Close
  4. TcpClient.Close(同时关闭 NetworkStream)
  5. CryptoStream 构造函数上的 leaveOpen 参数(true -> 使 NetworkStream 保持打开状态,false -> 它在 NetworkStream 上调用 Close)

接收器应用关键点:

  1. CryptoStream 构造函数上的 leaveOpen 参数(这里没什么有趣的),

附注:

  • Flush 将缓冲区中的所有数据写入底层流。它不会关闭 Stream。
  • Close 显然关闭了流
  • CryptoStream 会在流关闭时自动管理最终密码块本身,因此需要使用 Flushing 进行预期。

你自己已经有了答案:

您必须关闭 NetworkStream 才能让接收方能够接收到它。 有多种方法可以按照说明进行操作。

刷新不会关闭流,因此它不起作用。

此行为不依赖于CryptoStream。我还尝试了直接写入NetworkStream 而不使用加密-> 相同的结果。 但是,您可以让 tcp 客户端保持打开状态,这意味着您仍然可以进行活动连接。 所以就像你发现的,我测试过:

  • 1)、2) 和 5)true 不起作用(网络流将被关闭)
  • 3)、4) 和 5)false 将起作用(网络流将保持打开状态)

这是我的测试代码(几乎一样,但还是决定贴出来):

  1. 客户:

     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, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 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, 0, buffer.Length)) > 0)
                         {
                             csEncrypt.Write(buffer, 0, bytesRead);
                             //csEncrypt.FlushFinalBlock();
                             break;
                         }
                     }
                 }
             }
         }
     }
    
  2. 服务器:

     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, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 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, 0, buffer.Length)) > 0)
                         {
                             br.Write(buffer, 0, bytesRead);
                         }
                     }
                 }
             }
         }
     }
    

【讨论】:

    猜你喜欢
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 2020-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多