69

In the documentation of hardware that allows us to control it via UDP/IP, I found the following fragment:

In this communication protocol, DWORD is a 4 bytes data, WORD is a 2 bytes data, BYTE is a single byte data. The storage format is little endian, namely 4 bytes (32bits) data is stored as: d7-d0, d15-d8, d23-d16, d31-d24; double bytes (16bits) data is stored as: d7-d0 , d15-d8.

I am wondering how this translates to C#? Do I have to convert stuff before sending it over? For example, if I want to send over a 32 bit integer, or a 4 character string?

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
TimothyP
  • 21,178
  • 26
  • 94
  • 142

6 Answers6

94

C# itself doesn't define the endianness. Whenever you convert to bytes, however, you're making a choice. The BitConverter class has an IsLittleEndian field to tell you how it will behave, but it doesn't give the choice. The same goes for BinaryReader/BinaryWriter.

My MiscUtil library has an EndianBitConverter class which allows you to define the endianness; there are similar equivalents for BinaryReader/Writer. No online usage guide I'm afraid, but they're trivial :)

(EndianBitConverter also has a piece of functionality which isn't present in the normal BitConverter, which is to do conversions in-place in a byte array.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 4
    Also remember in C# it is possible to copy values directly e.g. *ptr = value; in which case you should be concerned about the computer architectures byte order. – markmnl Nov 18 '13 at 02:52
  • 1
    Through ReSharper's decompiling I can see that the `IsLittleEndian` property in `BitConverter` on my installed .Net framework on my Windows PC is simply _hardcoded_ to return `True`, though. Are there _actual_ cases of big-endian implementations of .Net? – Nyerguds Apr 20 '18 at 08:30
  • 1
    @Nyerguds: It's certainly *feasible* in .NET Core - see https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/BitConverter.cs (I don't know that I've ever actually run anything on such an implementation, but I'd definitely avoid assuming it'll always be true.) – Jon Skeet Apr 20 '18 at 09:12
  • 1
    I think your MiscUtil library should be redirected to System.Memory: System.Buffer.Binary.BinaryPrimitives. I believe these methods will be special-cased by the JIT compiler to use hardware instructions that will be much faster than would otherwise be possible with C# code. – MarkPflug Jun 26 '18 at 16:39
48

You can also use

IPAddress.NetworkToHostOrder(...)

For short, int or long.

Jan Bannister
  • 4,859
  • 8
  • 38
  • 45
  • 1
    -1 because this works only for conversions to and from *Big Endian*, and the OP specifically asked for conversion to *Little Endian*. In particular, the function will only do something on Little Endian hosts (where the OP doesn't need it) and it will do *nothing* on Big Endian hosts (where the OP needs the functionality). – DarthGizka Jul 12 '21 at 11:16
12

Re little-endian, the short answer (to do I need to do anything) is "probably not, but it depends on your hardware". You can check with:

bool le = BitConverter.IsLittleEndian;

Depending on what this says, you might want to reverse portions of your buffers. Alternatively, Jon Skeet has specific-endian converters here (look for EndianBitConverter).

Note that itaniums (for example) are big-endian. Most Intels are little-endian.

Re the specific UDP/IP...?

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • ... Is anyone using .net on itanium? – SingleNegationElimination Mar 26 '09 at 23:09
  • hmm. What basic research I just did seems to suggest that Windows is always little-endian (Itaniums can support either endiannesses). So BitConverter.IsLittleEndian seems like it's *always* going to return true, unless you're running something wierld like Mono on a big-endian linux on itanium. See http://blogs.msdn.com/larryosterman/archive/2005/06/07/426334.aspx – piers7 Jan 05 '10 at 07:55
  • One of the comments suggests xna on xbox may be big-endian; I haven't checked: http://blogs.msdn.com/larryosterman/archive/2005/06/07/426334.aspx#426591 – Marc Gravell Jan 05 '10 at 08:23
3

You need to know about network byte order as well as CPU endian-ness.

Typically for TCP/UDP comms, you always convert data to network byte order using the htons function (and ntohs, and their related functions).

Normally network order is big-endian, but in this case (for some reason!) the comms is little endian, so those functions are not very useful. This is important as you cannot assume the UDP comms they have implemented follow any other standards, it also makes life difficult if you have a big-endian architecture as you just can't wrap everything with htons as you should :-(

However, if you're coming from an intel x86 architecture, then you're already little-endian, so just send the data without conversion.

Martin Liversage
  • 104,481
  • 22
  • 209
  • 256
gbjbaanb
  • 51,617
  • 12
  • 104
  • 148
1

I'm playing around with packed data in UDP Multicast and I needed something to reorder UInt16 octets since I noticed an error in packet header (Wireshark), so I made this:

    private UInt16 swapOctetsUInt16(UInt16 toSwap)
    {
        Int32 tmp = 0;
        tmp = toSwap >> 8;
        tmp = tmp | ((toSwap & 0xff) << 8);
        return (UInt16) tmp;
    }

In case of UInt32,

    private UInt32 swapOctetsUInt32(UInt32 toSwap)
    {
        UInt32 tmp = 0;
        tmp = toSwap >> 24;
        tmp = tmp | ((toSwap & 0xff0000) >> 8);
        tmp = tmp | ((toSwap & 0xff00) << 8);
        tmp = tmp | ((toSwap & 0xff) << 24);
        return tmp;
    }

This is just for testing

    private void testSwap() {
        UInt16 tmp1 = 0x0a0b;
        UInt32 tmp2 = 0x0a0b0c0d;
        SoapHexBinary shb1 = new SoapHexBinary(BitConverter.GetBytes(tmp1));
        SoapHexBinary shb2 = new SoapHexBinary(BitConverter.GetBytes(swapOctetsUInt16(tmp1)));
        Debug.WriteLine("{0}", shb1.ToString());
        Debug.WriteLine("{0}", shb2.ToString());
        SoapHexBinary shb3 = new SoapHexBinary(BitConverter.GetBytes(tmp2));
        SoapHexBinary shb4 = new SoapHexBinary(BitConverter.GetBytes(swapOctetsUInt32(tmp2)));
        Debug.WriteLine("{0}", shb3.ToString());
        Debug.WriteLine("{0}", shb4.ToString());
    }

from which output was this:

    0B0A: {0}
    0A0B: {0}
    0D0C0B0A: {0}
    0A0B0C0D: {0}
0

If you're parsing and performance is not critical, consider this very simple code:

private static byte[] NetworkToHostOrder (byte[] array, int offset, int length)
{
    return array.Skip (offset).Take (length).Reverse ().ToArray ();
}

int foo = BitConverter.ToInt64 (NetworkToHostOrder (queue, 14, 8), 0);
mafu
  • 31,798
  • 42
  • 154
  • 247