主题。
我经常在示例中看到如何使用 byte[],例如,在发送 POST 请求时。
我不明白为什么需要这样做,它是低级编程的雏形吗?
据我所知,String 是一个 Char 数组,每个字符在内存中由其 ASCII 码表示,当这个字符串转换为 byte[] 时会发生什么?
例如,这用于发送 POST 请求,但另一方面,控制器接受开箱即用的模型并立即创建对象。Microsoft 建议在新开发中使用 TPL 而不是 Thread,因为 Thread 级别较低。
TPL 将线程外包给 CLI,为什么不对 byte[] 做同样的事情呢?
基本上,我很困惑。
使用字节数组对我来说似乎很奇怪的示例
https://docs.microsoft.com/ru-ru/dotnet/standard/security/encrypting-data
class Class1
{
static void Main()
{
//Initialize the byte arrays to the public key information.
byte[] modulus =
{
214,46,220,83,160,73,40,39,201,155,19,202,3,11,191,178,56,
74,90,36,248,103,18,144,170,163,145,87,54,61,34,220,222,
207,137,149,173,14,92,120,206,222,158,28,40,24,30,16,175,
108,128,35,230,118,40,121,113,125,216,130,11,24,90,48,194,
240,105,44,76,34,57,249,228,125,80,38,9,136,29,117,207,139,
168,181,85,137,126,10,126,242,120,247,121,8,100,12,201,171,
38,226,193,180,190,117,177,87,143,242,213,11,44,180,113,93,
106,99,179,68,175,211,164,116,64,148,226,254,172,147
};
byte[] exponent = { 1, 0, 1 };
//Create values to store encrypted symmetric keys.
byte[] encryptedSymmetricKey;
byte[] encryptedSymmetricIV;
//Create a new instance of the RSA class.
RSA rsa = RSA.Create();
//Create a new instance of the RSAParameters structure.
RSAParameters rsaKeyInfo = new RSAParameters();
//Set rsaKeyInfo to the public key values.
rsaKeyInfo.Modulus = modulus;
rsaKeyInfo.Exponent = exponent;
//Import key parameters into rsa.
rsa.ImportParameters(rsaKeyInfo);
//Create a new instance of the default Aes implementation class.
Aes aes = Aes.Create();
//Encrypt the symmetric key and IV.
encryptedSymmetricKey = rsa.Encrypt(aes.Key, RSAEncryptionPadding.Pkcs1);
encryptedSymmetricIV = rsa.Encrypt(aes.IV, RSAEncryptionPadding.Pkcs1);
}
}
公钥只是一个数字,为什么是字节?
这是一个来自 metanit 的示例,发送 POST 请求,为什么我在这里将字符串转换为字节?不好,我知道该方法接受一个字节数组,但为什么它不能只接受一个字符串并用它做所有需要的事情呢?
WebRequest request = WebRequest.Create("https://localhost:44391/api/Order/");
request.Method = "POST"; // для отправки используется метод Post
// данные для отправки
string data = _data;
// преобразуем данные в массив байтов
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(data);
// устанавливаем тип содержимого - параметр ContentType
request.ContentType = "application/json";
// Устанавливаем заголовок Content-Length запроса - свойство ContentLength
request.ContentLength = byteArray.Length;
//записываем данные в поток запроса
using (Stream dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
}
WebResponse response = await request.GetResponseAsync();
using (Stream stream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(stream))
{
d = reader.ReadToEnd();
}
}
response.Close();
不正确,
string
作为双字节char
编码的 UTF-16 序列而不是 ASCII 存储在内存中。没有人强迫您手动转码为字节数组,尤其是在您使用现代网络 API 的情况下。
WebRequest
过时了,更是如此。看 - 我没有重新编码任何东西。但这并不意味着不会发生重新编码。它只是隐藏在 .NET 代码中的某个地方。
该字符串以 UTF-16 格式存储在内存中,并以 UTF-8 格式发送。在什么地方,字符串以字节的形式存储在内存中,并以字节的形式发送。我们都知道只有字节,内存中只能有字节。与通信通道一样,只能发送或接收字节。只是.NET很方便,不需要手动转码。
是否可以在
string
不“按原样”重新编码的情况下发送?这很容易,但您需要 .NET Core 或 .NET 5,因为在较旧的框架中,您必须在不安全的代码周围摇摆不定才能使其正确。您当然可以通过
Encoding.Unicode.GetBytes(text)
,但为了清楚起见,我不会使用它,尽管字节数组中的结果是相同的。如果构造函数
ByteArrayContent
将其作为参数ReadOnlySpan
,则可以将字符串内存的内容直接发送到网络,不仅无需转换,甚至无需“按原样”复制数据。当然,您可以通过StreamContent
创建自己的Stream
直接从 读取内存的方法来绕过这个限制string
,但这会变成很多代码,只是为了证明这是可能的,我不会写它。关于为什么要将数字转换为字节,它是类型和字节。好吧,例如,有
Int32
- 4 个字节。由于它Socket
只能发送字节,为了通过网络发送这个数字,您需要将其转换为字节。如果你真的变态,你可以像使用字符串一样,通过读取这个 Int32 所在的那个 RAM 单元的内容来发送这些字节。的确,有一个问题,在某些情况下,某个具有相同int的局部变量可能根本不在内存中,例如,存储在处理器寄存器中,并且只能通过网络发送内存,因此,作为通过网络发送的数据源,并使用字节数组作为指向 .NET 中包含数据的一块内存的最原始指针。至于密码学。.NET 中作为原语的最长整数
Int64
是 8 个字节。而代表密钥和其他加密数据的 8 个字节甚至都不好笑。即使是古老的 MD5 哈希也是 16 个字节。然后可以轻松清除字节数组,及时从内存中清除秘密数据。顺便说一句,正是因为字符串string
不能被清除(它不是可变的)——不建议在其中存储用于加密的密钥或密码。因此,在密码学中,数组无处不在。顺便说一句,我最近为Apache MD5密码加密算法写了代码,所以我很痛苦以尽量减少来回复制内存中的字节,至少为了性能,相同的跨度Span<T>
/在这方面帮助了我很多ReadOnlySpan<T>
.你的问题分为两部分。
首先,关于密码学。这里将键表示为数字,虽然可能,但不会给出任何东西。对于我们这些密码学用户来说,所有的工作都是关于转换字节集。我们不使用加法/乘法功能,也不使用加密密钥或加密消息的十进制表示。事实上,密码学适用于字节集,数字允许我们进行的额外操作只会导致“如果我们添加两个密钥会发生什么”的问题。等等
尽管键可以表示为容量足够大的数字,但这没有意义,就像将文件表示为数字没有意义一样。
关于第二部分,你是对的,你引用的 API 有点过时了。现在习惯上这样写:
如您所见,将对象序列化为 JSON 和编写正确
HttpContent
的 'a 等细节隐藏在方便的扩展方法后面,这些方法为您做所有事情。但是,如果您愿意,没有人会剥夺您的低级能力和完全控制权,您可以
HttpContent
自己编写。让我们先考虑一个更简单的例子。您想通过网络发送 Int32 或将其写入文件。当您使用抽象数据类型 Int32 时,作为程序员的您并不关心它在内存中的精确表示方式。这是一个可能因平台而异的实现细节。它可以采用什么值以及它支持什么操作对你来说很重要。
历史上发生过这种情况(稍后会详细介绍),只能通过网络发送字节。简单地从内存中以字节为单位复制 Int32 表示并通过网络发送它会导致接收端出现问题。但是如果接收方有不同的表示 Int32 的方式呢?可能根本没有 .NET 应用程序,而是只能使用字节的 8 位微控制器。这是通过网络发送字节的原因之一——为了互操作性,即 不同系统的兼容性。字节被用作所有操作系统都能理解的最小元素。
也就是说,问题在于可以用 4 个字节的形式表示一个 32 位 int,而不是用一种方式,而是至少用两种方式,它们的字节顺序不同。并且您需要指定要选择哪种方法。在这里,您将抽象类型 Int32 编码为字节。接收方和发送方必须就如何解释通过网络传输或存储在文件中的字节流达成一致。
使用字符串,完全相同的问题,只是更糟。首先,字符串本身是一个比数字还要抽象的东西(一定范围内的数字至少可以直接存入内存)。字符串由字符组成,字符不能直接存储在内存中,可以用一些字节序列对其进行编码。再一次,你编码的方式是一个实现细节。
就像 Int32 一样,您不能简单地通过网络发送字符串,您只能发送使用某种编码方法转换为字节的字符串。在这里,编程语言和/或库可以提供一种手段,如其他答案中所示,使生活更轻松而不进行手动转码,但指定编码(显式或隐式)将是必要的。