본문 바로가기
C++ 200제/코딩 IT 정보

C# TCP에서 사용할 binary(바이너리) 프레임 포맷 예제

by vicddory 2017. 2. 13.

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 프레임 포맷[C# TCP에서 사용할 binary 프레임 포맷 예제]


어느 플랫폼이건 어느 언어 건 메모리 필드의 위치가 보장되지 않으면, 나도 모르는 사이에 메모리 속 필드의 위치가 뒤죽박죽이 되버립니다. 곤란한 경우가 되겠죠.


그래서 LayoutKind의 속성을 이용해 이 위치를 고정할 필요가 있습니다.




이 부분에 대해선 아래 정성태님의 글을 참조해 주세요.



 C# TCP에서 사용할 binary(바이너리) 프레임 포맷 예제

댓글