티스토리 뷰

목차

    반응형

    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(바이너리) 프레임 포맷 예제

    반응형