Какова цель параметра, переданного в: IPAddress .HostToNetworkOrder?

#c# #network-programming #endianness

#c# #сетевое программирование #порядковый номер

Вопрос:

Я смотрю на некоторый код, который написал кто-то другой и выглядит так:

 protected override void CloseAndSend(BinaryWriter request, uint lengthPos)
{
request.Seek((int)lengthPos, SeekOrigin.Begin);
request.Write(IPAddress.HostToNetworkOrder((int)(request.BaseStream.Length - lengthPos - sizeof(int))));

//more code here.
}
  

Я понимаю, что IPAddress .HostToNetworkOrder гарантирует, что используется соответствующий порядковый номер, однако какова цель аргументации, т.Е. (int)(request.BaseStream.Length - lengthPos - sizeof(int))) ? Я прочитал это: https://learn.microsoft.com/en-us/dotnet/api/system.net.ipaddress.hosttonetworkorder?view=netcore-3.1 . Я не могу найти никаких примеров и, следовательно, причину вопроса. Я потратил два часа на поиск в Google.

Обновить

Вот код, который заполняет двоичный файл (в правильном порядке):

     byte EOL = 0;
        BinaryWriter writer = new BinaryWriter(new MemoryStream());
        writer.Write(UTF8Encoding.UTF8.GetBytes("API"));
        writer.Write(EOL);
        writer.Write((int)0);
        writer.Write(UTF8Encoding.ASCII.GetBytes("v100..155"));
        //request.BaseStream.Length - lengthPos - sizeof(int)=9
        //writer.BaseStream.Length=17
  

Комментарии:

1. Это число преобразуется в сетевой порядок. Вы хотите записать число в двоичной форме, но не знаете, используется ли в настоящее время большой или маленький порядковый номер, поэтому вы используете этот метод для преобразования в сетевой порядок (большой порядковый номер), если это необходимо.

2. @Evk, спасибо. Разве вам не пришлось бы конвертировать весь поток?

3. Похоже, разница в том, что большой конец стихи Маленький конец. Смотрите : learn.microsoft.com/en-us/dotnet/api /. … Это в примечаниях к ссылке (или вашей ссылке).

4. @jdweng, я разместил эту ссылку в своем вопросе. Знаете ли вы какие-либо сценарии, в которых вы бы не преобразовывали весь поток?

5. Да, если данные не все int32 (4 байта данных).

Ответ №1:

Предположим, вам нужно передать короткое int длинное число по проводу. По проводу вы можете передавать только двоичные данные, и для представления такого числа требуется более 1 байта. Тогда возникает проблема — можно прочитать несколько байтов либо слева направо, либо справа налево, и результаты будут разными. Это означает, что протокол должен указывать это, или, если не указано, должно быть соглашение. Такое соглашение существует для сетевых протоколов, и используется big endian, если не указано иное.

Теперь BinaryWriter всегда используется последовательность текущей архитектуры (вы можете проверить это BitConverter.IsLittleEndian , например, через). Что делать, если ваша текущая архитектура имеет небольшой конец, и вам нужно записать число (например, следующую длину сообщения, которая является обычной в протоколах TCP) по проводам? Допустим, длина равна 1, а количество байтов, зарезервированных для этой длины, равно 2 (таким образом, общая длина равна short ). Если вы просто сделаете

 request.Write(length)
  

в архитектуре с малым конечным порядком два байта 01 — 00 записываются по проводам. Однако 01 — 00 байт в большом конце представляют собой совершенно другое число — 256. Потребитель на другом конце провода, если он принимает большой порядковый номер, как и должно быть, прочитает два байта и интерпретирует их как совершенно разную длину сообщения.

Поэтому, чтобы избежать этого, при необходимости преобразуйте указанное число в большой порядковый номер:

 request.Write(IPAddress.HostToNetworkOrder(length))
  

Если вы используете little endian HostToNetworkOrder , он преобразует short 1 в 256, а затем BinaryWriter , который также использует little endian в этом случае, запишет байты 00 — 01 (представление 256 в little endian). Если вы используете big endian, то HostToNetworkOrder вернет 1 обратно и BinaryWriter снова запишет те же 00 — 01 байт по проводу, обеспечивая согласованное представление.

Чтобы следить за вашим обновлением:

Рассматриваемый код записывает строку «API» в UTF-8 (3 байта), затем некоторый байт EOL (теперь у нас есть 4 байта), а затем резервирует 4 байта, записывая нулевое целое число. Он продолжает записывать другой материал, а затем возвращается обратно в позицию этого зарезервированного 4-байтового целого числа (содержащего все нулевые байты). Затем он вычисляет длину материала, следующего за этим 4-байтовым целым числом («v100..155» и так далее), и записывает эту длину в зарезервированную позицию. Таким довольно странным образом он просто записывает длину следующих данных, что является обычным делом в большинстве протоколов TCP.

Если это весь код, то простой способ сделать то же самое — просто:

     byte EOL = 0;
    BinaryWriter writer = new BinaryWriter(new MemoryStream());
    writer.Write(UTF8Encoding.UTF8.GetBytes("API"));
    writer.Write(EOL);
    var data = UTF8Encoding.ASCII.GetBytes("v100..155");
    writer.Write(IPAddress.HostToNetworkOrder(data.Length));
    writer.Write(data);
  

Комментарии:

1. Спасибо. Как я уже сказал в вопросе, я понимаю концепцию бесконечности. Чего я не понимаю, так это почему только часть потока кажется преобразованной (см. Аргумент, переданный в: IPAddress . HostToNetworkOrder . Знаете ли вы какие-либо сценарии, в которых вы не конвертировали бы весь поток?

2. @w0051977 endianess применяется не ко всему потоку, а только к фрагментам данных, закодированных в нем. Предположим, вы передаете 3 двухбайтовых числа по проводу. Тогда при одном конце это будет a-b-c-d-e-f , а при другом: b-a-d-c-f-e этого никогда не будет f-e-d-c-b-a . В этом случае преобразуется число ( число является результатом (int)(request.BaseStream.Length - lengthPos - sizeof(int)) ), и это число затем записывается в BinaryWriter . Если результатом этого выражения является, скажем 10 , число 10, то оно записывается в BinaryWriter, обеспечивая надлежащую последовательность.

3. можете ли вы привести пример того, где вы бы преобразовали только часть потока в своем ответе?

4. @w0051977 код в вашем вопросе ничего не преобразует. Он ищет указанную позицию в потоке и записывает туда определенный номер, вот и все. Поэтому я не понимаю, о чем вы спрашиваете.

5. @w0051977 Да, это, вероятно, строка, завершающаяся нулем, по тем же причинам, что и выше: для строк неизвестной длины вы можете заполнить их нулевым байтом, чтобы указать конец строки. Для произвольных двоичных данных (не строк) вы не можете этого сделать, потому что нулевой байт абсолютно допустим внутри самих данных