【问题标题】:ReadAsync is emptying streamReadAsync 正在清空流
【发布时间】:2018-03-24 20:57:41
【问题描述】:

所以,我让这段代码在客户端/服务器程序之间正常通信。通信是通过一个字节缓冲区完成的,该缓冲区有两个元素,一个指定后面有多少字节的 int 和“有效负载”。如果我用我的 调试器单步执行并检查。当我的代码到达该行时

await cStream.ReadAsync(bytes, 0, 4);

如果我检查可用的字节数,它会显示 32。一旦我跨过这一行,它就会显示可用字节数为 0。如果我直接从 _stream(我的 TcpClient 的 NetworkStream)读取,那么代码就可以正常工作。

完整代码是

private async void RecvAsync()
{

    byte[]          key     = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
    byte[]          iv      = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
    RijndaelManaged crypto  = new RijndaelManaged();
    CryptoStream    cStream = new CryptoStream(_stream, crypto.CreateDecryptor(key, iv), CryptoStreamMode.Read);

    int bytesRead = 0;

    do
    {
        try
        {
            byte[] bytes = new byte[4];
            await cStream.ReadAsync( bytes, 0, 4 );
            //await _stream.ReadAsync(bytes, 0, 4, _cancelToken.Token);

            int size = BitConverter.ToInt32(bytes, 0);


            cStream = new CryptoStream(_stream, crypto.CreateDecryptor(key, iv), CryptoStreamMode.Read);
            bytes = new byte[size];
            bytesRead = cStream.Read( bytes, 0, size );
            //await _stream.ReadAsync(bytes, 0, bytes.Length, _cancelToken.Token); //_stream.Read( bytes, 0, size );

            int id   = BitConverter.ToInt32( bytes, 0 );
            int type = BitConverter.ToInt32( bytes, 4 );

            UTF8Encoding utf  = new UTF8Encoding();
            string       body = utf.GetString( bytes, 8, size - 8 );

            Packet packet = new Packet( id, (ServerDataType)type, body );
            ProcessPacket( packet );
        }
        catch ( Exception ) { break; }
    } while ( bytesRead > 0 );
}

【问题讨论】:

  • 注释的两行代码是我之前直接从 NetworkStream 读取的,没有加密,它使用这两行而不是加密可以顺利工作。
  • CryptoStream 假设包装流中的所有数据都将被处理,因此它读取更大的块以提高效率。如果你需要控制它,你需要制作一个限制的流包装器。但请注意,它总是需要读取转换输入块大小的倍数。
  • 你知道什么是 TCP keep-alive 消息吗?它是一个零字节的数据报,用于防止服务器关闭空闲通道。发送二进制数据的正确方法是在消息前加上字节计数并读取,直到读取所有字节。 TCP 会将消息分段成最大大小约为 1500 字节的数据报。您必须设计来处理碎片。一次读取一个字节效率不高。
  • @bartonjs 所以我必须读取整个可用缓冲区并从中解析?
  • @jdweng 这正是我所做的,我读取了一个整数,该整数告诉了其余数据的长度,然后我读取了该数据说要读取的字节数。跨度>

标签: c# sockets cryptostream


【解决方案1】:

试试下面的代码:

        private async void RecvAsync()
        {

            byte[]          key     = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
            byte[]          iv      = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
            RijndaelManaged crypto  = new RijndaelManaged();
            CryptoStream    cStream = new CryptoStream(_stream, crypto.CreateDecryptor(key, iv), CryptoStreamMode.Read);

            int bytesRead = 0;

            byte[] bytes = new byte[8];
            List<byte> buffer = new List<byte>();
            cStream.Read( bytes, 0, 4 );
            //await _stream.ReadAsync(bytes, 0, 4, _cancelToken.Token);
            int size = BitConverter.ToInt32(bytes, 0);

            do
            {
                bytesRead = cStream.Read( bytes, 0, 8 );

                buffer.AddRange(bytes.Take(bytesRead));

            } while ( buffer.Count() < size );
            try
            {

                if(buffer.Count() >= 8)
                {

                    int id   = BitConverter.ToInt32( buffer.ToArray(), 0 );
                    int type = BitConverter.ToInt32( buffer.ToArray(), 4 );

                    UTF8Encoding utf  = new UTF8Encoding();
                    string  body = utf.GetString( buffer.ToArray(), 8, size - 8 );

                    Packet packet = new Packet( id, (ServerDataType)type, body );
                    ProcessPacket( packet );
                }
            }
            catch ( Exception ) { break; }
        }

【讨论】:

  • 我实际上遇到了一个不同的解决方案,我认为我可以适应我的需求。
猜你喜欢
  • 1970-01-01
  • 2021-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多