목록으로

Programming Notes

C#에서 구조체 패킷 송수신: Marshaling의 마법

C#으로 네트워크 통신을 구현하다 보면 구조체(struct) 형태의 데이터를 패킷으로 주고받아야 할 경우가 많습니다. 이때 단순히 구조체 변수를 네트워크 스트림에 직접 던져 넣을 수는 없습니다. 다른 시스템, 혹은 다른 언어로 작성된 프로그램과 통신을 위해서는 데이터의 표현 방식을 일관되게 맞춰야 하는데, 이 과정을 바로 **마샬링(Marshalling)**이라고 합니다. 마샬링은 메모리에서의 구조체 표현을 네트워크 전송에 적합한 바이트 배열(Byte Array)로 변환하고, 반대로 수신 시에는 바이트 배열을 다시 구조체로 복원하는 과정을 의미합니다. 마치 마법처럼 다른 시스템의 언어와 데이터 형식의 차이를 매끄럽게 연결해주는 역할을 하는 것이죠.

본격적으로 C#에서 구조체 패킷 송수신을 위한 마샬링 방법을 살펴보겠습니다. 핵심은 System.Runtime.InteropServices 네임스페이스에 포함된 Marshal 클래스를 사용하는 것입니다. 송신 과정에서는 Marshal.SizeOf 메서드를 통해 구조체의 크기를 구하고, Marshal.StructureToPtr 메서드를 이용해 구조체를 바이트 배열로 변환합니다. 이렇게 얻은 바이트 배열을 네트워크 스트림을 통해 전송합니다. 수신측에서는 네트워크 스트림에서 바이트 배열을 수신한 후, Marshal.AllocHGlobal 메서드로 메모리 공간을 할당하고, Marshal.Copy 메서드로 바이트 배열을 메모리에 복사합니다. 마지막으로 Marshal.PtrToStructure 메서드를 이용하여 메모리에 복사된 바이트 배열을 다시 구조체로 변환하여 원하는 데이터를 얻을 수 있습니다. 물론, 메모리 관리를 위해 Marshal.FreeHGlobal 메서드를 통해 할당된 메모리를 해제하는 것을 잊지 말아야 합니다. 이 과정에서 바이트 순서(Endianness) 문제를 고려해야 할 수도 있으며, 필요에 따라 BitConverter 클래스를 활용하여 바이트 순서를 변환해야 할 수 있습니다. 구조체의 구성원에 따라 다양한 마샬링 속성을 추가적으로 지정해야 원활한 데이터 변환이 가능하다는 점도 명심해야 합니다. 예를 들어, 문자열 변수의 경우, 길이를 명시적으로 지정하거나, 특정 문자 인코딩을 사용하는 등의 추가적인 작업이 필요할 수 있습니다.

결론적으로, C#에서 구조체 패킷 송수신은 마샬링을 통해 효율적으로 처리할 수 있습니다. Marshal 클래스의 다양한 메서드를 적절히 활용하고, 데이터 형식과 바이트 순서, 메모리 관리 등을 주의 깊게 고려한다면 안정적이고 효과적인 네트워크 통신을 구현할 수 있습니다. 하지만 마샬링은 다소 복잡하고 오류 발생 가능성이 높은 작업이므로, 더욱 간편하고 안전한 방법으로는 직렬화(Serialization) 라이브러리(예: Newtonsoft.Json, Protobuf-net)를 사용하는 것을 고려해 볼 수 있습니다. 이러한 라이브러리는 마샬링의 복잡성을 추상화하여 개발 편의성을 높여줍니다.