'C'에 해당되는 글 1건

  1. 2014.02.18 [C] MsgPack을 이용 데이터 주고 받기

Message (Data) Serialization 방법들은 여러가지가 있다. 특히 이기종 간에 객체 전달을 보장 하기 위해서 각각이 여러가지 제약 조건들이 있다..

그중에 최근 많이 쓰이고 있는 것들은

비교 자료 들은 여러 곳에서 찾을 수 있다.

http://www.slideshare.net/IgorAnishchenko/pb-vs-thrift-vs-avro

정리하면, Avro는 동적 전환이 가능 하며 C를 지원 하고, Thrift 와 Protocol Buffer는 명확한 데이터 형을 코드상에 Define 해두어야 한다. 그리고 다양한 형(Type)을 지원하기 위해 지원되는 언어들이 객체지향 언어가 많다. Official 하게는 C를 지원하지 않는다.

Message Pack(msgpack)은 Avro와 비슷한 특성을 지니고 있지만 조금 다른면이 있다. 기본적으로 Schema 등과 같은 데이터 Type Definition이 필요 없고, Serialized 된 데이터 크기도 상당히 작은 장점이 있다.

https://github.com/eishay/jvm-serializers/wiki

또한 Message Pack은 기본적으로 Protocol 을 공개하고 다양한 OpenSource Contributers에 의해 구현된 구현체 들이 존재 한다. 가장 맘에 드는 부분은 C(C++)로 된 구현체가 메인격에 속하고, Serialization 속도도 빠르고 결과 데이터도 다른 것들 보다 상당히 작은 편에 속한다.( 첫번째 링크 참고)

Avro역시 C 버전이 존재하지만, Documentation이 너무 약하고 심지어 API 주석 조차 없어, 개발 하는데 정말 안습한 상황이다. 그나마 Java는 지원이 괜찮아 많은 User가 존재한다. 자바를 보고 C버전을 이해할 수도 있자나? 라는 말은 하지말자. 이미 우리에게 MessagePack이 있다.

부차적인 얘기지만 개인적으로 구현해서 쓰던 허접한 Protocol과도 상당히 유사해서 애정이 간다고나 할까?ㅋ 기존 코드에 해당 영역만 바꾸면 돼서 일꺼리도 크게 줄었다.

C 라이브러리는 아래 Github url에서 가져오자.

https://github.com/msgpack/msgpack-c

C로 MessagePack을 사용할때 주로 맞닥들이는 상황은, 데이터를 Pack(Serialized) 했지만, Unpack 정보가 부족 하다는 것이다.

아래는 Pack하는 예제이다.

    char *buf = NULL;
    msgpack_sbuffer sbuf;
    msgpack_sbuffer_init(&sbuf);
    msgpack_packer pck;
    msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
    /* The array will store `num` contiguous blocks made of a, b, c attributes */
    msgpack_pack_array(&pck, 3 * num);
    for (int i = 0; i < num; ++i) {
        msgpack_pack_uint32(&pck, s[i].a);
        msgpack_pack_uint32(&pck, s[i].b);
        msgpack_pack_float(&pck, s[i].c);
    }

위와같이 Pack을 할때는 Type별로 Pack을 하고 있는데, Uppack 할 함수를 찾아 보면 Type별 Unpack 함수가 없다. msgpack_unpack_next() 함수가 거의 전부인 상황이다. 이것은 Library의 특성인데, 기본적인 데이터 타입인 msgpack_object 내부를 보면 해답을 찾을 수 있다.

typedef struct msgpack_object {
    msgpack_object_type type;
    msgpack_object_union via;
} msgpack_object;

typedef union {
    bool boolean;
    uint64_t u64;
    int64_t  i64;
    double   dec;
    msgpack_object_array array;
    msgpack_object_map map;
    msgpack_object_raw raw;
} msgpack_object_union;

msgpack_object는 type과 via msgpack_object_union을 가지고 있다. 각 데이터 타입별로(전체는 아니지만) 데이터 형을 Union 으로 가지고 있으면서, type을 기준으로 적절히 데이터를 끄집어 내야 한다. 아래 예제중 끄집어 내는 부분을 보자.

    some_struct *s = NULL;
    msgpack_unpacked msg;
    msgpack_unpacked_init(&msg);
    if (msgpack_unpack_next(&msg, ptr, size, NULL)) {
        msgpack_object root = msg.data;
        if (root.type == MSGPACK_OBJECT_ARRAY) {
            assert(root.via.array.size % 3 == 0);
            *num = root.via.array.size / 3;
            s = malloc(root.via.array.size*sizeof(*s));
            for (int i = 0, j = 0; i < root.via.array.size; i += 3, j++) {
                s[j].a = root.via.array.ptr[i].via.u64;
                s[j].b = root.via.array.ptr[i + 1].via.u64;
                s[j].c = root.via.array.ptr[i + 2].via.dec;
            }
        }
    }

위처럼 root .type을 기준으로 해당 데이터를 적절히 처리 하거나(!), 또는 전체 type를 확인해 가면서 데이터를 General 하게 parsing 하도록 해야 한다. 하지만 이건 C에서 불가능 하자나. PHP 라이브러리가 이런 구조로 되어있다.

전체 소스

Soure Reference : http://stackoverflow.com/questions/15393838/how-do-i-unpack-and-extract-data-properly-using-msgpack-c

#include 
#include 
#include 

typedef struct some_struct {
    uint32_t a;
    uint32_t b;
    float c;
} some_struct;

static char *pack(const some_struct *s, int num, int *size);
static some_struct *unpack(const void *ptr, int size, int *num);

/* Fixtures */
some_struct ary[] = {
    { 1234, 5678, 3.14f },
    { 4321, 8765, 4.13f },
    { 2143, 6587, 1.34f }
};

int main(void) {
    /** PACK */
    int size;
    char *buf = pack(ary, sizeof(ary)/sizeof(ary[0]), &size);
    printf("pack %zd struct(s): %d byte(s)\n", sizeof(ary)/sizeof(ary[0]), size);

    /** UNPACK */
    int num;
    some_struct *s = unpack(buf, size, &num);
    printf("unpack: %d struct(s)\n", num);

    /** CHECK */
    assert(num == (int) sizeof(ary)/sizeof(ary[0]));
    for (int i = 0; i < num; i++) {
        assert(s[i].a == ary[i].a);
        assert(s[i].b == ary[i].b);
        assert(s[i].c == ary[i].c);
    }
    printf("check ok. Exiting...\n");

    free(buf);
    free(s);

    return 0;
}

static char *pack(const some_struct *s, int num, int *size) {
    assert(num > 0);
    char *buf = NULL;
    msgpack_sbuffer sbuf;
    msgpack_sbuffer_init(&sbuf);
    msgpack_packer pck;
    msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
    /* The array will store `num` contiguous blocks made of a, b, c attributes */
    msgpack_pack_array(&pck, 3 * num);
    for (int i = 0; i < num; ++i) {
        msgpack_pack_uint32(&pck, s[i].a);
        msgpack_pack_uint32(&pck, s[i].b);
        msgpack_pack_float(&pck, s[i].c);
    }
    *size = sbuf.size;
    buf = malloc(sbuf.size);
    memcpy(buf, sbuf.data, sbuf.size);
    msgpack_sbuffer_destroy(&sbuf);
    return buf;
}

static some_struct *unpack(const void *ptr, int size, int *num) {
    some_struct *s = NULL;
    msgpack_unpacked msg;
    msgpack_unpacked_init(&msg);
    if (msgpack_unpack_next(&msg, ptr, size, NULL)) {
        msgpack_object root = msg.data;
        if (root.type == MSGPACK_OBJECT_ARRAY) {
            assert(root.via.array.size % 3 == 0);
            *num = root.via.array.size / 3;
            s = malloc(root.via.array.size*sizeof(*s));
            for (int i = 0, j = 0; i < root.via.array.size; i += 3, j++) {
                s[j].a = root.via.array.ptr[i].via.u64;
                s[j].b = root.via.array.ptr[i + 1].via.u64;
                s[j].c = root.via.array.ptr[i + 2].via.dec;
            }
        }
    }
    msgpack_unpacked_destroy(&msg);
    return s;
}




Posted by 지누스

댓글을 달아 주세요



티스토리 툴바