티스토리 뷰
목차
반응형
C# TCP에서 사용할 binary(바이너리) 프레임 포맷 예제
예를 들어, 아래와 같은 데이터를 다루게 된다면...
Field |
Offset |
Type |
size(object) |
id |
0 |
unsigned int |
1 |
name |
1 |
Byte Array |
40 |
grade |
41 |
sign float |
8 |
아래와 같은 포맷을 만들어 볼 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] struct DataPacket { [MarshalAs(UnmanagedType.U4)] public uint Id; // As I understood from your question, the Name field // has a prefixed size of 40 bytes. Attention here: // the SizeConst actually means '40 characters', not bytes // If you choose to use the Unicode charset, set SizeConst = 20 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)] public String Name; // This will be serialized in little endian format // in your question this field is 8 bytes long, which // in c# corresponds to the double type. If you really mean // the float type (4 bytes), change here. public double Grade; // Calling this method will return a byte array with the contents // of the struct ready to be sent via the tcp socket. public byte[] Serialize() { // allocate a byte array for the struct data var buffer = new byte[Marshal.SizeOf(typeof(DataPacket))]; // Allocate a GCHandle and get the array pointer var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned); var pBuffer = gch.AddrOfPinnedObject(); // copy data from struct to array and unpin the gc pointer Marshal.StructureToPtr(this, pBuffer, false); gch.Free(); return buffer; } // this method will deserialize a byte array into the struct. public void Deserialize(ref byte[] data) { var gch = GCHandle.Alloc(data, GCHandleType.Pinned); this = (DataPacket)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(DataPacket)); gch.Free(); } } | cs |
그리고, 실제로는 아래처럼 사용해 볼 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | DataPacket packet; packet.Id = 1234; packet.Name = "Marvin the paranoid robot"; packet.Grade = 9.2; // serialize var bytes = packet.Serialize(); // send via tcp var tcp = new TcpClient(...); tcp.GetStream().Write(bytes, 0, bytes.Length); // deserializing; DataPacket receivedPacket; receivedPacket.deserialize(bytes); | cs |
여기서 C#의 LayoutKind에 대해서 먼저 이해할 필요가 있습니다.
[C# TCP에서 사용할 binary 프레임 포맷 예제]
어느 플랫폼이건 어느 언어 건 메모리 필드의 위치가 보장되지 않으면, 나도 모르는 사이에 메모리 속 필드의 위치가 뒤죽박죽이 되버립니다. 곤란한 경우가 되겠죠.
그래서 LayoutKind의 속성을 이용해 이 위치를 고정할 필요가 있습니다.
이 부분에 대해선 아래 정성태님의 글을 참조해 주세요.
C# TCP에서 사용할 binary(바이너리) 프레임 포맷 예제
반응형