C#에서 엔디안 변경
C#에서 네트워크 통신을 할게 있어서 바이트오더를 빅엔디안으로 해주려다가 알게 된게 있어서 정리한다.
2026년 수정 안내 원래 이 글은 “
HostToNetworkOrder는 unsigned 타입을 지원하지 않아 쓸 수 없다”고 결론 내렸는데, 정확하지 않았다. 캐스팅으로 그대로 쓸 수 있다. 그리고 대안으로 제시했던Array.Reverse방식도 호스트가 항상 리틀엔디안이라고 가정하는 문제가 있었다. 둘 다 바로잡는다.
HostToNetworkOrder는 unsigned도 다룰 수 있다
System.Net.IPAddress.HostToNetworkOrder와 NetworkToHostOrder는 short, int, long만 오버로드되어 있다. ushort, uint는 직접 넘길 수 없다.
하지만 바이트 순서를 뒤집는 것은 부호와 무관하게 비트 패턴에만 좌우되는 연산이다. 그래서 부호 있는 타입으로 캐스팅해 넘기고, 결과를 다시 부호 없는 타입으로 캐스팅해 받으면 그대로 쓸 수 있다.
ushort value = 0xABCD;
ushort be = (ushort)IPAddress.HostToNetworkOrder( (short)value );
uint valueU = 0xAABBCCDD;
uint beU = (uint)IPAddress.HostToNetworkOrder( (int)valueU );
당시 “강제로 형변환도 해봤지만 데이터가 잘못 들어가기만 했다”고 적었는데, 짐작으로는 short가 아니라 (short)(int)value처럼 부호 확장이 두 번 겹치거나, 캐스팅 순서가 어긋났을 가능성이 크다. 위처럼 원본 크기에 맞는 부호 있는 타입으로 정확히 한 번만 캐스팅하면 문제없이 동작한다.
Array.Reverse 방식의 함정
원래 이 글에서 대안으로 제시했던 코드는 이렇다.
var arr = BitConverter.GetBytes( total_length );
Array.Reverse( arr );
arr.CopyTo( buffer, offset );
이 코드는 호스트가 리틀엔디안이라고 가정하고 무조건 뒤집는다. 대부분의 x86/x64/ARM(리틀엔디안 모드) 환경에서는 우연히 맞아떨어지지만, 빅엔디안 환경에서 돌리면 오히려 순서가 깨진다. 네트워크 바이트 오더를 다루는 코드라면 이 가정을 명시적으로 확인해야 한다.
var arr = BitConverter.GetBytes( total_length );
if ( BitConverter.IsLittleEndian )
Array.Reverse( arr );
arr.CopyTo( buffer, offset );
지금이라면 BinaryPrimitives를 쓴다
.NET Core 2.1(2018)부터는 System.Buffers.Binary.BinaryPrimitives가 추가되어, 엔디안 변환을 훨씬 명확하고 안전하게 처리할 수 있다. 호스트 엔디안을 가정하지 않고, 할당 없이 버퍼에 바로 쓰고 읽는다.
using System.Buffers.Binary;
Span<byte> buffer = stackalloc byte[ 4 ];
BinaryPrimitives.WriteUInt32BigEndian( buffer, valueU );
uint readBack = BinaryPrimitives.ReadUInt32BigEndian( buffer );
ushort, uint, ulong, short, int, long을 빅엔디안/리틀엔디안 양쪽으로 읽고 쓰는 메서드가 모두 준비되어 있어, 새로 작성하는 코드라면 이쪽을 쓰는 것이 좋다.
댓글 남기기